From b4a1c418bb3f923858dd55729f585e189327a038 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Fri, 13 Mar 2026 10:15:49 -0700 Subject: [PATCH] Run oxfmt across repo, add format script and docs Add .oxfmtignore to skip generated bindings and wasm-pack output. Add npm format script, update DEVELOPMENT.md for Vite+ toolchain, and format all non-generated files with oxfmt. Co-Authored-By: Claude Opus 4.6 --- .claude-context.md | 11 + .../release/generate-release-notes.md | 2 +- .github/ISSUE_TEMPLATE/bug_report.md | 24 +- .github/pull_request_template.md | 1 + .github/workflows/ci.yml | 2 +- .github/workflows/claude.yml | 1 - .github/workflows/release-app.yml | 2 +- .github/workflows/sponsors.yml | 10 +- .oxfmtignore | 2 + .vscode/extensions.json | 6 +- Cargo.toml | 48 +- DEVELOPMENT.md | 28 +- README.md | 14 +- crates-cli/yaak-cli/Cargo.toml | 9 +- crates-cli/yaak-cli/README.md | 6 +- crates-tauri/yaak-app/Cargo.toml | 11 +- .../yaak-app/capabilities/default.json | 4 +- crates-tauri/yaak-app/package.json | 2 +- crates-tauri/yaak-app/tauri.conf.json | 9 +- crates-tauri/yaak-app/tauri.release.conf.json | 4 +- crates-tauri/yaak-fonts/index.ts | 10 +- crates-tauri/yaak-fonts/package.json | 2 +- crates-tauri/yaak-license/index.ts | 28 +- crates-tauri/yaak-license/package.json | 2 +- crates-tauri/yaak-mac-window/index.ts | 6 +- crates-tauri/yaak-mac-window/package.json | 2 +- .../yaak-mac-window/permissions/default.toml | 5 +- crates/yaak-crypto/index.ts | 10 +- crates/yaak-crypto/package.json | 2 +- crates/yaak-git/index.ts | 213 +++---- crates/yaak-git/package.json | 2 +- crates/yaak-http/Cargo.toml | 7 +- crates/yaak-models/guest-js/atoms.ts | 66 ++- crates/yaak-models/guest-js/index.ts | 12 +- crates/yaak-models/guest-js/store.ts | 84 ++- crates/yaak-models/guest-js/types.ts | 6 +- crates/yaak-models/guest-js/util.ts | 2 +- crates/yaak-models/package.json | 2 +- crates/yaak-plugins/index.ts | 22 +- crates/yaak-plugins/package.json | 2 +- crates/yaak-sse/index.ts | 2 +- crates/yaak-sse/package.json | 2 +- crates/yaak-sync/index.ts | 30 +- crates/yaak-sync/package.json | 2 +- crates/yaak-templates/build-wasm.cjs | 22 +- crates/yaak-templates/index.ts | 6 +- crates/yaak-templates/package.json | 2 +- crates/yaak-ws/Cargo.toml | 5 +- crates/yaak-ws/index.ts | 12 +- crates/yaak-ws/package.json | 2 +- npm/cli-darwin-arm64/package.json | 8 +- npm/cli-darwin-x64/package.json | 8 +- npm/cli-linux-arm64/package.json | 8 +- npm/cli-linux-x64/package.json | 8 +- npm/cli-win32-arm64/package.json | 8 +- npm/cli-win32-x64/package.json | 8 +- npm/cli/common.js | 4 +- npm/cli/install.js | 6 +- npm/cli/package.json | 16 +- npm/prepare-publish.js | 18 +- package.json | 23 +- packages/common-lib/formatSize.ts | 8 +- packages/common-lib/index.ts | 6 +- packages/common-lib/package.json | 2 +- packages/common-lib/templateFunction.ts | 14 +- packages/plugin-runtime-types/package.json | 14 +- packages/plugin-runtime-types/src/index.ts | 10 +- .../src/plugins/AuthenticationPlugin.ts | 10 +- .../src/plugins/Context.ts | 52 +- .../src/plugins/FilterPlugin.ts | 4 +- .../src/plugins/FolderActionPlugin.ts | 4 +- .../src/plugins/GrpcRequestActionPlugin.ts | 4 +- .../src/plugins/HttpRequestActionPlugin.ts | 4 +- .../src/plugins/ImporterPlugin.ts | 22 +- .../src/plugins/TemplateFunctionPlugin.ts | 12 +- .../src/plugins/ThemePlugin.ts | 2 +- .../plugins/WebsocketRequestActionPlugin.ts | 4 +- .../src/plugins/WorkspaceActionPlugin.ts | 4 +- .../plugin-runtime-types/src/plugins/index.ts | 32 +- packages/plugin-runtime/src/EventChannel.ts | 2 +- packages/plugin-runtime/src/PluginHandle.ts | 8 +- packages/plugin-runtime/src/PluginInstance.ts | 257 ++++----- packages/plugin-runtime/src/common.ts | 10 +- packages/plugin-runtime/src/index.ts | 38 +- .../plugin-runtime/src/interceptStdout.ts | 4 +- packages/plugin-runtime/src/migrations.ts | 6 +- packages/plugin-runtime/tests/common.test.ts | 120 ++-- plugins-external/faker/README.md | 2 +- plugins-external/faker/package.json | 4 +- plugins-external/faker/src/index.ts | 74 +-- plugins-external/faker/tests/init.test.ts | 16 +- plugins-external/httpsnippet/README.md | 40 +- plugins-external/httpsnippet/package.json | 8 +- plugins-external/httpsnippet/src/index.ts | 180 +++--- plugins-external/mcp-server/package.json | 8 +- plugins-external/mcp-server/src/index.ts | 16 +- plugins-external/mcp-server/src/server.ts | 40 +- .../mcp-server/src/tools/folder.ts | 78 +-- .../mcp-server/src/tools/helpers.ts | 6 +- .../mcp-server/src/tools/httpRequest.ts | 94 ++-- .../mcp-server/src/tools/schemas.ts | 10 +- .../mcp-server/src/tools/toast.ts | 56 +- .../mcp-server/src/tools/window.ts | 26 +- .../mcp-server/src/tools/workspace.ts | 12 +- plugins-external/mcp-server/src/types.ts | 2 +- plugins/action-copy-curl/package.json | 4 +- plugins/action-copy-curl/src/index.ts | 104 ++-- plugins/action-copy-curl/tests/index.test.ts | 358 ++++++------ plugins/action-copy-grpcurl/README.md | 1 - plugins/action-copy-grpcurl/package.json | 4 +- plugins/action-copy-grpcurl/src/index.ts | 78 +-- .../action-copy-grpcurl/tests/index.test.ts | 128 ++--- plugins/action-send-folder/package.json | 4 +- plugins/action-send-folder/src/index.ts | 35 +- plugins/auth-apikey/package.json | 4 +- plugins/auth-apikey/src/index.ts | 48 +- plugins/auth-aws/README.md | 2 +- plugins/auth-aws/package.json | 4 +- plugins/auth-aws/src/index.ts | 72 +-- plugins/auth-basic/README.md | 2 +- plugins/auth-basic/package.json | 4 +- plugins/auth-basic/src/index.ts | 28 +- plugins/auth-basic/tests/index.test.ts | 76 +-- plugins/auth-bearer/package.json | 4 +- plugins/auth-bearer/src/index.ts | 34 +- plugins/auth-bearer/tests/index.test.ts | 66 +-- plugins/auth-jwt/package.json | 4 +- plugins/auth-jwt/src/index.ts | 140 ++--- plugins/auth-ntlm/package.json | 4 +- plugins/auth-ntlm/src/index.ts | 50 +- plugins/auth-ntlm/src/modules.d.ts | 2 +- plugins/auth-ntlm/tests/index.test.ts | 54 +- plugins/auth-oauth1/package.json | 4 +- plugins/auth-oauth1/src/index.ts | 128 ++--- plugins/auth-oauth2/README.md | 2 +- plugins/auth-oauth2/package.json | 4 +- plugins/auth-oauth2/src/callbackServer.ts | 80 +-- plugins/auth-oauth2/src/fetchAccessToken.ts | 50 +- .../src/getOrRefreshAccessToken.ts | 42 +- .../src/grants/authorizationCode.ts | 78 +-- .../src/grants/clientCredentials.ts | 66 +-- plugins/auth-oauth2/src/grants/implicit.ts | 66 +-- plugins/auth-oauth2/src/grants/password.ts | 16 +- plugins/auth-oauth2/src/index.ts | 532 +++++++++--------- plugins/auth-oauth2/src/store.ts | 22 +- plugins/auth-oauth2/src/util.ts | 34 +- plugins/auth-oauth2/tests/util.test.ts | 118 ++-- plugins/filter-jsonpath/package.json | 4 +- plugins/filter-jsonpath/src/index.ts | 13 +- plugins/filter-xpath/package.json | 4 +- plugins/filter-xpath/src/index.ts | 19 +- plugins/importer-curl/package.json | 4 +- plugins/importer-curl/src/index.ts | 222 ++++---- plugins/importer-curl/tests/index.test.ts | 438 +++++++------- plugins/importer-insomnia/package.json | 4 +- plugins/importer-insomnia/src/common.ts | 14 +- plugins/importer-insomnia/src/index.ts | 14 +- plugins/importer-insomnia/src/v4.ts | 116 ++-- plugins/importer-insomnia/src/v5.ts | 138 ++--- plugins/importer-insomnia/tests/index.test.ts | 22 +- plugins/importer-openapi/package.json | 4 +- plugins/importer-openapi/src/index.ts | 14 +- .../tests/fixtures/petstore.yaml | 224 ++++---- plugins/importer-openapi/tests/index.test.ts | 36 +- .../importer-postman-environment/package.json | 4 +- .../importer-postman-environment/src/index.ts | 40 +- .../tests/index.test.ts | 18 +- plugins/importer-postman/package.json | 4 +- plugins/importer-postman/src/index.ts | 234 ++++---- plugins/importer-postman/tests/index.test.ts | 38 +- plugins/importer-yaak/package.json | 4 +- plugins/importer-yaak/src/index.ts | 26 +- plugins/importer-yaak/tests/index.test.ts | 94 ++-- .../template-function-1password/package.json | 4 +- .../template-function-1password/src/index.ts | 116 ++-- plugins/template-function-cookie/package.json | 4 +- plugins/template-function-cookie/src/index.ts | 14 +- plugins/template-function-ctx/package.json | 4 +- plugins/template-function-ctx/src/index.ts | 14 +- plugins/template-function-encode/package.json | 4 +- plugins/template-function-encode/src/index.ts | 54 +- plugins/template-function-fs/package.json | 4 +- plugins/template-function-fs/src/index.ts | 46 +- plugins/template-function-hash/package.json | 4 +- plugins/template-function-hash/src/index.ts | 54 +- plugins/template-function-json/package.json | 4 +- plugins/template-function-json/src/index.ts | 102 ++-- plugins/template-function-prompt/package.json | 4 +- plugins/template-function-prompt/src/index.ts | 112 ++-- plugins/template-function-random/package.json | 4 +- plugins/template-function-random/src/index.ts | 36 +- plugins/template-function-regex/package.json | 4 +- plugins/template-function-regex/src/index.ts | 68 +-- .../tests/regex.test.ts | 188 +++---- .../template-function-request/package.json | 4 +- .../template-function-request/src/index.ts | 202 +++---- .../template-function-response/package.json | 4 +- .../template-function-response/src/index.ts | 196 +++---- .../template-function-timestamp/package.json | 4 +- .../template-function-timestamp/src/index.ts | 92 +-- .../tests/formatDatetime.test.ts | 78 +-- plugins/template-function-uuid/package.json | 4 +- plugins/template-function-uuid/src/index.ts | 54 +- plugins/template-function-xml/package.json | 4 +- plugins/template-function-xml/src/index.ts | 62 +- plugins/themes-yaak/package.json | 4 +- plugins/themes-yaak/src/index.ts | 72 +-- plugins/themes-yaak/src/themes/andromeda.ts | 58 +- .../themes-yaak/src/themes/atom-one-dark.ts | 58 +- plugins/themes-yaak/src/themes/ayu.ts | 148 ++--- plugins/themes-yaak/src/themes/bluloco.ts | 94 ++-- plugins/themes-yaak/src/themes/catppuccin.ts | 198 +++---- plugins/themes-yaak/src/themes/cobalt2.ts | 58 +- plugins/themes-yaak/src/themes/dracula.ts | 34 +- plugins/themes-yaak/src/themes/everforest.ts | 94 ++-- plugins/themes-yaak/src/themes/fleet.ts | 234 ++++---- .../themes-yaak/src/themes/github-dimmed.ts | 56 +- plugins/themes-yaak/src/themes/github.ts | 78 +-- plugins/themes-yaak/src/themes/gruvbox.ts | 30 +- .../themes-yaak/src/themes/high-contrast.ts | 68 +-- plugins/themes-yaak/src/themes/horizon.ts | 58 +- .../themes-yaak/src/themes/hotdog-stand.ts | 84 +-- .../themes-yaak/src/themes/material-darker.ts | 50 +- .../themes-yaak/src/themes/material-ocean.ts | 54 +- .../src/themes/material-palenight.ts | 54 +- plugins/themes-yaak/src/themes/monokai-pro.ts | 290 +++++----- plugins/themes-yaak/src/themes/moonlight.ts | 32 +- plugins/themes-yaak/src/themes/night-owl.ts | 96 ++-- plugins/themes-yaak/src/themes/noctis.ts | 58 +- plugins/themes-yaak/src/themes/nord.ts | 98 ++-- .../themes-yaak/src/themes/one-dark-pro.ts | 56 +- plugins/themes-yaak/src/themes/panda.ts | 58 +- plugins/themes-yaak/src/themes/relaxing.ts | 24 +- plugins/themes-yaak/src/themes/rose-pine.ts | 126 ++--- .../src/themes/shades-of-purple.ts | 118 ++-- plugins/themes-yaak/src/themes/slack.ts | 58 +- plugins/themes-yaak/src/themes/solarized.ts | 94 ++-- .../themes-yaak/src/themes/synthwave-84.ts | 68 +-- plugins/themes-yaak/src/themes/tokyo-night.ts | 146 ++--- plugins/themes-yaak/src/themes/triangle.ts | 52 +- plugins/themes-yaak/src/themes/vitesse.ts | 94 ++-- .../src/themes/winter-is-coming.ts | 58 +- scripts/create-migration.cjs | 26 +- scripts/git-hooks/post-checkout.mjs | 69 ++- scripts/install-wasm-pack.cjs | 16 +- scripts/publish-core-plugins.cjs | 22 +- scripts/replace-version.cjs | 14 +- scripts/run-dev.mjs | 41 +- scripts/run-workspaces-dev.mjs | 38 +- scripts/vendor-node.cjs | 76 +-- scripts/vendor-plugins.cjs | 20 +- scripts/vendor-protoc.cjs | 90 +-- src-web/commands/commands.tsx | 82 +-- src-web/commands/createEnvironment.tsx | 28 +- .../commands/deleteWebsocketConnections.ts | 8 +- src-web/commands/moveToWorkspace.tsx | 24 +- src-web/commands/openFolderSettings.tsx | 16 +- src-web/commands/openSettings.tsx | 22 +- src-web/commands/openWorkspaceFromSyncDir.tsx | 14 +- src-web/commands/openWorkspaceSettings.tsx | 16 +- src-web/commands/switchWorkspace.tsx | 20 +- src-web/components/BinaryFileEditor.tsx | 28 +- src-web/components/CargoFeature.tsx | 8 +- .../components/CloneGitRepositoryDialog.tsx | 46 +- src-web/components/ColorIndicator.tsx | 8 +- src-web/components/CommandPaletteDialog.tsx | 210 +++---- .../components/ConfirmLargeRequestBody.tsx | 40 +- src-web/components/ConfirmLargeResponse.tsx | 30 +- .../ConfirmLargeResponseRequest.tsx | 30 +- src-web/components/CookieDialog.tsx | 14 +- src-web/components/CookieDropdown.tsx | 66 +-- src-web/components/CopyButton.tsx | 22 +- src-web/components/CopyIconButton.tsx | 22 +- src-web/components/CreateDropdown.tsx | 10 +- .../components/CreateEnvironmentDialog.tsx | 24 +- src-web/components/CreateWorkspaceDialog.tsx | 44 +- src-web/components/Dialogs.tsx | 14 +- src-web/components/DnsOverridesEditor.tsx | 40 +- src-web/components/DropMarker.tsx | 22 +- src-web/components/DynamicForm.tsx | 138 ++--- src-web/components/EmptyStateText.tsx | 8 +- src-web/components/EncryptionHelp.tsx | 2 +- .../components/EnvironmentActionsDropdown.tsx | 38 +- .../components/EnvironmentColorIndicator.tsx | 6 +- src-web/components/EnvironmentColorPicker.tsx | 10 +- src-web/components/EnvironmentEditDialog.tsx | 106 ++-- src-web/components/EnvironmentEditor.tsx | 68 +-- .../components/EnvironmentSharableTooltip.tsx | 2 +- src-web/components/ErrorBoundary.tsx | 20 +- src-web/components/ExportDataDialog.tsx | 42 +- src-web/components/FolderLayout.tsx | 86 +-- src-web/components/FolderSettingsDialog.tsx | 91 ++- src-web/components/FormMultipartEditor.tsx | 12 +- src-web/components/FormUrlencodedEditor.tsx | 16 +- src-web/components/GlobalHooks.tsx | 24 +- src-web/components/GrpcConnectionLayout.tsx | 66 +-- src-web/components/GrpcEditor.tsx | 82 +-- .../components/GrpcProtoSelectionDialog.tsx | 62 +- src-web/components/GrpcRequestPane.tsx | 128 ++--- src-web/components/GrpcResponsePane.tsx | 88 +-- src-web/components/HeaderSize.tsx | 36 +- src-web/components/HeadersEditor.tsx | 84 +-- .../components/HttpAuthenticationEditor.tsx | 78 +-- src-web/components/HttpRequestLayout.tsx | 32 +- src-web/components/HttpRequestPane.tsx | 198 +++---- src-web/components/HttpResponsePane.tsx | 178 +++--- src-web/components/HttpResponseTimeline.tsx | 266 ++++----- src-web/components/ImportCurlButton.tsx | 26 +- src-web/components/ImportDataDialog.tsx | 14 +- src-web/components/IsDev.tsx | 4 +- src-web/components/JsonBodyEditor.tsx | 50 +- .../components/KeyboardShortcutsDialog.tsx | 4 +- src-web/components/LicenseBadge.tsx | 78 +-- src-web/components/LocalImage.tsx | 14 +- src-web/components/Markdown.tsx | 78 +-- src-web/components/MarkdownEditor.tsx | 32 +- src-web/components/MoveToWorkspaceDialog.tsx | 38 +- src-web/components/Overlay.tsx | 34 +- src-web/components/Portal.tsx | 6 +- src-web/components/Prose.tsx | 8 +- .../RecentGrpcConnectionsDropdown.tsx | 30 +- .../RecentHttpResponsesDropdown.tsx | 48 +- src-web/components/RecentRequestsDropdown.tsx | 46 +- .../RecentWebsocketConnectionsDropdown.tsx | 32 +- .../components/RedirectToLatestWorkspace.tsx | 50 +- src-web/components/RequestBodyViewer.tsx | 34 +- src-web/components/RequestMethodDropdown.tsx | 56 +- src-web/components/ResizeHandle.tsx | 42 +- src-web/components/ResponseCookies.tsx | 54 +- src-web/components/ResponseHeaders.tsx | 14 +- src-web/components/ResponseInfo.tsx | 8 +- src-web/components/RouteError.tsx | 16 +- src-web/components/SelectFile.tsx | 64 +-- src-web/components/Settings/Settings.tsx | 78 +-- .../Settings/SettingsCertificates.tsx | 66 +-- .../components/Settings/SettingsGeneral.tsx | 46 +- .../components/Settings/SettingsHotkeys.tsx | 90 +-- .../components/Settings/SettingsInterface.tsx | 88 +-- .../components/Settings/SettingsLicense.tsx | 64 +-- .../components/Settings/SettingsPlugins.tsx | 92 +-- src-web/components/Settings/SettingsProxy.tsx | 124 ++-- src-web/components/Settings/SettingsTheme.tsx | 112 ++-- src-web/components/SettingsDropdown.tsx | 78 +-- src-web/components/Sidebar.tsx | 303 +++++----- src-web/components/SidebarActions.tsx | 16 +- src-web/components/SwitchWorkspaceDialog.tsx | 20 +- .../components/SyncToFilesystemSetting.tsx | 18 +- src-web/components/TemplateFunctionDialog.tsx | 110 ++-- src-web/components/Toasts.tsx | 18 +- src-web/components/UrlBar.tsx | 38 +- src-web/components/UrlParameterEditor.tsx | 20 +- src-web/components/WebsocketRequestLayout.tsx | 26 +- src-web/components/WebsocketRequestPane.tsx | 126 +++-- src-web/components/WebsocketResponsePane.tsx | 92 +-- src-web/components/WindowControls.tsx | 22 +- src-web/components/Workspace.tsx | 118 ++-- .../components/WorkspaceActionsDropdown.tsx | 94 ++-- .../components/WorkspaceEncryptionSetting.tsx | 74 +-- src-web/components/WorkspaceHeader.tsx | 56 +- .../components/WorkspaceSettingsDialog.tsx | 60 +- src-web/components/core/Alert.tsx | 6 +- src-web/components/core/AutoScroller.tsx | 16 +- src-web/components/core/Banner.tsx | 16 +- src-web/components/core/BulkPairEditor.tsx | 22 +- src-web/components/core/Button.tsx | 100 ++-- .../components/core/ButtonInfiniteLoading.tsx | 6 +- src-web/components/core/Checkbox.tsx | 32 +- src-web/components/core/ColorPicker.tsx | 70 +-- src-web/components/core/Confirm.tsx | 20 +- src-web/components/core/CountBadge.tsx | 22 +- src-web/components/core/DetailsBanner.tsx | 26 +- src-web/components/core/Dialog.tsx | 62 +- src-web/components/core/DismissibleBanner.tsx | 20 +- src-web/components/core/Dropdown.tsx | 172 +++--- .../core/Editor/BetterMatchDecorator.ts | 2 +- src-web/components/core/Editor/DiffViewer.tsx | 20 +- src-web/components/core/Editor/Editor.css | 2 +- src-web/components/core/Editor/Editor.tsx | 174 +++--- src-web/components/core/Editor/LazyEditor.tsx | 6 +- src-web/components/core/Editor/extensions.ts | 166 +++--- .../core/Editor/filter/extension.ts | 32 +- .../components/core/Editor/filter/filter.ts | 14 +- .../core/Editor/filter/highlight.ts | 6 +- .../components/core/Editor/filter/query.ts | 176 +++--- .../core/Editor/genericCompletion.ts | 6 +- .../core/Editor/hyperlink/extension.ts | 42 +- src-web/components/core/Editor/json-lint.ts | 14 +- .../components/core/Editor/pairs/extension.ts | 6 +- .../components/core/Editor/pairs/highlight.ts | 2 +- src-web/components/core/Editor/pairs/pairs.ts | 26 +- .../core/Editor/searchMatchCount.ts | 18 +- src-web/components/core/Editor/singleLine.ts | 10 +- .../components/core/Editor/text/extension.ts | 6 +- src-web/components/core/Editor/text/text.ts | 10 +- .../core/Editor/timeline/extension.ts | 6 +- .../core/Editor/timeline/highlight.ts | 2 +- .../core/Editor/timeline/timeline.terms.ts | 5 +- .../core/Editor/timeline/timeline.ts | 19 +- .../components/core/Editor/twig/completion.ts | 40 +- .../components/core/Editor/twig/extension.ts | 30 +- .../components/core/Editor/twig/highlight.ts | 2 +- .../core/Editor/twig/pathParameters.ts | 22 +- .../core/Editor/twig/templateTags.ts | 96 ++-- .../components/core/Editor/twig/twig.test.ts | 88 +-- src-web/components/core/Editor/twig/twig.ts | 12 +- src-web/components/core/Editor/twig/util.ts | 14 +- .../components/core/Editor/url/completion.ts | 6 +- .../components/core/Editor/url/extension.ts | 6 +- .../components/core/Editor/url/highlight.ts | 2 +- src-web/components/core/Editor/url/url.ts | 10 +- src-web/components/core/EventViewer.tsx | 61 +- src-web/components/core/EventViewerRow.tsx | 16 +- src-web/components/core/FormattedError.tsx | 12 +- src-web/components/core/Heading.tsx | 14 +- src-web/components/core/Hotkey.tsx | 18 +- src-web/components/core/HotkeyLabel.tsx | 8 +- src-web/components/core/HotkeyList.tsx | 14 +- src-web/components/core/HttpMethodTag.tsx | 81 +-- .../core/HttpResponseDurationTag.tsx | 10 +- src-web/components/core/HttpStatusTag.tsx | 30 +- src-web/components/core/Icon.tsx | 52 +- src-web/components/core/IconButton.tsx | 54 +- src-web/components/core/IconTooltip.tsx | 18 +- src-web/components/core/InlineCode.tsx | 8 +- src-web/components/core/Input.tsx | 270 ++++----- src-web/components/core/JsonAttributeTree.tsx | 46 +- src-web/components/core/KeyValueRow.tsx | 16 +- src-web/components/core/Label.tsx | 12 +- src-web/components/core/Link.tsx | 22 +- src-web/components/core/LoadingIcon.tsx | 36 +- src-web/components/core/PairEditor.tsx | 222 ++++---- src-web/components/core/PairEditor.util.tsx | 6 +- src-web/components/core/PairOrBulkEditor.tsx | 24 +- src-web/components/core/PillButton.tsx | 8 +- src-web/components/core/PlainInput.tsx | 118 ++-- src-web/components/core/Prompt.tsx | 22 +- src-web/components/core/RadioCards.tsx | 18 +- src-web/components/core/RadioDropdown.tsx | 24 +- src-web/components/core/SegmentedControl.tsx | 46 +- src-web/components/core/Select.tsx | 82 +-- src-web/components/core/Separator.tsx | 36 +- src-web/components/core/SizeTag.tsx | 4 +- src-web/components/core/SplitLayout.tsx | 48 +- src-web/components/core/Stacks.tsx | 58 +- src-web/components/core/Table.tsx | 26 +- src-web/components/core/Tabs/Tabs.tsx | 154 ++--- src-web/components/core/Toast.tsx | 58 +- src-web/components/core/Tooltip.tsx | 52 +- .../components/core/WebsocketStatusTag.tsx | 30 +- src-web/components/core/tree/Tree.tsx | 98 ++-- .../components/core/tree/TreeDragOverlay.tsx | 14 +- .../components/core/tree/TreeDropMarker.tsx | 12 +- .../components/core/tree/TreeIndentGuide.tsx | 12 +- src-web/components/core/tree/TreeItem.tsx | 86 +-- src-web/components/core/tree/TreeItemList.tsx | 18 +- src-web/components/core/tree/atoms.ts | 14 +- src-web/components/core/tree/common.ts | 4 +- .../core/tree/useSelectableItems.ts | 4 +- .../components/git/BranchSelectionDialog.tsx | 10 +- src-web/components/git/GitCommitDialog.tsx | 123 ++-- src-web/components/git/GitDropdown.tsx | 272 ++++----- src-web/components/git/GitRemotesDialog.tsx | 20 +- src-web/components/git/HistoryDialog.tsx | 12 +- src-web/components/git/callbacks.tsx | 16 +- src-web/components/git/credentials.tsx | 30 +- src-web/components/git/diverged.tsx | 51 +- src-web/components/git/git-util.ts | 32 +- .../components/git/showAddRemoteDialog.tsx | 22 +- src-web/components/git/uncommitted.tsx | 16 +- .../graphql/GraphQLDocsExplorer.tsx | 230 ++++---- src-web/components/graphql/GraphQLEditor.tsx | 82 +-- src-web/components/graphql/graphqlAtoms.ts | 4 +- .../responseViewers/AudioViewer.tsx | 6 +- .../responseViewers/BinaryViewer.tsx | 20 +- .../components/responseViewers/CsvViewer.tsx | 12 +- .../responseViewers/EventStreamViewer.tsx | 42 +- .../responseViewers/HTMLOrTextViewer.tsx | 28 +- .../responseViewers/ImageViewer.tsx | 14 +- .../components/responseViewers/JsonViewer.tsx | 6 +- .../responseViewers/MultipartViewer.tsx | 42 +- .../components/responseViewers/PdfViewer.tsx | 36 +- .../components/responseViewers/SvgViewer.tsx | 6 +- .../components/responseViewers/TextViewer.tsx | 48 +- .../responseViewers/VideoViewer.tsx | 6 +- .../responseViewers/WebPageViewer.tsx | 6 +- src-web/font-size.ts | 14 +- src-web/font.ts | 20 +- src-web/hooks/useActiveCookieJar.ts | 18 +- src-web/hooks/useActiveEnvironment.ts | 12 +- .../hooks/useActiveEnvironmentVariables.ts | 6 +- src-web/hooks/useActiveFolder.ts | 6 +- src-web/hooks/useActiveFolderId.ts | 8 +- src-web/hooks/useActiveRequest.ts | 12 +- src-web/hooks/useActiveRequestId.ts | 8 +- src-web/hooks/useActiveWorkspace.ts | 10 +- .../hooks/useActiveWorkspaceChangedToast.tsx | 12 +- src-web/hooks/useAllRequests.ts | 4 +- src-web/hooks/useAuthTab.tsx | 94 ++-- src-web/hooks/useCancelHttpResponse.ts | 6 +- src-web/hooks/useCheckForUpdates.tsx | 20 +- src-web/hooks/useClickOutside.ts | 12 +- src-web/hooks/useContainerQuery.ts | 4 +- src-web/hooks/useCopyHttpResponse.ts | 10 +- src-web/hooks/useCreateCookieJar.ts | 28 +- src-web/hooks/useCreateDropdownItems.tsx | 62 +- src-web/hooks/useCreateWorkspace.tsx | 12 +- src-web/hooks/useDebouncedState.ts | 6 +- src-web/hooks/useDebouncedValue.ts | 4 +- src-web/hooks/useDeleteGrpcConnections.ts | 8 +- src-web/hooks/useDeleteHttpResponses.ts | 8 +- src-web/hooks/useDeleteSendHistory.tsx | 40 +- .../hooks/useEnvironmentValueVisibility.ts | 8 +- src-web/hooks/useEnvironmentVariables.ts | 22 +- src-web/hooks/useEnvironmentsBreakdown.ts | 10 +- src-web/hooks/useEventViewerKeyboard.ts | 18 +- src-web/hooks/useExportData.tsx | 30 +- src-web/hooks/useFastMutation.ts | 14 +- src-web/hooks/useFloatingSidebarHidden.ts | 10 +- src-web/hooks/useFolderActions.ts | 20 +- src-web/hooks/useFormatText.ts | 14 +- src-web/hooks/useGrpc.ts | 42 +- src-web/hooks/useGrpcProtoFiles.ts | 8 +- src-web/hooks/useGrpcRequestActions.ts | 22 +- src-web/hooks/useHeadersTab.tsx | 14 +- src-web/hooks/useHotKey.ts | 350 ++++++------ src-web/hooks/useHttpAuthentication.ts | 24 +- src-web/hooks/useHttpAuthenticationConfig.ts | 32 +- src-web/hooks/useHttpRequestActions.ts | 20 +- src-web/hooks/useHttpRequestBody.ts | 12 +- src-web/hooks/useHttpResponseEvents.ts | 24 +- src-web/hooks/useImportCurl.ts | 28 +- src-web/hooks/useInheritedAuthentication.ts | 8 +- src-web/hooks/useInheritedHeaders.ts | 12 +- src-web/hooks/useInstallPlugin.ts | 6 +- src-web/hooks/useIntrospectGraphQL.ts | 61 +- src-web/hooks/useIsEncryptionEnabled.ts | 4 +- src-web/hooks/useIsFullscreen.ts | 10 +- src-web/hooks/useKeyValue.ts | 28 +- src-web/hooks/useKeyboardEvent.ts | 6 +- src-web/hooks/useLatestGrpcConnection.ts | 6 +- src-web/hooks/useLatestHttpResponse.ts | 6 +- src-web/hooks/useListenToTauriEvent.ts | 10 +- src-web/hooks/useModelAncestors.ts | 12 +- src-web/hooks/useParentFolders.ts | 8 +- src-web/hooks/usePinnedGrpcConnection.ts | 34 +- src-web/hooks/usePinnedHttpResponse.ts | 14 +- src-web/hooks/usePinnedWebsocketConnection.ts | 36 +- src-web/hooks/usePluginInfo.ts | 20 +- src-web/hooks/usePlugins.ts | 24 +- src-web/hooks/usePortal.ts | 8 +- src-web/hooks/usePreferredAppearance.ts | 6 +- src-web/hooks/useRandomKey.ts | 4 +- src-web/hooks/useRecentCookieJars.ts | 18 +- src-web/hooks/useRecentEnvironments.ts | 16 +- src-web/hooks/useRecentRequests.ts | 16 +- src-web/hooks/useRecentWorkspaces.ts | 20 +- src-web/hooks/useRenderTemplate.ts | 22 +- src-web/hooks/useRequestEditor.tsx | 16 +- src-web/hooks/useRequestUpdateKey.ts | 28 +- src-web/hooks/useResolvedAppearance.ts | 8 +- src-web/hooks/useResolvedTheme.ts | 14 +- src-web/hooks/useResponseBodyEventSource.ts | 10 +- src-web/hooks/useResponseBodyText.ts | 12 +- src-web/hooks/useResponseViewMode.ts | 8 +- src-web/hooks/useSaveResponse.tsx | 32 +- src-web/hooks/useScrollIntoView.ts | 4 +- src-web/hooks/useSendAnyHttpRequest.ts | 24 +- src-web/hooks/useSendManyRequests.ts | 6 +- src-web/hooks/useShouldFloatSidebar.ts | 2 +- src-web/hooks/useSidebarHidden.ts | 10 +- src-web/hooks/useSidebarItemCollapsed.ts | 8 +- src-web/hooks/useSidebarWidth.ts | 10 +- src-web/hooks/useStateWithDeps.ts | 4 +- src-web/hooks/useStoplightsVisible.ts | 6 +- src-web/hooks/useSyncFontSizeSetting.ts | 10 +- src-web/hooks/useSyncWorkspaceChildModels.ts | 8 +- src-web/hooks/useSyncWorkspaceRequestTitle.ts | 22 +- src-web/hooks/useSyncZoomSetting.ts | 18 +- src-web/hooks/useTemplateFunctionConfig.ts | 26 +- src-web/hooks/useTemplateFunctions.tsx | 22 +- src-web/hooks/useTemplateTokensToString.ts | 10 +- src-web/hooks/useTimedBoolean.ts | 4 +- src-web/hooks/useTimelineViewMode.ts | 10 +- src-web/hooks/useToggle.ts | 2 +- src-web/hooks/useToggleCommandPalette.tsx | 14 +- src-web/hooks/useWebsocketRequestActions.ts | 20 +- src-web/hooks/useWindowFocus.ts | 6 +- src-web/hooks/useWorkspaceActions.ts | 20 +- src-web/hooks/useZoom.ts | 6 +- src-web/index.html | 58 +- src-web/init/sync.ts | 30 +- src-web/lib/alert.ts | 18 +- src-web/lib/appInfo.ts | 8 +- src-web/lib/atoms.ts | 20 +- src-web/lib/atoms/atomWithKVStorage.ts | 8 +- src-web/lib/capitalize.ts | 4 +- src-web/lib/color.ts | 2 +- src-web/lib/confirm.ts | 20 +- src-web/lib/constants.ts | 6 +- src-web/lib/contentType.ts | 74 +-- src-web/lib/copy.ts | 14 +- src-web/lib/createRequestAndNavigate.tsx | 14 +- src-web/lib/data/charsets.ts | 238 ++++---- src-web/lib/data/connections.ts | 2 +- src-web/lib/data/encodings.ts | 2 +- src-web/lib/data/headerNames.ts | 104 ++-- src-web/lib/data/mimetypes.ts | 422 +++++++------- src-web/lib/defaultHeaders.ts | 6 +- src-web/lib/deleteModelWithConfirm.tsx | 24 +- src-web/lib/dialog.ts | 6 +- src-web/lib/diffYaml.ts | 6 +- src-web/lib/dnd.ts | 12 +- .../duplicateRequestOrFolderAndNavigate.tsx | 14 +- src-web/lib/editEnvironment.tsx | 28 +- src-web/lib/encryption.ts | 44 +- src-web/lib/fireAndForget.ts | 8 +- src-web/lib/formatters.ts | 24 +- src-web/lib/generateId.ts | 4 +- src-web/lib/getNodeText.ts | 10 +- src-web/lib/importData.tsx | 62 +- src-web/lib/initGlobalListeners.tsx | 172 +++--- src-web/lib/jotai.ts | 2 +- src-web/lib/jsonComments.ts | 6 +- src-web/lib/keyValueStore.ts | 20 +- src-web/lib/markdown.ts | 14 +- src-web/lib/minPromiseMillis.ts | 2 +- src-web/lib/model_util.test.ts | 82 ++- src-web/lib/model_util.ts | 59 +- src-web/lib/prepareImportQuerystring.ts | 8 +- src-web/lib/prompt-form.tsx | 18 +- src-web/lib/prompt.ts | 22 +- src-web/lib/queryClient.ts | 6 +- src-web/lib/renameModelWithPrompt.tsx | 24 +- src-web/lib/resolvedModelName.ts | 42 +- src-web/lib/responseBody.ts | 14 +- src-web/lib/reveal.ts | 12 +- src-web/lib/router.ts | 6 +- src-web/lib/scopes.ts | 2 +- src-web/lib/sendEphemeralRequest.ts | 8 +- src-web/lib/setWorkspaceSearchParams.ts | 12 +- src-web/lib/settings.ts | 6 +- src-web/lib/setupOrConfigureEncryption.tsx | 16 +- src-web/lib/showColorPicker.tsx | 14 +- src-web/lib/tauri.ts | 106 ++-- src-web/lib/theme/appearance.ts | 28 +- src-web/lib/theme/themes.ts | 106 ++-- src-web/lib/theme/window.ts | 54 +- src-web/lib/theme/yaakColor.ts | 26 +- src-web/lib/toast.tsx | 16 +- src-web/main.tsx | 36 +- src-web/modules.d.ts | 2 +- src-web/package.json | 2 +- src-web/postcss.config.cjs | 10 +- src-web/routeTree.gen.ts | 211 ++++--- src-web/routes/__root.tsx | 30 +- src-web/routes/index.tsx | 6 +- .../routes/workspaces/$workspaceId/index.tsx | 8 +- .../$workspaceId/requests/$requestId.tsx | 6 +- .../workspaces/$workspaceId/settings.tsx | 10 +- src-web/routes/workspaces/index.tsx | 6 +- src-web/tailwind.config.cjs | 184 +++--- src-web/theme.ts | 26 +- src-web/vite.config.ts | 45 +- vite.config.ts | 8 +- 664 files changed, 13638 insertions(+), 13492 deletions(-) create mode 100644 .oxfmtignore diff --git a/.claude-context.md b/.claude-context.md index 2ca845f7..94a20862 100644 --- a/.claude-context.md +++ b/.claude-context.md @@ -1,9 +1,11 @@ # Claude Context: Detaching Tauri from Yaak ## Goal + Make Yaak runnable as a standalone CLI without Tauri as a dependency. The core Rust crates in `crates/` should be usable independently, while Tauri-specific code lives in `crates-tauri/`. ## Project Structure + ``` crates/ # Core crates - should NOT depend on Tauri crates-tauri/ # Tauri-specific crates (yaak-app, yaak-tauri-utils, etc.) @@ -13,11 +15,13 @@ crates-cli/ # CLI crate (yaak-cli) ## Completed Work ### 1. Folder Restructure + - Moved Tauri-dependent app code to `crates-tauri/yaak-app/` - Created `crates-tauri/yaak-tauri-utils/` for shared Tauri utilities (window traits, api_client, error handling) - Created `crates-cli/yaak-cli/` for the standalone CLI ### 2. Decoupled Crates (no longer depend on Tauri) + - **yaak-models**: Uses `init_standalone()` pattern for CLI database access - **yaak-http**: Removed Tauri plugin, HttpConnectionManager initialized in yaak-app setup - **yaak-common**: Only contains Tauri-free utilities (serde, platform) @@ -25,6 +29,7 @@ crates-cli/ # CLI crate (yaak-cli) - **yaak-grpc**: Replaced AppHandle with GrpcConfig struct, uses tokio::process::Command instead of Tauri sidecar ### 3. CLI Implementation + - Basic CLI at `crates-cli/yaak-cli/src/main.rs` - Commands: workspaces, requests, send (by ID), get (ad-hoc URL), create - Uses same database as Tauri app via `yaak_models::init_standalone()` @@ -32,12 +37,14 @@ crates-cli/ # CLI crate (yaak-cli) ## Remaining Work ### Crates Still Depending on Tauri (in `crates/`) + 1. **yaak-git** (3 files) - Moderate complexity 2. **yaak-plugins** (13 files) - **Hardest** - deeply integrated with Tauri for plugin-to-window communication 3. **yaak-sync** (4 files) - Moderate complexity 4. **yaak-ws** (5 files) - Moderate complexity ### Pattern for Decoupling + 1. Remove Tauri plugin `init()` function from the crate 2. Move commands to `yaak-app/src/commands.rs` or keep inline in `lib.rs` 3. Move extension traits (e.g., `SomethingManagerExt`) to yaak-app or yaak-tauri-utils @@ -47,6 +54,7 @@ crates-cli/ # CLI crate (yaak-cli) 7. Replace `tauri::async_runtime::block_on` with `tokio::runtime::Handle::current().block_on()` ## Key Files + - `crates-tauri/yaak-app/src/lib.rs` - Main Tauri app, setup block initializes managers - `crates-tauri/yaak-app/src/commands.rs` - Migrated Tauri commands - `crates-tauri/yaak-app/src/models_ext.rs` - Database plugin and extension traits @@ -54,9 +62,11 @@ crates-cli/ # CLI crate (yaak-cli) - `crates/yaak-models/src/lib.rs` - Contains `init_standalone()` for CLI usage ## Git Branch + Working on `detach-tauri` branch. ## Recent Commits + ``` c40cff40 Remove Tauri dependencies from yaak-crypto and yaak-grpc df495f1d Move Tauri utilities from yaak-common to yaak-tauri-utils @@ -67,6 +77,7 @@ e718a5f1 Refactor models_ext to use init_standalone from yaak-models ``` ## Testing + - Run `cargo check -p ` to verify a crate builds without Tauri - Run `npm run app-dev` to test the Tauri app still works - Run `cargo run -p yaak-cli -- --help` to test the CLI diff --git a/.claude/commands/release/generate-release-notes.md b/.claude/commands/release/generate-release-notes.md index 97fba5bc..a2e98b4a 100644 --- a/.claude/commands/release/generate-release-notes.md +++ b/.claude/commands/release/generate-release-notes.md @@ -8,7 +8,7 @@ Generate formatted release notes for Yaak releases by analyzing git history and ## What to do 1. Identifies the version tag and previous version -2. Retrieves all commits between versions +2. Retrieves all commits between versions - If the version is a beta version, it retrieves commits between the beta version and previous beta version - If the version is a stable version, it retrieves commits between the stable version and the previous stable version 3. Fetches PR descriptions for linked issues to find: diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index dd84ea78..9b77ea71 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -1,10 +1,9 @@ --- name: Bug report about: Create a report to help us improve -title: '' -labels: '' -assignees: '' - +title: "" +labels: "" +assignees: "" --- **Describe the bug** @@ -12,6 +11,7 @@ A clear and concise description of what the bug is. **To Reproduce** Steps to reproduce the behavior: + 1. Go to '...' 2. Click on '....' 3. Scroll down to '....' @@ -24,15 +24,17 @@ A clear and concise description of what you expected to happen. If applicable, add screenshots to help explain your problem. **Desktop (please complete the following information):** - - OS: [e.g. iOS] - - Browser [e.g. chrome, safari] - - Version [e.g. 22] + +- OS: [e.g. iOS] +- Browser [e.g. chrome, safari] +- Version [e.g. 22] **Smartphone (please complete the following information):** - - Device: [e.g. iPhone6] - - OS: [e.g. iOS8.1] - - Browser [e.g. stock browser, safari] - - Version [e.g. 22] + +- Device: [e.g. iPhone6] +- OS: [e.g. iOS8.1] +- Browser [e.g. stock browser, safari] +- Version [e.g. 22] **Additional context** Add any other context about the problem here. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 33c88d65..7286aa20 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -11,6 +11,7 @@ - [ ] I added or updated tests when reasonable. Approved feedback item (required if not a bug fix or small-scope improvement): + ## Related diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3797737b..55216e09 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -16,7 +16,7 @@ jobs: - uses: actions/checkout@v4 - uses: voidzero-dev/setup-vp@v1 with: - node-version: '24' + node-version: "24" cache: true - uses: dtolnay/rust-toolchain@stable - uses: Swatinem/rust-cache@v2 diff --git a/.github/workflows/claude.yml b/.github/workflows/claude.yml index d300267f..9471a059 100644 --- a/.github/workflows/claude.yml +++ b/.github/workflows/claude.yml @@ -47,4 +47,3 @@ jobs: # See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md # or https://code.claude.com/docs/en/cli-reference for available options # claude_args: '--allowed-tools Bash(gh pr:*)' - diff --git a/.github/workflows/release-app.yml b/.github/workflows/release-app.yml index 32d62a7a..3a6f695f 100644 --- a/.github/workflows/release-app.yml +++ b/.github/workflows/release-app.yml @@ -53,7 +53,7 @@ jobs: - name: Setup Vite+ uses: voidzero-dev/setup-vp@v1 with: - node-version: '24' + node-version: "24" cache: true - name: install Rust stable diff --git a/.github/workflows/sponsors.yml b/.github/workflows/sponsors.yml index 457565e8..008b34c8 100644 --- a/.github/workflows/sponsors.yml +++ b/.github/workflows/sponsors.yml @@ -16,23 +16,23 @@ jobs: uses: JamesIves/github-sponsors-readme-action@v1 with: token: ${{ secrets.SPONSORS_PAT }} - file: 'README.md' + file: "README.md" maximum: 1999 template: 'User avatar: {{{ login }}}  ' active-only: false include-private: true - marker: 'sponsors-base' + marker: "sponsors-base" - name: Generate Sponsors uses: JamesIves/github-sponsors-readme-action@v1 with: token: ${{ secrets.SPONSORS_PAT }} - file: 'README.md' + file: "README.md" minimum: 2000 template: 'User avatar: {{{ login }}}  ' active-only: false include-private: true - marker: 'sponsors-premium' + marker: "sponsors-premium" # ⚠️ Note: You can use any deployment step here to automatically push the README # changes back to your branch. @@ -41,4 +41,4 @@ jobs: with: branch: main force: false - folder: '.' + folder: "." diff --git a/.oxfmtignore b/.oxfmtignore new file mode 100644 index 00000000..7d38738a --- /dev/null +++ b/.oxfmtignore @@ -0,0 +1,2 @@ +**/bindings/** +crates/yaak-templates/pkg/** diff --git a/.vscode/extensions.json b/.vscode/extensions.json index bab062e5..b2b5eb73 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,3 +1,7 @@ { - "recommendations": ["rust-lang.rust-analyzer", "bradlc.vscode-tailwindcss", "VoidZero.vite-plus-extension-pack"] + "recommendations": [ + "rust-lang.rust-analyzer", + "bradlc.vscode-tailwindcss", + "VoidZero.vite-plus-extension-pack" + ] } diff --git a/Cargo.toml b/Cargo.toml index ac0e884f..acd11e16 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,30 +1,30 @@ [workspace] resolver = "2" members = [ - "crates/yaak", - # Shared crates (no Tauri dependency) - "crates/yaak-core", - "crates/yaak-common", - "crates/yaak-crypto", - "crates/yaak-git", - "crates/yaak-grpc", - "crates/yaak-http", - "crates/yaak-models", - "crates/yaak-plugins", - "crates/yaak-sse", - "crates/yaak-sync", - "crates/yaak-templates", - "crates/yaak-tls", - "crates/yaak-ws", - "crates/yaak-api", - # CLI crates - "crates-cli/yaak-cli", - # Tauri-specific crates - "crates-tauri/yaak-app", - "crates-tauri/yaak-fonts", - "crates-tauri/yaak-license", - "crates-tauri/yaak-mac-window", - "crates-tauri/yaak-tauri-utils", + "crates/yaak", + # Shared crates (no Tauri dependency) + "crates/yaak-core", + "crates/yaak-common", + "crates/yaak-crypto", + "crates/yaak-git", + "crates/yaak-grpc", + "crates/yaak-http", + "crates/yaak-models", + "crates/yaak-plugins", + "crates/yaak-sse", + "crates/yaak-sync", + "crates/yaak-templates", + "crates/yaak-tls", + "crates/yaak-ws", + "crates/yaak-api", + # CLI crates + "crates-cli/yaak-cli", + # Tauri-specific crates + "crates-tauri/yaak-app", + "crates-tauri/yaak-fonts", + "crates-tauri/yaak-license", + "crates-tauri/yaak-mac-window", + "crates-tauri/yaak-tauri-utils", ] [workspace.dependencies] diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index 77ba2cf9..cf5683a2 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -1,24 +1,26 @@ # Developer Setup -Yaak is a combined Node.js and Rust monorepo. It is a [Tauri](https://tauri.app) project, so +Yaak is a combined Node.js and Rust monorepo. It is a [Tauri](https://tauri.app) project, so uses Rust and HTML/CSS/JS for the main application but there is also a plugin system powered by a Node.js sidecar that communicates to the app over gRPC. -Because of the moving parts, there are a few setup steps required before development can +Because of the moving parts, there are a few setup steps required before development can begin. ## Prerequisites Make sure you have the following tools installed: -- [Node.js](https://nodejs.org/en/download/package-manager) +- [Node.js](https://nodejs.org/en/download/package-manager) (v24+) - [Rust](https://www.rust-lang.org/tools/install) +- [Vite+](https://vite.dev/guide/vite-plus) (`vp` CLI) Check the installations with the following commands: ```shell node -v npm -v +vp --version rustc --version ``` @@ -45,12 +47,12 @@ npm start ## SQLite Migrations New migrations can be created from the `src-tauri/` directory: - + ```shell npm run migration ``` -Rerun the app to apply the migrations. +Rerun the app to apply the migrations. _Note: For safety, development builds use a separate database location from production builds._ @@ -61,9 +63,9 @@ _Note: For safety, development builds use a separate database location from prod lezer-generator components/core/Editor//.grammar > components/core/Editor//.ts ``` -## Linting & Formatting +## Linting and Formatting -This repo uses Biome for linting and formatting (replacing ESLint + Prettier). +This repo uses [Vite+](https://vite.dev/guide/vite-plus) for linting (oxlint) and formatting (oxfmt). - Lint the entire repo: @@ -71,12 +73,6 @@ This repo uses Biome for linting and formatting (replacing ESLint + Prettier). npm run lint ``` -- Auto-fix lint issues where possible: - -```sh -npm run lint:fix -``` - - Format code: ```sh @@ -84,5 +80,7 @@ npm run format ``` Notes: -- Many workspace packages also expose the same scripts (`lint`, `lint:fix`, and `format`). -- TypeScript type-checking still runs separately via `tsc --noEmit` in relevant packages. + +- A pre-commit hook runs `vp lint` automatically on commit. +- Some workspace packages also run `tsc --noEmit` for type-checking. +- VS Code users should install the recommended extensions for format-on-save support. diff --git a/README.md b/README.md index 3533a39a..b60f8769 100644 --- a/README.md +++ b/README.md @@ -16,8 +16,6 @@


- -

User avatar: MVST-Solutions  User avatar: dharsanb  User avatar: railwayapp  User avatar: caseyamcl  User avatar: bytebase  User avatar:   

@@ -27,12 +25,10 @@ ![Yaak API Client](https://yaak.app/static/screenshot.png) - ## Features -Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it. -Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in. - +Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it. +Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in. ### 🌐 Work with any API @@ -41,21 +37,23 @@ Built with [Tauri](https://tauri.app), Rust, and React, it’s fast, lightweight - Filter and inspect responses with JSONPath or XPath. ### 🔐 Stay secure + - Use OAuth 2.0, JWT, Basic Auth, or custom plugins for authentication. -- Secure sensitive values with encrypted secrets. +- Secure sensitive values with encrypted secrets. - Store secrets in your OS keychain. ### ☁️ Organize & collaborate + - Group requests into workspaces and nested folders. - Use environment variables to switch between dev, staging, and prod. - Mirror workspaces to your filesystem for versioning in Git or syncing with Dropbox. ### 🧩 Extend & customize + - Insert dynamic values like UUIDs or timestamps with template tags. - Pick from built-in themes or build your own. - Create plugins to extend authentication, template tags, or the UI. - ## Contribution Policy > [!IMPORTANT] diff --git a/crates-cli/yaak-cli/Cargo.toml b/crates-cli/yaak-cli/Cargo.toml index 4c62578e..b3cb4b73 100644 --- a/crates-cli/yaak-cli/Cargo.toml +++ b/crates-cli/yaak-cli/Cargo.toml @@ -29,7 +29,14 @@ schemars = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } sha2 = { workspace = true } -tokio = { workspace = true, features = ["rt-multi-thread", "macros", "io-util", "net", "signal", "time"] } +tokio = { workspace = true, features = [ + "rt-multi-thread", + "macros", + "io-util", + "net", + "signal", + "time", +] } walkdir = "2" webbrowser = "1" zip = "4" diff --git a/crates-cli/yaak-cli/README.md b/crates-cli/yaak-cli/README.md index 65f36868..32157e2d 100644 --- a/crates-cli/yaak-cli/README.md +++ b/crates-cli/yaak-cli/README.md @@ -1,6 +1,6 @@ # Yaak CLI -The `yaak` CLI for publishing plugins and creating/updating/sending requests. +The `yaak` CLI for publishing plugins and creating/updating/sending requests. ## Installation @@ -24,8 +24,8 @@ Use the `yaak` CLI with agents like Claude or Codex to do useful things for you. Here are some example prompts: ```text -Scan my API routes and create a workspace (using yaak cli) with -all the requests needed for me to do manual testing? +Scan my API routes and create a workspace (using yaak cli) with +all the requests needed for me to do manual testing? ``` ```text diff --git a/crates-tauri/yaak-app/Cargo.toml b/crates-tauri/yaak-app/Cargo.toml index 06afbbc0..2212a585 100644 --- a/crates-tauri/yaak-app/Cargo.toml +++ b/crates-tauri/yaak-app/Cargo.toml @@ -35,7 +35,16 @@ r2d2 = "0.8.10" r2d2_sqlite = "0.25.0" mime_guess = "2.0.5" rand = "0.9.0" -reqwest = { workspace = true, features = ["multipart", "gzip", "brotli", "deflate", "json", "rustls-tls-manual-roots-no-provider", "socks", "http2"] } +reqwest = { workspace = true, features = [ + "multipart", + "gzip", + "brotli", + "deflate", + "json", + "rustls-tls-manual-roots-no-provider", + "socks", + "http2", +] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true, features = ["raw_value"] } tauri = { workspace = true, features = ["devtools", "protocol-asset"] } diff --git a/crates-tauri/yaak-app/capabilities/default.json b/crates-tauri/yaak-app/capabilities/default.json index 684716e3..4ec64a82 100644 --- a/crates-tauri/yaak-app/capabilities/default.json +++ b/crates-tauri/yaak-app/capabilities/default.json @@ -1,9 +1,7 @@ { "identifier": "default", "description": "Default capabilities for all build variants", - "windows": [ - "*" - ], + "windows": ["*"], "permissions": [ "core:app:allow-identifier", "core:event:allow-emit", diff --git a/crates-tauri/yaak-app/package.json b/crates-tauri/yaak-app/package.json index bfdc28b6..1ade5450 100644 --- a/crates-tauri/yaak-app/package.json +++ b/crates-tauri/yaak-app/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/tauri", - "private": true, "version": "1.0.0", + "private": true, "main": "bindings/index.ts" } diff --git a/crates-tauri/yaak-app/tauri.conf.json b/crates-tauri/yaak-app/tauri.conf.json index 606c72d0..ca6a1c15 100644 --- a/crates-tauri/yaak-app/tauri.conf.json +++ b/crates-tauri/yaak-app/tauri.conf.json @@ -14,10 +14,7 @@ "assetProtocol": { "enable": true, "scope": { - "allow": [ - "$APPDATA/responses/*", - "$RESOURCE/static/*" - ] + "allow": ["$APPDATA/responses/*", "$RESOURCE/static/*"] } } } @@ -25,9 +22,7 @@ "plugins": { "deep-link": { "desktop": { - "schemes": [ - "yaak" - ] + "schemes": ["yaak"] } } }, diff --git a/crates-tauri/yaak-app/tauri.release.conf.json b/crates-tauri/yaak-app/tauri.release.conf.json index 0f6151a0..bc91f894 100644 --- a/crates-tauri/yaak-app/tauri.release.conf.json +++ b/crates-tauri/yaak-app/tauri.release.conf.json @@ -16,9 +16,7 @@ }, "plugins": { "updater": { - "endpoints": [ - "https://update.yaak.app/check/{{target}}/{{arch}}/{{current_version}}" - ], + "endpoints": ["https://update.yaak.app/check/{{target}}/{{arch}}/{{current_version}}"], "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IEVGRkFGMjQxRUNEOTQ3MzAKUldRd1I5bnNRZkw2NzRtMnRlWTN3R24xYUR3aGRsUjJzWGwvdHdEcGljb3ZJMUNlMjFsaHlqVU4K" } }, diff --git a/crates-tauri/yaak-fonts/index.ts b/crates-tauri/yaak-fonts/index.ts index acb05258..7d227d0f 100644 --- a/crates-tauri/yaak-fonts/index.ts +++ b/crates-tauri/yaak-fonts/index.ts @@ -1,14 +1,14 @@ -import { useQuery } from '@tanstack/react-query'; -import { invoke } from '@tauri-apps/api/core'; -import { Fonts } from './bindings/gen_fonts'; +import { useQuery } from "@tanstack/react-query"; +import { invoke } from "@tauri-apps/api/core"; +import { Fonts } from "./bindings/gen_fonts"; export async function listFonts() { - return invoke('plugin:yaak-fonts|list', {}); + return invoke("plugin:yaak-fonts|list", {}); } export function useFonts() { return useQuery({ - queryKey: ['list_fonts'], + queryKey: ["list_fonts"], queryFn: () => listFonts(), }); } diff --git a/crates-tauri/yaak-fonts/package.json b/crates-tauri/yaak-fonts/package.json index 36cb741e..d84f0d9e 100644 --- a/crates-tauri/yaak-fonts/package.json +++ b/crates-tauri/yaak-fonts/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/fonts", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates-tauri/yaak-license/index.ts b/crates-tauri/yaak-license/index.ts index 7de30646..ec789fac 100644 --- a/crates-tauri/yaak-license/index.ts +++ b/crates-tauri/yaak-license/index.ts @@ -1,31 +1,31 @@ -import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'; -import { invoke } from '@tauri-apps/api/core'; -import { listen } from '@tauri-apps/api/event'; -import { appInfo } from '@yaakapp/app/lib/appInfo'; -import { useEffect } from 'react'; -import { LicenseCheckStatus } from './bindings/license'; +import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; +import { invoke } from "@tauri-apps/api/core"; +import { listen } from "@tauri-apps/api/event"; +import { appInfo } from "@yaakapp/app/lib/appInfo"; +import { useEffect } from "react"; +import { LicenseCheckStatus } from "./bindings/license"; -export * from './bindings/license'; +export * from "./bindings/license"; -const CHECK_QUERY_KEY = ['license.check']; +const CHECK_QUERY_KEY = ["license.check"]; export function useLicense() { const queryClient = useQueryClient(); const activate = useMutation({ - mutationKey: ['license.activate'], - mutationFn: (payload) => invoke('plugin:yaak-license|activate', payload), + mutationKey: ["license.activate"], + mutationFn: (payload) => invoke("plugin:yaak-license|activate", payload), onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }), }); const deactivate = useMutation({ - mutationKey: ['license.deactivate'], - mutationFn: () => invoke('plugin:yaak-license|deactivate'), + mutationKey: ["license.deactivate"], + mutationFn: () => invoke("plugin:yaak-license|deactivate"), onSuccess: () => queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }), }); // Check the license again after a license is activated useEffect(() => { - const unlisten = listen('license-activated', async () => { + const unlisten = listen("license-activated", async () => { await queryClient.invalidateQueries({ queryKey: CHECK_QUERY_KEY }); }); return () => { @@ -41,7 +41,7 @@ export function useLicense() { if (!appInfo.featureLicense) { return null; } - return invoke('plugin:yaak-license|check'); + return invoke("plugin:yaak-license|check"); }, }); diff --git a/crates-tauri/yaak-license/package.json b/crates-tauri/yaak-license/package.json index c6c8d557..f3861900 100644 --- a/crates-tauri/yaak-license/package.json +++ b/crates-tauri/yaak-license/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/license", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates-tauri/yaak-mac-window/index.ts b/crates-tauri/yaak-mac-window/index.ts index 1d01c0c9..d2c98823 100644 --- a/crates-tauri/yaak-mac-window/index.ts +++ b/crates-tauri/yaak-mac-window/index.ts @@ -1,9 +1,9 @@ -import { invoke } from '@tauri-apps/api/core'; +import { invoke } from "@tauri-apps/api/core"; export function setWindowTitle(title: string) { - invoke('plugin:yaak-mac-window|set_title', { title }).catch(console.error); + invoke("plugin:yaak-mac-window|set_title", { title }).catch(console.error); } export function setWindowTheme(bgColor: string) { - invoke('plugin:yaak-mac-window|set_theme', { bgColor }).catch(console.error); + invoke("plugin:yaak-mac-window|set_theme", { bgColor }).catch(console.error); } diff --git a/crates-tauri/yaak-mac-window/package.json b/crates-tauri/yaak-mac-window/package.json index e4f23426..2c4cda03 100644 --- a/crates-tauri/yaak-mac-window/package.json +++ b/crates-tauri/yaak-mac-window/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/mac-window", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates-tauri/yaak-mac-window/permissions/default.toml b/crates-tauri/yaak-mac-window/permissions/default.toml index 31b4fcf1..ea9b21cc 100644 --- a/crates-tauri/yaak-mac-window/permissions/default.toml +++ b/crates-tauri/yaak-mac-window/permissions/default.toml @@ -1,6 +1,3 @@ [default] description = "Default permissions for the plugin" -permissions = [ - "allow-set-title", - "allow-set-theme", -] +permissions = ["allow-set-title", "allow-set-theme"] diff --git a/crates/yaak-crypto/index.ts b/crates/yaak-crypto/index.ts index 66f3910b..58179042 100644 --- a/crates/yaak-crypto/index.ts +++ b/crates/yaak-crypto/index.ts @@ -1,17 +1,17 @@ -import { invoke } from '@tauri-apps/api/core'; +import { invoke } from "@tauri-apps/api/core"; export function enableEncryption(workspaceId: string) { - return invoke('cmd_enable_encryption', { workspaceId }); + return invoke("cmd_enable_encryption", { workspaceId }); } export function revealWorkspaceKey(workspaceId: string) { - return invoke('cmd_reveal_workspace_key', { workspaceId }); + return invoke("cmd_reveal_workspace_key", { workspaceId }); } export function setWorkspaceKey(args: { workspaceId: string; key: string }) { - return invoke('cmd_set_workspace_key', args); + return invoke("cmd_set_workspace_key", args); } export function disableEncryption(workspaceId: string) { - return invoke('cmd_disable_encryption', { workspaceId }); + return invoke("cmd_disable_encryption", { workspaceId }); } diff --git a/crates/yaak-crypto/package.json b/crates/yaak-crypto/package.json index 96b3d62c..585b375a 100644 --- a/crates/yaak-crypto/package.json +++ b/crates/yaak-crypto/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/crypto", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates/yaak-git/index.ts b/crates/yaak-git/index.ts index 559b1e5e..ae28fc56 100644 --- a/crates/yaak-git/index.ts +++ b/crates/yaak-git/index.ts @@ -1,60 +1,66 @@ -import { useQuery } from '@tanstack/react-query'; -import { invoke } from '@tauri-apps/api/core'; -import { createFastMutation } from '@yaakapp/app/hooks/useFastMutation'; -import { queryClient } from '@yaakapp/app/lib/queryClient'; -import { useMemo } from 'react'; -import { BranchDeleteResult, CloneResult, GitCommit, GitRemote, GitStatusSummary, PullResult, PushResult } from './bindings/gen_git'; -import { showToast } from '@yaakapp/app/lib/toast'; +import { useQuery } from "@tanstack/react-query"; +import { invoke } from "@tauri-apps/api/core"; +import { createFastMutation } from "@yaakapp/app/hooks/useFastMutation"; +import { queryClient } from "@yaakapp/app/lib/queryClient"; +import { useMemo } from "react"; +import { + BranchDeleteResult, + CloneResult, + GitCommit, + GitRemote, + GitStatusSummary, + PullResult, + PushResult, +} from "./bindings/gen_git"; +import { showToast } from "@yaakapp/app/lib/toast"; -export * from './bindings/gen_git'; -export * from './bindings/gen_models'; +export * from "./bindings/gen_git"; +export * from "./bindings/gen_models"; export interface GitCredentials { username: string; password: string; } -export type DivergedStrategy = 'force_reset' | 'merge' | 'cancel'; +export type DivergedStrategy = "force_reset" | "merge" | "cancel"; -export type UncommittedChangesStrategy = 'reset' | 'cancel'; +export type UncommittedChangesStrategy = "reset" | "cancel"; export interface GitCallbacks { addRemote: () => Promise; promptCredentials: ( - result: Extract, + result: Extract, ) => Promise; - promptDiverged: ( - result: Extract, - ) => Promise; + promptDiverged: (result: Extract) => Promise; promptUncommittedChanges: () => Promise; forceSync: () => Promise; } -const onSuccess = () => queryClient.invalidateQueries({ queryKey: ['git'] }); +const onSuccess = () => queryClient.invalidateQueries({ queryKey: ["git"] }); export function useGit(dir: string, callbacks: GitCallbacks, refreshKey?: string) { const mutations = useMemo(() => gitMutations(dir, callbacks), [dir, callbacks]); const fetchAll = useQuery({ - queryKey: ['git', 'fetch_all', dir, refreshKey], - queryFn: () => invoke('cmd_git_fetch_all', { dir }), + queryKey: ["git", "fetch_all", dir, refreshKey], + queryFn: () => invoke("cmd_git_fetch_all", { dir }), refetchInterval: 10 * 60_000, }); return [ { remotes: useQuery({ - queryKey: ['git', 'remotes', dir, refreshKey], + queryKey: ["git", "remotes", dir, refreshKey], queryFn: () => getRemotes(dir), placeholderData: (prev) => prev, }), log: useQuery({ - queryKey: ['git', 'log', dir, refreshKey], - queryFn: () => invoke('cmd_git_log', { dir }), + queryKey: ["git", "log", dir, refreshKey], + queryFn: () => invoke("cmd_git_log", { dir }), placeholderData: (prev) => prev, }), status: useQuery({ refetchOnMount: true, - queryKey: ['git', 'status', dir, refreshKey, fetchAll.dataUpdatedAt], - queryFn: () => invoke('cmd_git_status', { dir }), + queryKey: ["git", "status", dir, refreshKey, fetchAll.dataUpdatedAt], + queryFn: () => invoke("cmd_git_status", { dir }), placeholderData: (prev) => prev, }), }, @@ -67,151 +73,167 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => { const remotes = await getRemotes(dir); if (remotes.length === 0) { const remote = await callbacks.addRemote(); - if (remote == null) throw new Error('No remote found'); + if (remote == null) throw new Error("No remote found"); } - const result = await invoke('cmd_git_push', { dir }); - if (result.type !== 'needs_credentials') return result; + const result = await invoke("cmd_git_push", { dir }); + if (result.type !== "needs_credentials") return result; // Needs credentials, prompt for them const creds = await callbacks.promptCredentials(result); - if (creds == null) throw new Error('Canceled'); + if (creds == null) throw new Error("Canceled"); - await invoke('cmd_git_add_credential', { + await invoke("cmd_git_add_credential", { remoteUrl: result.url, username: creds.username, password: creds.password, }); // Push again - return invoke('cmd_git_push', { dir }); + return invoke("cmd_git_push", { dir }); }; const handleError = (err: unknown) => { showToast({ id: err instanceof Error ? err.message : String(err), message: err instanceof Error ? err.message : String(err), - color: 'danger', + color: "danger", timeout: 5000, }); - } + }; return { init: createFastMutation({ - mutationKey: ['git', 'init'], - mutationFn: () => invoke('cmd_git_initialize', { dir }), + mutationKey: ["git", "init"], + mutationFn: () => invoke("cmd_git_initialize", { dir }), onSuccess, }), add: createFastMutation({ - mutationKey: ['git', 'add', dir], - mutationFn: (args) => invoke('cmd_git_add', { dir, ...args }), + mutationKey: ["git", "add", dir], + mutationFn: (args) => invoke("cmd_git_add", { dir, ...args }), onSuccess, }), addRemote: createFastMutation({ - mutationKey: ['git', 'add-remote'], - mutationFn: (args) => invoke('cmd_git_add_remote', { dir, ...args }), + mutationKey: ["git", "add-remote"], + mutationFn: (args) => invoke("cmd_git_add_remote", { dir, ...args }), onSuccess, }), rmRemote: createFastMutation({ - mutationKey: ['git', 'rm-remote', dir], - mutationFn: (args) => invoke('cmd_git_rm_remote', { dir, ...args }), + mutationKey: ["git", "rm-remote", dir], + mutationFn: (args) => invoke("cmd_git_rm_remote", { dir, ...args }), onSuccess, }), createBranch: createFastMutation({ - mutationKey: ['git', 'branch', dir], - mutationFn: (args) => invoke('cmd_git_branch', { dir, ...args }), + mutationKey: ["git", "branch", dir], + mutationFn: (args) => invoke("cmd_git_branch", { dir, ...args }), onSuccess, }), mergeBranch: createFastMutation({ - mutationKey: ['git', 'merge', dir], - mutationFn: (args) => invoke('cmd_git_merge_branch', { dir, ...args }), + mutationKey: ["git", "merge", dir], + mutationFn: (args) => invoke("cmd_git_merge_branch", { dir, ...args }), onSuccess, }), - deleteBranch: createFastMutation({ - mutationKey: ['git', 'delete-branch', dir], - mutationFn: (args) => invoke('cmd_git_delete_branch', { dir, ...args }), + deleteBranch: createFastMutation< + BranchDeleteResult, + string, + { branch: string; force?: boolean } + >({ + mutationKey: ["git", "delete-branch", dir], + mutationFn: (args) => invoke("cmd_git_delete_branch", { dir, ...args }), onSuccess, }), deleteRemoteBranch: createFastMutation({ - mutationKey: ['git', 'delete-remote-branch', dir], - mutationFn: (args) => invoke('cmd_git_delete_remote_branch', { dir, ...args }), + mutationKey: ["git", "delete-remote-branch", dir], + mutationFn: (args) => invoke("cmd_git_delete_remote_branch", { dir, ...args }), onSuccess, }), - renameBranch: createFastMutation({ - mutationKey: ['git', 'rename-branch', dir], - mutationFn: (args) => invoke('cmd_git_rename_branch', { dir, ...args }), + renameBranch: createFastMutation({ + mutationKey: ["git", "rename-branch", dir], + mutationFn: (args) => invoke("cmd_git_rename_branch", { dir, ...args }), onSuccess, }), checkout: createFastMutation({ - mutationKey: ['git', 'checkout', dir], - mutationFn: (args) => invoke('cmd_git_checkout', { dir, ...args }), + mutationKey: ["git", "checkout", dir], + mutationFn: (args) => invoke("cmd_git_checkout", { dir, ...args }), onSuccess, }), commit: createFastMutation({ - mutationKey: ['git', 'commit', dir], - mutationFn: (args) => invoke('cmd_git_commit', { dir, ...args }), + mutationKey: ["git", "commit", dir], + mutationFn: (args) => invoke("cmd_git_commit", { dir, ...args }), onSuccess, }), commitAndPush: createFastMutation({ - mutationKey: ['git', 'commit_push', dir], + mutationKey: ["git", "commit_push", dir], mutationFn: async (args) => { - await invoke('cmd_git_commit', { dir, ...args }); + await invoke("cmd_git_commit", { dir, ...args }); return push(); }, onSuccess, }), push: createFastMutation({ - mutationKey: ['git', 'push', dir], + mutationKey: ["git", "push", dir], mutationFn: push, onSuccess, }), pull: createFastMutation({ - mutationKey: ['git', 'pull', dir], + mutationKey: ["git", "pull", dir], async mutationFn() { - const result = await invoke('cmd_git_pull', { dir }); + const result = await invoke("cmd_git_pull", { dir }); - if (result.type === 'needs_credentials') { + if (result.type === "needs_credentials") { const creds = await callbacks.promptCredentials(result); - if (creds == null) throw new Error('Canceled'); + if (creds == null) throw new Error("Canceled"); - await invoke('cmd_git_add_credential', { + await invoke("cmd_git_add_credential", { remoteUrl: result.url, username: creds.username, password: creds.password, }); // Pull again after credentials - return invoke('cmd_git_pull', { dir }); + return invoke("cmd_git_pull", { dir }); } - if (result.type === 'uncommitted_changes') { - void callbacks.promptUncommittedChanges().then(async (strategy) => { - if (strategy === 'cancel') return; + if (result.type === "uncommitted_changes") { + void callbacks + .promptUncommittedChanges() + .then(async (strategy) => { + if (strategy === "cancel") return; - await invoke('cmd_git_reset_changes', { dir }); - return invoke('cmd_git_pull', { dir }); - }).then(async () => { await onSuccess(); await callbacks.forceSync(); }, handleError); + await invoke("cmd_git_reset_changes", { dir }); + return invoke("cmd_git_pull", { dir }); + }) + .then(async () => { + await onSuccess(); + await callbacks.forceSync(); + }, handleError); } - if (result.type === 'diverged') { - void callbacks.promptDiverged(result).then((strategy) => { - if (strategy === 'cancel') return; + if (result.type === "diverged") { + void callbacks + .promptDiverged(result) + .then((strategy) => { + if (strategy === "cancel") return; - if (strategy === 'force_reset') { - return invoke('cmd_git_pull_force_reset', { + if (strategy === "force_reset") { + return invoke("cmd_git_pull_force_reset", { + dir, + remote: result.remote, + branch: result.branch, + }); + } + + return invoke("cmd_git_pull_merge", { dir, remote: result.remote, branch: result.branch, }); - } - - return invoke('cmd_git_pull_merge', { - dir, - remote: result.remote, - branch: result.branch, - }); - }).then(async () => { await onSuccess(); await callbacks.forceSync(); }, handleError); + }) + .then(async () => { + await onSuccess(); + await callbacks.forceSync(); + }, handleError); } return result; @@ -219,20 +241,20 @@ export const gitMutations = (dir: string, callbacks: GitCallbacks) => { onSuccess, }), unstage: createFastMutation({ - mutationKey: ['git', 'unstage', dir], - mutationFn: (args) => invoke('cmd_git_unstage', { dir, ...args }), + mutationKey: ["git", "unstage", dir], + mutationFn: (args) => invoke("cmd_git_unstage", { dir, ...args }), onSuccess, }), resetChanges: createFastMutation({ - mutationKey: ['git', 'reset-changes', dir], - mutationFn: () => invoke('cmd_git_reset_changes', { dir }), + mutationKey: ["git", "reset-changes", dir], + mutationFn: () => invoke("cmd_git_reset_changes", { dir }), onSuccess, }), } as const; }; async function getRemotes(dir: string) { - return invoke('cmd_git_remotes', { dir }); + return invoke("cmd_git_remotes", { dir }); } /** @@ -241,21 +263,24 @@ async function getRemotes(dir: string) { export async function gitClone( url: string, dir: string, - promptCredentials: (args: { url: string; error: string | null }) => Promise, + promptCredentials: (args: { + url: string; + error: string | null; + }) => Promise, ): Promise { - const result = await invoke('cmd_git_clone', { url, dir }); - if (result.type !== 'needs_credentials') return result; + const result = await invoke("cmd_git_clone", { url, dir }); + if (result.type !== "needs_credentials") return result; // Prompt for credentials const creds = await promptCredentials({ url: result.url, error: result.error }); - if (creds == null) return {type: 'cancelled'}; + if (creds == null) return { type: "cancelled" }; // Store credentials and retry - await invoke('cmd_git_add_credential', { + await invoke("cmd_git_add_credential", { remoteUrl: result.url, username: creds.username, password: creds.password, }); - return invoke('cmd_git_clone', { url, dir }); + return invoke("cmd_git_clone", { url, dir }); } diff --git a/crates/yaak-git/package.json b/crates/yaak-git/package.json index 2c5450b3..671fe49a 100644 --- a/crates/yaak-git/package.json +++ b/crates/yaak-git/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/git", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates/yaak-http/Cargo.toml b/crates/yaak-http/Cargo.toml index 34ebba4b..ece83bfa 100644 --- a/crates/yaak-http/Cargo.toml +++ b/crates/yaak-http/Cargo.toml @@ -19,7 +19,12 @@ hyper-util = { version = "0.1.17", default-features = false, features = ["client log = { workspace = true } mime_guess = "2.0.5" regex = "1.11.1" -reqwest = { workspace = true, features = ["rustls-tls-manual-roots-no-provider", "socks", "http2", "stream"] } +reqwest = { workspace = true, features = [ + "rustls-tls-manual-roots-no-provider", + "socks", + "http2", + "stream", +] } serde = { workspace = true, features = ["derive"] } serde_json = { workspace = true } thiserror = { workspace = true } diff --git a/crates/yaak-models/guest-js/atoms.ts b/crates/yaak-models/guest-js/atoms.ts index da3ad730..b9dc3e18 100644 --- a/crates/yaak-models/guest-js/atoms.ts +++ b/crates/yaak-models/guest-js/atoms.ts @@ -1,35 +1,39 @@ -import { atom } from 'jotai'; +import { atom } from "jotai"; -import { selectAtom } from 'jotai/utils'; -import type { AnyModel } from '../bindings/gen_models'; -import { ExtractModel } from './types'; -import { newStoreData } from './util'; +import { selectAtom } from "jotai/utils"; +import type { AnyModel } from "../bindings/gen_models"; +import { ExtractModel } from "./types"; +import { newStoreData } from "./util"; export const modelStoreDataAtom = atom(newStoreData()); -export const cookieJarsAtom = createOrderedModelAtom('cookie_jar', 'name', 'asc'); -export const environmentsAtom = createOrderedModelAtom('environment', 'sortPriority', 'asc'); -export const foldersAtom = createModelAtom('folder'); -export const grpcConnectionsAtom = createOrderedModelAtom('grpc_connection', 'createdAt', 'desc'); -export const grpcEventsAtom = createOrderedModelAtom('grpc_event', 'createdAt', 'asc'); -export const grpcRequestsAtom = createModelAtom('grpc_request'); -export const httpRequestsAtom = createModelAtom('http_request'); -export const httpResponsesAtom = createOrderedModelAtom('http_response', 'createdAt', 'desc'); -export const httpResponseEventsAtom = createOrderedModelAtom('http_response_event', 'createdAt', 'asc'); -export const keyValuesAtom = createModelAtom('key_value'); -export const pluginsAtom = createModelAtom('plugin'); -export const settingsAtom = createSingularModelAtom('settings'); -export const websocketRequestsAtom = createModelAtom('websocket_request'); -export const websocketEventsAtom = createOrderedModelAtom('websocket_event', 'createdAt', 'asc'); -export const websocketConnectionsAtom = createOrderedModelAtom( - 'websocket_connection', - 'createdAt', - 'desc', +export const cookieJarsAtom = createOrderedModelAtom("cookie_jar", "name", "asc"); +export const environmentsAtom = createOrderedModelAtom("environment", "sortPriority", "asc"); +export const foldersAtom = createModelAtom("folder"); +export const grpcConnectionsAtom = createOrderedModelAtom("grpc_connection", "createdAt", "desc"); +export const grpcEventsAtom = createOrderedModelAtom("grpc_event", "createdAt", "asc"); +export const grpcRequestsAtom = createModelAtom("grpc_request"); +export const httpRequestsAtom = createModelAtom("http_request"); +export const httpResponsesAtom = createOrderedModelAtom("http_response", "createdAt", "desc"); +export const httpResponseEventsAtom = createOrderedModelAtom( + "http_response_event", + "createdAt", + "asc", ); -export const workspaceMetasAtom = createModelAtom('workspace_meta'); -export const workspacesAtom = createOrderedModelAtom('workspace', 'name', 'asc'); +export const keyValuesAtom = createModelAtom("key_value"); +export const pluginsAtom = createModelAtom("plugin"); +export const settingsAtom = createSingularModelAtom("settings"); +export const websocketRequestsAtom = createModelAtom("websocket_request"); +export const websocketEventsAtom = createOrderedModelAtom("websocket_event", "createdAt", "asc"); +export const websocketConnectionsAtom = createOrderedModelAtom( + "websocket_connection", + "createdAt", + "desc", +); +export const workspaceMetasAtom = createModelAtom("workspace_meta"); +export const workspacesAtom = createOrderedModelAtom("workspace", "name", "asc"); -export function createModelAtom(modelType: M) { +export function createModelAtom(modelType: M) { return selectAtom( modelStoreDataAtom, (data) => Object.values(data[modelType] ?? {}), @@ -37,19 +41,19 @@ export function createModelAtom(modelType: M) { ); } -export function createSingularModelAtom(modelType: M) { +export function createSingularModelAtom(modelType: M) { return selectAtom(modelStoreDataAtom, (data) => { const modelData = Object.values(data[modelType] ?? {}); const item = modelData[0]; - if (item == null) throw new Error('Failed creating singular model with no data: ' + modelType); + if (item == null) throw new Error("Failed creating singular model with no data: " + modelType); return item; }); } -export function createOrderedModelAtom( +export function createOrderedModelAtom( modelType: M, field: keyof ExtractModel, - order: 'asc' | 'desc', + order: "asc" | "desc", ) { return selectAtom( modelStoreDataAtom, @@ -58,7 +62,7 @@ export function createOrderedModelAtom( return Object.values(modelData).sort( (a: ExtractModel, b: ExtractModel) => { const n = a[field] > b[field] ? 1 : -1; - return order === 'desc' ? n * -1 : n; + return order === "desc" ? n * -1 : n; }, ); }, diff --git a/crates/yaak-models/guest-js/index.ts b/crates/yaak-models/guest-js/index.ts index ef564895..7ceff15c 100644 --- a/crates/yaak-models/guest-js/index.ts +++ b/crates/yaak-models/guest-js/index.ts @@ -1,11 +1,11 @@ -import { AnyModel } from '../bindings/gen_models'; +import { AnyModel } from "../bindings/gen_models"; -export * from '../bindings/gen_models'; -export * from '../bindings/gen_util'; -export * from './store'; -export * from './atoms'; +export * from "../bindings/gen_models"; +export * from "../bindings/gen_util"; +export * from "./store"; +export * from "./atoms"; export function modelTypeLabel(m: AnyModel): string { const capitalize = (str: string) => str.charAt(0).toUpperCase() + str.slice(1); - return m.model.split('_').map(capitalize).join(' '); + return m.model.split("_").map(capitalize).join(" "); } diff --git a/crates/yaak-models/guest-js/store.ts b/crates/yaak-models/guest-js/store.ts index 38ff71f9..78a96693 100644 --- a/crates/yaak-models/guest-js/store.ts +++ b/crates/yaak-models/guest-js/store.ts @@ -1,10 +1,10 @@ -import { invoke } from '@tauri-apps/api/core'; -import { getCurrentWebviewWindow } from '@tauri-apps/api/webviewWindow'; -import { resolvedModelName } from '@yaakapp/app/lib/resolvedModelName'; -import { AnyModel, ModelPayload } from '../bindings/gen_models'; -import { modelStoreDataAtom } from './atoms'; -import { ExtractModel, JotaiStore, ModelStoreData } from './types'; -import { newStoreData } from './util'; +import { invoke } from "@tauri-apps/api/core"; +import { getCurrentWebviewWindow } from "@tauri-apps/api/webviewWindow"; +import { resolvedModelName } from "@yaakapp/app/lib/resolvedModelName"; +import { AnyModel, ModelPayload } from "../bindings/gen_models"; +import { modelStoreDataAtom } from "./atoms"; +import { ExtractModel, JotaiStore, ModelStoreData } from "./types"; +import { newStoreData } from "./util"; let _store: JotaiStore | null = null; @@ -12,11 +12,11 @@ export function initModelStore(store: JotaiStore) { _store = store; getCurrentWebviewWindow() - .listen('model_write', ({ payload }) => { + .listen("model_write", ({ payload }) => { if (shouldIgnoreModel(payload)) return; mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => { - if (payload.change.type === 'upsert') { + if (payload.change.type === "upsert") { return { ...prev, [payload.model.model]: { @@ -36,7 +36,7 @@ export function initModelStore(store: JotaiStore) { function mustStore(): JotaiStore { if (_store == null) { - throw new Error('Model store was not initialized'); + throw new Error("Model store was not initialized"); } return _store; @@ -45,8 +45,8 @@ function mustStore(): JotaiStore { let _activeWorkspaceId: string | null = null; export async function changeModelStoreWorkspace(workspaceId: string | null) { - console.log('Syncing models with new workspace', workspaceId); - const workspaceModelsStr = await invoke('models_workspace_models', { + console.log("Syncing models with new workspace", workspaceId); + const workspaceModelsStr = await invoke("models_workspace_models", { workspaceId, // NOTE: if no workspace id provided, it will just fetch global models }); const workspaceModels = JSON.parse(workspaceModelsStr) as AnyModel[]; @@ -57,12 +57,12 @@ export async function changeModelStoreWorkspace(workspaceId: string | null) { mustStore().set(modelStoreDataAtom, data); - console.log('Synced model store with workspace', workspaceId, data); + console.log("Synced model store with workspace", workspaceId, data); _activeWorkspaceId = workspaceId; } -export function listModels>( +export function listModels>( modelType: M | ReadonlyArray, ): T[] { let data = mustStore().get(modelStoreDataAtom); @@ -70,7 +70,7 @@ export function listModels Object.values(data[t]) as T[]); } -export function getModel>( +export function getModel>( modelType: M | ReadonlyArray, id: string, ): T | null { @@ -83,9 +83,7 @@ export function getModel>( +export function patchModelById>( model: M, id: string, patch: Partial | ((prev: T) => T), @@ -105,54 +103,54 @@ export function patchModelById>( - base: Pick, +export async function patchModel>( + base: Pick, patch: Partial, ): Promise { return patchModelById(base.model, base.id, patch); } -export async function updateModel>( +export async function updateModel>( model: T, ): Promise { - return invoke('models_upsert', { model }); + return invoke("models_upsert", { model }); } export async function deleteModelById< - M extends AnyModel['model'], + M extends AnyModel["model"], T extends ExtractModel, >(modelType: M | M[], id: string) { let model = getModel(modelType, id); await deleteModel(model); } -export async function deleteModel>( +export async function deleteModel>( model: T | null, ) { if (model == null) { - throw new Error('Failed to delete null model'); + throw new Error("Failed to delete null model"); } - await invoke('models_delete', { model }); + await invoke("models_delete", { model }); } -export function duplicateModel>( +export function duplicateModel>( model: T | null, ) { if (model == null) { - throw new Error('Failed to duplicate null model'); + throw new Error("Failed to duplicate null model"); } // If the model has a name, try to duplicate it with a name that doesn't conflict - let name = 'name' in model ? resolvedModelName(model) : undefined; + let name = "name" in model ? resolvedModelName(model) : undefined; if (name != null) { const existingModels = listModels(model.model); for (let i = 0; i < 100; i++) { const hasConflict = existingModels.some((m) => { - if ('folderId' in m && 'folderId' in model && model.folderId !== m.folderId) { + if ("folderId" in m && "folderId" in model && model.folderId !== m.folderId) { return false; } else if (resolvedModelName(m) !== name) { return false; @@ -166,7 +164,7 @@ export function duplicateModel\d+))?$/); if (m != null && m.groups?.n == null) { - name = name.substring(0, m.index) + ' Copy 2'; + name = name.substring(0, m.index) + " Copy 2"; } else if (m != null && m.groups?.n != null) { name = name.substring(0, m.index) + ` Copy ${parseInt(m.groups.n) + 1}`; } else { @@ -175,23 +173,23 @@ export function duplicateModel('models_duplicate', { model: { ...model, name } }); + return invoke("models_duplicate", { model: { ...model, name } }); } export async function createGlobalModel>( - patch: Partial & Pick, + patch: Partial & Pick, ): Promise { - return invoke('models_upsert', { model: patch }); + return invoke("models_upsert", { model: patch }); } export async function createWorkspaceModel>( - patch: Partial & Pick, + patch: Partial & Pick, ): Promise { - return invoke('models_upsert', { model: patch }); + return invoke("models_upsert", { model: patch }); } export function replaceModelsInStore< - M extends AnyModel['model'], + M extends AnyModel["model"], T extends Extract, >(model: M, models: T[]) { const newModels: Record = {}; @@ -208,7 +206,7 @@ export function replaceModelsInStore< } export function mergeModelsInStore< - M extends AnyModel['model'], + M extends AnyModel["model"], T extends Extract, >(model: M, models: T[], filter?: (model: T) => boolean) { mustStore().set(modelStoreDataAtom, (prev: ModelStoreData) => { @@ -237,7 +235,7 @@ export function mergeModelsInStore< function shouldIgnoreModel({ model, updateSource }: ModelPayload) { // Never ignore updates from non-user sources - if (updateSource.type !== 'window') { + if (updateSource.type !== "window") { return false; } @@ -247,11 +245,11 @@ function shouldIgnoreModel({ model, updateSource }: ModelPayload) { } // Only sync models that belong to this workspace, if a workspace ID is present - if ('workspaceId' in model && model.workspaceId !== _activeWorkspaceId) { + if ("workspaceId" in model && model.workspaceId !== _activeWorkspaceId) { return true; } - if (model.model === 'key_value' && model.namespace === 'no_sync') { + if (model.model === "key_value" && model.namespace === "no_sync") { return true; } diff --git a/crates/yaak-models/guest-js/types.ts b/crates/yaak-models/guest-js/types.ts index 6f11bad7..56f63062 100644 --- a/crates/yaak-models/guest-js/types.ts +++ b/crates/yaak-models/guest-js/types.ts @@ -1,8 +1,8 @@ -import { createStore } from 'jotai'; -import { AnyModel } from '../bindings/gen_models'; +import { createStore } from "jotai"; +import { AnyModel } from "../bindings/gen_models"; export type ExtractModel = T extends { model: M } ? T : never; export type ModelStoreData = { - [M in T['model']]: Record>; + [M in T["model"]]: Record>; }; export type JotaiStore = ReturnType; diff --git a/crates/yaak-models/guest-js/util.ts b/crates/yaak-models/guest-js/util.ts index 81981114..7435a267 100644 --- a/crates/yaak-models/guest-js/util.ts +++ b/crates/yaak-models/guest-js/util.ts @@ -1,4 +1,4 @@ -import { ModelStoreData } from './types'; +import { ModelStoreData } from "./types"; export function newStoreData(): ModelStoreData { return { diff --git a/crates/yaak-models/package.json b/crates/yaak-models/package.json index c7258427..a1e2ed76 100644 --- a/crates/yaak-models/package.json +++ b/crates/yaak-models/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/models", - "private": true, "version": "1.0.0", + "private": true, "main": "guest-js/index.ts" } diff --git a/crates/yaak-plugins/index.ts b/crates/yaak-plugins/index.ts index bfe7065d..0b992b79 100644 --- a/crates/yaak-plugins/index.ts +++ b/crates/yaak-plugins/index.ts @@ -1,30 +1,30 @@ -import { invoke } from '@tauri-apps/api/core'; -import { PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse } from './bindings/gen_api'; +import { invoke } from "@tauri-apps/api/core"; +import { PluginNameVersion, PluginSearchResponse, PluginUpdatesResponse } from "./bindings/gen_api"; -export * from './bindings/gen_models'; -export * from './bindings/gen_events'; -export * from './bindings/gen_search'; +export * from "./bindings/gen_models"; +export * from "./bindings/gen_events"; +export * from "./bindings/gen_search"; export async function searchPlugins(query: string) { - return invoke('cmd_plugins_search', { query }); + return invoke("cmd_plugins_search", { query }); } export async function installPlugin(name: string, version: string | null) { - return invoke('cmd_plugins_install', { name, version }); + return invoke("cmd_plugins_install", { name, version }); } export async function uninstallPlugin(pluginId: string) { - return invoke('cmd_plugins_uninstall', { pluginId }); + return invoke("cmd_plugins_uninstall", { pluginId }); } export async function checkPluginUpdates() { - return invoke('cmd_plugins_updates', {}); + return invoke("cmd_plugins_updates", {}); } export async function updateAllPlugins() { - return invoke('cmd_plugins_update_all', {}); + return invoke("cmd_plugins_update_all", {}); } export async function installPluginFromDirectory(directory: string) { - return invoke('cmd_plugins_install_from_directory', { directory }); + return invoke("cmd_plugins_install_from_directory", { directory }); } diff --git a/crates/yaak-plugins/package.json b/crates/yaak-plugins/package.json index e48f823a..25496a15 100644 --- a/crates/yaak-plugins/package.json +++ b/crates/yaak-plugins/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/plugins", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates/yaak-sse/index.ts b/crates/yaak-sse/index.ts index 3b4faf38..f41d82df 100644 --- a/crates/yaak-sse/index.ts +++ b/crates/yaak-sse/index.ts @@ -1 +1 @@ -export * from './bindings/sse'; +export * from "./bindings/sse"; diff --git a/crates/yaak-sse/package.json b/crates/yaak-sse/package.json index 5ca3b1aa..0bce2079 100644 --- a/crates/yaak-sse/package.json +++ b/crates/yaak-sse/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/sse", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates/yaak-sync/index.ts b/crates/yaak-sync/index.ts index 295c03a6..810f3039 100644 --- a/crates/yaak-sync/index.ts +++ b/crates/yaak-sync/index.ts @@ -1,24 +1,24 @@ -import { Channel, invoke } from '@tauri-apps/api/core'; -import { emit } from '@tauri-apps/api/event'; -import type { WatchResult } from '@yaakapp-internal/tauri'; -import { SyncOp } from './bindings/gen_sync'; -import { WatchEvent } from './bindings/gen_watch'; +import { Channel, invoke } from "@tauri-apps/api/core"; +import { emit } from "@tauri-apps/api/event"; +import type { WatchResult } from "@yaakapp-internal/tauri"; +import { SyncOp } from "./bindings/gen_sync"; +import { WatchEvent } from "./bindings/gen_watch"; -export * from './bindings/gen_models'; +export * from "./bindings/gen_models"; export async function calculateSync(workspaceId: string, syncDir: string) { - return invoke('cmd_sync_calculate', { + return invoke("cmd_sync_calculate", { workspaceId, syncDir, }); } export async function calculateSyncFsOnly(dir: string) { - return invoke('cmd_sync_calculate_fs', { dir }); + return invoke("cmd_sync_calculate_fs", { dir }); } export async function applySync(workspaceId: string, syncDir: string, syncOps: SyncOp[]) { - return invoke('cmd_sync_apply', { + return invoke("cmd_sync_apply", { workspaceId, syncDir, syncOps: syncOps, @@ -30,10 +30,10 @@ export function watchWorkspaceFiles( syncDir: string, callback: (e: WatchEvent) => void, ) { - console.log('Watching workspace files', workspaceId, syncDir); + console.log("Watching workspace files", workspaceId, syncDir); const channel = new Channel(); channel.onmessage = callback; - const unlistenPromise = invoke('cmd_sync_watch', { + const unlistenPromise = invoke("cmd_sync_watch", { workspaceId, syncDir, channel, @@ -46,7 +46,7 @@ export function watchWorkspaceFiles( return () => unlistenPromise .then(async ({ unlistenEvent }) => { - console.log('Unwatching workspace files', workspaceId, syncDir); + console.log("Unwatching workspace files", workspaceId, syncDir); unlistenToWatcher(unlistenEvent); }) .catch(console.error); @@ -59,11 +59,11 @@ function unlistenToWatcher(unlistenEvent: string) { } function getWatchKeys() { - return sessionStorage.getItem('workspace-file-watchers')?.split(',').filter(Boolean) ?? []; + return sessionStorage.getItem("workspace-file-watchers")?.split(",").filter(Boolean) ?? []; } function setWatchKeys(keys: string[]) { - sessionStorage.setItem('workspace-file-watchers', keys.join(',')); + sessionStorage.setItem("workspace-file-watchers", keys.join(",")); } function addWatchKey(key: string) { @@ -79,6 +79,6 @@ function removeWatchKey(key: string) { // On page load, unlisten to all zombie watchers const keys = getWatchKeys(); if (keys.length > 0) { - console.log('Unsubscribing to zombie file watchers', keys); + console.log("Unsubscribing to zombie file watchers", keys); keys.forEach(unlistenToWatcher); } diff --git a/crates/yaak-sync/package.json b/crates/yaak-sync/package.json index 79c0b9c9..dbd1b0a1 100644 --- a/crates/yaak-sync/package.json +++ b/crates/yaak-sync/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/sync", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/crates/yaak-templates/build-wasm.cjs b/crates/yaak-templates/build-wasm.cjs index 23611d0d..9bf9eddb 100644 --- a/crates/yaak-templates/build-wasm.cjs +++ b/crates/yaak-templates/build-wasm.cjs @@ -1,18 +1,18 @@ -const { execSync } = require('node:child_process'); -const fs = require('node:fs'); -const path = require('node:path'); +const { execSync } = require("node:child_process"); +const fs = require("node:fs"); +const path = require("node:path"); -if (process.env.SKIP_WASM_BUILD === '1') { - console.log('Skipping wasm-pack build (SKIP_WASM_BUILD=1)'); +if (process.env.SKIP_WASM_BUILD === "1") { + console.log("Skipping wasm-pack build (SKIP_WASM_BUILD=1)"); return; } -execSync('wasm-pack build --target bundler', { stdio: 'inherit' }); +execSync("wasm-pack build --target bundler", { stdio: "inherit" }); // Rewrite the generated entry to use Vite's ?init import style instead of // the ES Module Integration style that wasm-pack generates, which Vite/rolldown // does not support in production builds. -const entry = path.join(__dirname, 'pkg', 'yaak_templates.js'); +const entry = path.join(__dirname, "pkg", "yaak_templates.js"); fs.writeFileSync( entry, [ @@ -20,8 +20,8 @@ fs.writeFileSync( 'export * from "./yaak_templates_bg.js";', 'import * as bg from "./yaak_templates_bg.js";', 'const instance = await init({ "./yaak_templates_bg.js": bg });', - 'bg.__wbg_set_wasm(instance.exports);', - 'instance.exports.__wbindgen_start();', - '', - ].join('\n'), + "bg.__wbg_set_wasm(instance.exports);", + "instance.exports.__wbindgen_start();", + "", + ].join("\n"), ); diff --git a/crates/yaak-templates/index.ts b/crates/yaak-templates/index.ts index 23dd9166..0b8ae3a4 100644 --- a/crates/yaak-templates/index.ts +++ b/crates/yaak-templates/index.ts @@ -1,6 +1,6 @@ -export * from './bindings/parser'; -import { Tokens } from './bindings/parser'; -import { escape_template, parse_template, unescape_template } from './pkg'; +export * from "./bindings/parser"; +import { Tokens } from "./bindings/parser"; +import { escape_template, parse_template, unescape_template } from "./pkg"; export function parseTemplate(template: string) { return parse_template(template) as Tokens; diff --git a/crates/yaak-templates/package.json b/crates/yaak-templates/package.json index 9826bfb6..5e89a677 100644 --- a/crates/yaak-templates/package.json +++ b/crates/yaak-templates/package.json @@ -1,7 +1,7 @@ { "name": "@yaakapp-internal/templates", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts", "scripts": { "bootstrap": "npm run build", diff --git a/crates/yaak-ws/Cargo.toml b/crates/yaak-ws/Cargo.toml index ee15c3ac..c574ae81 100644 --- a/crates/yaak-ws/Cargo.toml +++ b/crates/yaak-ws/Cargo.toml @@ -14,7 +14,10 @@ url = "2" serde_json = { workspace = true } thiserror = { workspace = true } tokio = { workspace = true, features = ["macros", "time", "test-util", "rt"] } -tokio-tungstenite = { version = "0.26.2", default-features = false, features = ["rustls-tls-native-roots", "connect"] } +tokio-tungstenite = { version = "0.26.2", default-features = false, features = [ + "rustls-tls-native-roots", + "connect", +] } yaak-http = { workspace = true } yaak-tls = { workspace = true } yaak-models = { workspace = true } diff --git a/crates/yaak-ws/index.ts b/crates/yaak-ws/index.ts index c9ce0fb7..11721d7c 100644 --- a/crates/yaak-ws/index.ts +++ b/crates/yaak-ws/index.ts @@ -1,8 +1,8 @@ -import { invoke } from '@tauri-apps/api/core'; -import { WebsocketConnection } from '@yaakapp-internal/models'; +import { invoke } from "@tauri-apps/api/core"; +import { WebsocketConnection } from "@yaakapp-internal/models"; export function deleteWebsocketConnections(requestId: string) { - return invoke('cmd_ws_delete_connections', { + return invoke("cmd_ws_delete_connections", { requestId, }); } @@ -16,7 +16,7 @@ export function connectWebsocket({ environmentId: string | null; cookieJarId: string | null; }) { - return invoke('cmd_ws_connect', { + return invoke("cmd_ws_connect", { requestId, environmentId, cookieJarId, @@ -24,7 +24,7 @@ export function connectWebsocket({ } export function closeWebsocket({ connectionId }: { connectionId: string }) { - return invoke('cmd_ws_close', { + return invoke("cmd_ws_close", { connectionId, }); } @@ -36,7 +36,7 @@ export function sendWebsocket({ connectionId: string; environmentId: string | null; }) { - return invoke('cmd_ws_send', { + return invoke("cmd_ws_send", { connectionId, environmentId, }); diff --git a/crates/yaak-ws/package.json b/crates/yaak-ws/package.json index 4956fa73..075582a9 100644 --- a/crates/yaak-ws/package.json +++ b/crates/yaak-ws/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/ws", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/npm/cli-darwin-arm64/package.json b/npm/cli-darwin-arm64/package.json index e8ca680b..4b79e733 100644 --- a/npm/cli-darwin-arm64/package.json +++ b/npm/cli-darwin-arm64/package.json @@ -5,6 +5,10 @@ "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" }, - "os": ["darwin"], - "cpu": ["arm64"] + "os": [ + "darwin" + ], + "cpu": [ + "arm64" + ] } diff --git a/npm/cli-darwin-x64/package.json b/npm/cli-darwin-x64/package.json index a7e34777..8d70f53c 100644 --- a/npm/cli-darwin-x64/package.json +++ b/npm/cli-darwin-x64/package.json @@ -5,6 +5,10 @@ "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" }, - "os": ["darwin"], - "cpu": ["x64"] + "os": [ + "darwin" + ], + "cpu": [ + "x64" + ] } diff --git a/npm/cli-linux-arm64/package.json b/npm/cli-linux-arm64/package.json index 21ed08a8..ae2ead43 100644 --- a/npm/cli-linux-arm64/package.json +++ b/npm/cli-linux-arm64/package.json @@ -5,6 +5,10 @@ "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" }, - "os": ["linux"], - "cpu": ["arm64"] + "os": [ + "linux" + ], + "cpu": [ + "arm64" + ] } diff --git a/npm/cli-linux-x64/package.json b/npm/cli-linux-x64/package.json index 72b7df70..91dce3f3 100644 --- a/npm/cli-linux-x64/package.json +++ b/npm/cli-linux-x64/package.json @@ -5,6 +5,10 @@ "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" }, - "os": ["linux"], - "cpu": ["x64"] + "os": [ + "linux" + ], + "cpu": [ + "x64" + ] } diff --git a/npm/cli-win32-arm64/package.json b/npm/cli-win32-arm64/package.json index fdfe1451..9cef632c 100644 --- a/npm/cli-win32-arm64/package.json +++ b/npm/cli-win32-arm64/package.json @@ -5,6 +5,10 @@ "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" }, - "os": ["win32"], - "cpu": ["arm64"] + "os": [ + "win32" + ], + "cpu": [ + "arm64" + ] } diff --git a/npm/cli-win32-x64/package.json b/npm/cli-win32-x64/package.json index 5f615e82..a13ff425 100644 --- a/npm/cli-win32-x64/package.json +++ b/npm/cli-win32-x64/package.json @@ -5,6 +5,10 @@ "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" }, - "os": ["win32"], - "cpu": ["x64"] + "os": [ + "win32" + ], + "cpu": [ + "x64" + ] } diff --git a/npm/cli/common.js b/npm/cli/common.js index 21794886..8f22e196 100644 --- a/npm/cli/common.js +++ b/npm/cli/common.js @@ -4,7 +4,7 @@ const BINARY_DISTRIBUTION_PACKAGES = { linux_arm64: "@yaakapp/cli-linux-arm64", linux_x64: "@yaakapp/cli-linux-x64", win32_x64: "@yaakapp/cli-win32-x64", - win32_arm64: "@yaakapp/cli-win32-arm64" + win32_arm64: "@yaakapp/cli-win32-arm64", }; const BINARY_DISTRIBUTION_VERSION = require("./package.json").version; @@ -16,5 +16,5 @@ module.exports = { BINARY_DISTRIBUTION_PACKAGES, BINARY_DISTRIBUTION_VERSION, BINARY_NAME, - PLATFORM_SPECIFIC_PACKAGE_NAME + PLATFORM_SPECIFIC_PACKAGE_NAME, }; diff --git a/npm/cli/install.js b/npm/cli/install.js index ef8f1b59..d5f13112 100644 --- a/npm/cli/install.js +++ b/npm/cli/install.js @@ -5,7 +5,7 @@ const https = require("node:https"); const { BINARY_DISTRIBUTION_VERSION, BINARY_NAME, - PLATFORM_SPECIFIC_PACKAGE_NAME + PLATFORM_SPECIFIC_PACKAGE_NAME, } = require("./common"); const fallbackBinaryPath = path.join(__dirname, BINARY_NAME); @@ -27,8 +27,8 @@ function makeRequest(url) { } else { reject( new Error( - `npm responded with status code ${response.statusCode} when downloading package ${url}` - ) + `npm responded with status code ${response.statusCode} when downloading package ${url}`, + ), ); } }) diff --git a/npm/cli/package.json b/npm/cli/package.json index f000e55b..78da3884 100644 --- a/npm/cli/package.json +++ b/npm/cli/package.json @@ -1,25 +1,25 @@ { "name": "@yaakapp/cli", "version": "0.0.1", - "main": "./index.js", "repository": { "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" }, - "scripts": { - "postinstall": "node ./install.js", - "prepublishOnly": "node ./prepublish.js" - }, "bin": { "yaak": "bin/cli.js", "yaakcli": "bin/cli.js" }, + "main": "./index.js", + "scripts": { + "postinstall": "node ./install.js", + "prepublishOnly": "node ./prepublish.js" + }, "optionalDependencies": { - "@yaakapp/cli-darwin-x64": "0.0.1", "@yaakapp/cli-darwin-arm64": "0.0.1", + "@yaakapp/cli-darwin-x64": "0.0.1", "@yaakapp/cli-linux-arm64": "0.0.1", "@yaakapp/cli-linux-x64": "0.0.1", - "@yaakapp/cli-win32-x64": "0.0.1", - "@yaakapp/cli-win32-arm64": "0.0.1" + "@yaakapp/cli-win32-arm64": "0.0.1", + "@yaakapp/cli-win32-x64": "0.0.1" } } diff --git a/npm/prepare-publish.js b/npm/prepare-publish.js index ba15dbe2..f576b9ff 100644 --- a/npm/prepare-publish.js +++ b/npm/prepare-publish.js @@ -14,34 +14,34 @@ const packages = [ "cli-linux-arm64", "cli-linux-x64", "cli-win32-arm64", - "cli-win32-x64" + "cli-win32-x64", ]; const binaries = [ { src: join(__dirname, "dist", "cli-darwin-arm64", "yaak"), - dest: join(__dirname, "cli-darwin-arm64", "bin", "yaak") + dest: join(__dirname, "cli-darwin-arm64", "bin", "yaak"), }, { src: join(__dirname, "dist", "cli-darwin-x64", "yaak"), - dest: join(__dirname, "cli-darwin-x64", "bin", "yaak") + dest: join(__dirname, "cli-darwin-x64", "bin", "yaak"), }, { src: join(__dirname, "dist", "cli-linux-arm64", "yaak"), - dest: join(__dirname, "cli-linux-arm64", "bin", "yaak") + dest: join(__dirname, "cli-linux-arm64", "bin", "yaak"), }, { src: join(__dirname, "dist", "cli-linux-x64", "yaak"), - dest: join(__dirname, "cli-linux-x64", "bin", "yaak") + dest: join(__dirname, "cli-linux-x64", "bin", "yaak"), }, { src: join(__dirname, "dist", "cli-win32-arm64", "yaak.exe"), - dest: join(__dirname, "cli-win32-arm64", "bin", "yaak.exe") + dest: join(__dirname, "cli-win32-arm64", "bin", "yaak.exe"), }, { src: join(__dirname, "dist", "cli-win32-x64", "yaak.exe"), - dest: join(__dirname, "cli-win32-x64", "bin", "yaak.exe") - } + dest: join(__dirname, "cli-win32-x64", "bin", "yaak.exe"), + }, ]; for (const { src, dest } of binaries) { @@ -67,7 +67,7 @@ for (const pkg of packages) { "@yaakapp/cli-linux-arm64": version, "@yaakapp/cli-linux-x64": version, "@yaakapp/cli-win32-x64": version, - "@yaakapp/cli-win32-arm64": version + "@yaakapp/cli-win32-arm64": version, }; } diff --git a/package.json b/package.json index aca27ec0..dc967498 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "yaak-app", - "private": true, "version": "0.0.0", + "private": true, "repository": { "type": "git", "url": "git+https://github.com/mountain-loop/yaak.git" @@ -82,6 +82,7 @@ "vendor:vendor-plugins": "node scripts/vendor-plugins.cjs", "vendor:vendor-protoc": "node scripts/vendor-protoc.cjs", "vendor:vendor-node": "node scripts/vendor-node.cjs", + "format": "vp fmt --ignore-path .oxfmtignore", "lint": "run-p lint:*", "lint:vp": "vp lint", "lint:workspaces": "npm run --workspaces --if-present lint", @@ -90,10 +91,12 @@ "tauri-before-build": "npm run bootstrap", "tauri-before-dev": "node scripts/run-workspaces-dev.mjs" }, - "overrides": { - "js-yaml": "^4.1.1", - "vite": "npm:@voidzero-dev/vite-plus-core@latest", - "vitest": "npm:@voidzero-dev/vite-plus-test@latest" + "dependencies": { + "@codemirror/lang-go": "^6.0.1", + "@codemirror/lang-java": "^6.0.2", + "@codemirror/lang-php": "^6.0.2", + "@codemirror/lang-python": "^6.2.1", + "@codemirror/legacy-modes": "^6.5.2" }, "devDependencies": { "@tauri-apps/cli": "^2.9.6", @@ -105,12 +108,10 @@ "vite-plus": "latest", "vitest": "npm:@voidzero-dev/vite-plus-test@latest" }, - "dependencies": { - "@codemirror/lang-go": "^6.0.1", - "@codemirror/lang-java": "^6.0.2", - "@codemirror/lang-php": "^6.0.2", - "@codemirror/lang-python": "^6.2.1", - "@codemirror/legacy-modes": "^6.5.2" + "overrides": { + "js-yaml": "^4.1.1", + "vite": "npm:@voidzero-dev/vite-plus-core@latest", + "vitest": "npm:@voidzero-dev/vite-plus-test@latest" }, "packageManager": "npm@11.11.1" } diff --git a/packages/common-lib/formatSize.ts b/packages/common-lib/formatSize.ts index c5f4e8e8..7d5ac66e 100644 --- a/packages/common-lib/formatSize.ts +++ b/packages/common-lib/formatSize.ts @@ -4,16 +4,16 @@ export function formatSize(bytes: number): string { if (bytes > 1000 * 1000 * 1000) { num = bytes / 1000 / 1000 / 1000; - unit = 'GB'; + unit = "GB"; } else if (bytes > 1000 * 1000) { num = bytes / 1000 / 1000; - unit = 'MB'; + unit = "MB"; } else if (bytes > 1000) { num = bytes / 1000; - unit = 'KB'; + unit = "KB"; } else { num = bytes; - unit = 'B'; + unit = "B"; } return `${Math.round(num * 10) / 10} ${unit}`; diff --git a/packages/common-lib/index.ts b/packages/common-lib/index.ts index 7297c64e..01983b42 100644 --- a/packages/common-lib/index.ts +++ b/packages/common-lib/index.ts @@ -1,3 +1,3 @@ -export * from './debounce'; -export * from './formatSize'; -export * from './templateFunction'; +export * from "./debounce"; +export * from "./formatSize"; +export * from "./templateFunction"; diff --git a/packages/common-lib/package.json b/packages/common-lib/package.json index 7d497db4..e5b41bf6 100644 --- a/packages/common-lib/package.json +++ b/packages/common-lib/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp-internal/lib", - "private": true, "version": "1.0.0", + "private": true, "main": "index.ts" } diff --git a/packages/common-lib/templateFunction.ts b/packages/common-lib/templateFunction.ts index c4e81123..4132ba25 100644 --- a/packages/common-lib/templateFunction.ts +++ b/packages/common-lib/templateFunction.ts @@ -2,20 +2,20 @@ import type { CallTemplateFunctionArgs, JsonPrimitive, TemplateFunctionArg, -} from '@yaakapp-internal/plugins'; +} from "@yaakapp-internal/plugins"; export function validateTemplateFunctionArgs( fnName: string, args: TemplateFunctionArg[], - values: CallTemplateFunctionArgs['values'], + values: CallTemplateFunctionArgs["values"], ): string | null { for (const arg of args) { - if ('inputs' in arg && arg.inputs) { + if ("inputs" in arg && arg.inputs) { // Recurse down const err = validateTemplateFunctionArgs(fnName, arg.inputs, values); if (err) return err; } - if (!('name' in arg)) continue; + if (!("name" in arg)) continue; if (arg.optional) continue; if (arg.defaultValue != null) continue; if (arg.hidden) continue; @@ -34,14 +34,14 @@ export function applyFormInputDefaults( ) { let newValues: { [p: string]: JsonPrimitive | undefined } = { ...values }; for (const input of inputs) { - if ('defaultValue' in input && values[input.name] === undefined) { + if ("defaultValue" in input && values[input.name] === undefined) { newValues[input.name] = input.defaultValue; } - if (input.type === 'checkbox' && values[input.name] === undefined) { + if (input.type === "checkbox" && values[input.name] === undefined) { newValues[input.name] = false; } // Recurse down to all child inputs - if ('inputs' in input) { + if ("inputs" in input) { newValues = applyFormInputDefaults(input.inputs ?? [], newValues); } } diff --git a/packages/plugin-runtime-types/package.json b/packages/plugin-runtime-types/package.json index d68a2e30..d4dd862d 100644 --- a/packages/plugin-runtime-types/package.json +++ b/packages/plugin-runtime-types/package.json @@ -3,23 +3,23 @@ "version": "0.8.0", "keywords": [ "api-client", - "insomnia-alternative", "bruno-alternative", + "insomnia-alternative", "postman-alternative" ], + "homepage": "https://yaak.app", + "bugs": { + "url": "https://feedback.yaak.app" + }, "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak" }, - "bugs": { - "url": "https://feedback.yaak.app" - }, - "homepage": "https://yaak.app", - "main": "lib/index.js", - "typings": "./lib/index.d.ts", "files": [ "lib/**/*" ], + "main": "lib/index.js", + "typings": "./lib/index.d.ts", "scripts": { "bootstrap": "npm run build", "build": "run-s build:copy-types build:tsc", diff --git a/packages/plugin-runtime-types/src/index.ts b/packages/plugin-runtime-types/src/index.ts index 14313963..8ef2c463 100644 --- a/packages/plugin-runtime-types/src/index.ts +++ b/packages/plugin-runtime-types/src/index.ts @@ -1,9 +1,9 @@ -export type * from './plugins'; -export type * from './themes'; +export type * from "./plugins"; +export type * from "./themes"; -export * from './bindings/gen_models'; -export * from './bindings/gen_events'; +export * from "./bindings/gen_models"; +export * from "./bindings/gen_events"; // Some extras for utility -export type { PartialImportResources } from './plugins/ImporterPlugin'; +export type { PartialImportResources } from "./plugins/ImporterPlugin"; diff --git a/packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts b/packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts index 4102c27d..e6427d3b 100644 --- a/packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts @@ -5,9 +5,9 @@ import type { FormInput, GetHttpAuthenticationSummaryResponse, HttpAuthenticationAction, -} from '../bindings/gen_events'; -import type { MaybePromise } from '../helpers'; -import type { Context } from './Context'; +} from "../bindings/gen_events"; +import type { MaybePromise } from "../helpers"; +import type { Context } from "./Context"; type AddDynamicMethod = { dynamic?: ( @@ -19,13 +19,13 @@ type AddDynamicMethod = { // oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern type AddDynamic = T extends any ? T extends { inputs?: FormInput[] } - ? Omit & { + ? Omit & { inputs: Array>; dynamic?: ( ctx: Context, args: CallHttpAuthenticationActionArgs, ) => MaybePromise< - Partial & { inputs: Array> }> | null | undefined + Partial & { inputs: Array> }> | null | undefined >; } : T & AddDynamicMethod diff --git a/packages/plugin-runtime-types/src/plugins/Context.ts b/packages/plugin-runtime-types/src/plugins/Context.ts index 1e139b6c..0a1afa49 100644 --- a/packages/plugin-runtime-types/src/plugins/Context.ts +++ b/packages/plugin-runtime-types/src/plugins/Context.ts @@ -26,10 +26,10 @@ import type { ShowToastRequest, TemplateRenderRequest, WorkspaceInfo, -} from '../bindings/gen_events.ts'; -import type { Folder, HttpRequest } from '../bindings/gen_models.ts'; -import type { JsonValue } from '../bindings/serde_json/JsonValue'; -import type { MaybePromise } from '../helpers'; +} from "../bindings/gen_events.ts"; +import type { Folder, HttpRequest } from "../bindings/gen_models.ts"; +import type { JsonValue } from "../bindings/serde_json/JsonValue"; +import type { MaybePromise } from "../helpers"; export type CallPromptFormDynamicArgs = { values: { [key in string]?: JsonPrimitive }; @@ -45,13 +45,13 @@ type AddDynamicMethod = { // oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern type AddDynamic = T extends any ? T extends { inputs?: FormInput[] } - ? Omit & { + ? Omit & { inputs: Array>; dynamic?: ( ctx: Context, args: CallPromptFormDynamicArgs, ) => MaybePromise< - Partial & { inputs: Array> }> | null | undefined + Partial & { inputs: Array> }> | null | undefined >; } : T & AddDynamicMethod @@ -59,11 +59,11 @@ type AddDynamic = T extends any export type DynamicPromptFormArg = AddDynamic; -type DynamicPromptFormRequest = Omit & { +type DynamicPromptFormRequest = Omit & { inputs: DynamicPromptFormArg[]; }; -export type WorkspaceHandle = Pick; +export type WorkspaceHandle = Pick; export interface Context { clipboard: { @@ -73,8 +73,8 @@ export interface Context { show(args: ShowToastRequest): Promise; }; prompt: { - text(args: PromptTextRequest): Promise; - form(args: DynamicPromptFormRequest): Promise; + text(args: PromptTextRequest): Promise; + form(args: DynamicPromptFormRequest): Promise; }; store: { set(key: string, value: T): Promise; @@ -94,41 +94,41 @@ export interface Context { openExternalUrl(url: string): Promise; }; cookies: { - listNames(): Promise; - getValue(args: GetCookieValueRequest): Promise; + listNames(): Promise; + getValue(args: GetCookieValueRequest): Promise; }; grpcRequest: { - render(args: RenderGrpcRequestRequest): Promise; + render(args: RenderGrpcRequestRequest): Promise; }; httpRequest: { - send(args: SendHttpRequestRequest): Promise; - getById(args: GetHttpRequestByIdRequest): Promise; - render(args: RenderHttpRequestRequest): Promise; - list(args?: ListHttpRequestsRequest): Promise; + send(args: SendHttpRequestRequest): Promise; + getById(args: GetHttpRequestByIdRequest): Promise; + render(args: RenderHttpRequestRequest): Promise; + list(args?: ListHttpRequestsRequest): Promise; create( - args: Omit, 'id' | 'model' | 'createdAt' | 'updatedAt'> & - Pick, + args: Omit, "id" | "model" | "createdAt" | "updatedAt"> & + Pick, ): Promise; update( - args: Omit, 'model' | 'createdAt' | 'updatedAt'> & - Pick, + args: Omit, "model" | "createdAt" | "updatedAt"> & + Pick, ): Promise; delete(args: { id: string }): Promise; }; folder: { - list(args?: ListFoldersRequest): Promise; + list(args?: ListFoldersRequest): Promise; getById(args: { id: string }): Promise; create( - args: Omit, 'id' | 'model' | 'createdAt' | 'updatedAt'> & - Pick, + args: Omit, "id" | "model" | "createdAt" | "updatedAt"> & + Pick, ): Promise; update( - args: Omit, 'model' | 'createdAt' | 'updatedAt'> & Pick, + args: Omit, "model" | "createdAt" | "updatedAt"> & Pick, ): Promise; delete(args: { id: string }): Promise; }; httpResponse: { - find(args: FindHttpResponsesRequest): Promise; + find(args: FindHttpResponsesRequest): Promise; }; templates: { render(args: TemplateRenderRequest & { data: T }): Promise; diff --git a/packages/plugin-runtime-types/src/plugins/FilterPlugin.ts b/packages/plugin-runtime-types/src/plugins/FilterPlugin.ts index d8a18a80..720d9f74 100644 --- a/packages/plugin-runtime-types/src/plugins/FilterPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/FilterPlugin.ts @@ -1,5 +1,5 @@ -import type { FilterResponse } from '../bindings/gen_events'; -import type { Context } from './Context'; +import type { FilterResponse } from "../bindings/gen_events"; +import type { Context } from "./Context"; export type FilterPlugin = { name: string; diff --git a/packages/plugin-runtime-types/src/plugins/FolderActionPlugin.ts b/packages/plugin-runtime-types/src/plugins/FolderActionPlugin.ts index 59060129..da5ad294 100644 --- a/packages/plugin-runtime-types/src/plugins/FolderActionPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/FolderActionPlugin.ts @@ -1,5 +1,5 @@ -import type { CallFolderActionArgs, FolderAction } from '../bindings/gen_events'; -import type { Context } from './Context'; +import type { CallFolderActionArgs, FolderAction } from "../bindings/gen_events"; +import type { Context } from "./Context"; export type FolderActionPlugin = FolderAction & { onSelect(ctx: Context, args: CallFolderActionArgs): Promise | void; diff --git a/packages/plugin-runtime-types/src/plugins/GrpcRequestActionPlugin.ts b/packages/plugin-runtime-types/src/plugins/GrpcRequestActionPlugin.ts index 881c907f..0defb53d 100644 --- a/packages/plugin-runtime-types/src/plugins/GrpcRequestActionPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/GrpcRequestActionPlugin.ts @@ -1,5 +1,5 @@ -import type { CallGrpcRequestActionArgs, GrpcRequestAction } from '../bindings/gen_events'; -import type { Context } from './Context'; +import type { CallGrpcRequestActionArgs, GrpcRequestAction } from "../bindings/gen_events"; +import type { Context } from "./Context"; export type GrpcRequestActionPlugin = GrpcRequestAction & { onSelect(ctx: Context, args: CallGrpcRequestActionArgs): Promise | void; diff --git a/packages/plugin-runtime-types/src/plugins/HttpRequestActionPlugin.ts b/packages/plugin-runtime-types/src/plugins/HttpRequestActionPlugin.ts index 3d9b7d74..6dd4036f 100644 --- a/packages/plugin-runtime-types/src/plugins/HttpRequestActionPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/HttpRequestActionPlugin.ts @@ -1,5 +1,5 @@ -import type { CallHttpRequestActionArgs, HttpRequestAction } from '../bindings/gen_events'; -import type { Context } from './Context'; +import type { CallHttpRequestActionArgs, HttpRequestAction } from "../bindings/gen_events"; +import type { Context } from "./Context"; export type HttpRequestActionPlugin = HttpRequestAction & { onSelect(ctx: Context, args: CallHttpRequestActionArgs): Promise | void; diff --git a/packages/plugin-runtime-types/src/plugins/ImporterPlugin.ts b/packages/plugin-runtime-types/src/plugins/ImporterPlugin.ts index 52ecca11..0d42c4cc 100644 --- a/packages/plugin-runtime-types/src/plugins/ImporterPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/ImporterPlugin.ts @@ -1,17 +1,17 @@ -import type { ImportResources } from '../bindings/gen_events'; -import type { AtLeast, MaybePromise } from '../helpers'; -import type { Context } from './Context'; +import type { ImportResources } from "../bindings/gen_events"; +import type { AtLeast, MaybePromise } from "../helpers"; +import type { Context } from "./Context"; -type RootFields = 'name' | 'id' | 'model'; -type CommonFields = RootFields | 'workspaceId'; +type RootFields = "name" | "id" | "model"; +type CommonFields = RootFields | "workspaceId"; export type PartialImportResources = { - workspaces: Array>; - environments: Array>; - folders: Array>; - httpRequests: Array>; - grpcRequests: Array>; - websocketRequests: Array>; + workspaces: Array>; + environments: Array>; + folders: Array>; + httpRequests: Array>; + grpcRequests: Array>; + websocketRequests: Array>; }; export type ImportPluginResponse = null | { diff --git a/packages/plugin-runtime-types/src/plugins/TemplateFunctionPlugin.ts b/packages/plugin-runtime-types/src/plugins/TemplateFunctionPlugin.ts index 747c4b7d..2a6bd1e4 100644 --- a/packages/plugin-runtime-types/src/plugins/TemplateFunctionPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/TemplateFunctionPlugin.ts @@ -1,6 +1,6 @@ -import type { CallTemplateFunctionArgs, FormInput, TemplateFunction } from '../bindings/gen_events'; -import type { MaybePromise } from '../helpers'; -import type { Context } from './Context'; +import type { CallTemplateFunctionArgs, FormInput, TemplateFunction } from "../bindings/gen_events"; +import type { MaybePromise } from "../helpers"; +import type { Context } from "./Context"; type AddDynamicMethod = { dynamic?: ( @@ -12,13 +12,13 @@ type AddDynamicMethod = { // oxlint-disable-next-line no-explicit-any -- distributive conditional type pattern type AddDynamic = T extends any ? T extends { inputs?: FormInput[] } - ? Omit & { + ? Omit & { inputs: Array>; dynamic?: ( ctx: Context, args: CallTemplateFunctionArgs, ) => MaybePromise< - Partial & { inputs: Array> }> | null | undefined + Partial & { inputs: Array> }> | null | undefined >; } : T & AddDynamicMethod @@ -26,7 +26,7 @@ type AddDynamic = T extends any export type DynamicTemplateFunctionArg = AddDynamic; -export type TemplateFunctionPlugin = Omit & { +export type TemplateFunctionPlugin = Omit & { args: DynamicTemplateFunctionArg[]; onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise; }; diff --git a/packages/plugin-runtime-types/src/plugins/ThemePlugin.ts b/packages/plugin-runtime-types/src/plugins/ThemePlugin.ts index 3cbc2124..7563470d 100644 --- a/packages/plugin-runtime-types/src/plugins/ThemePlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/ThemePlugin.ts @@ -1,3 +1,3 @@ -import type { Theme } from '../bindings/gen_events'; +import type { Theme } from "../bindings/gen_events"; export type ThemePlugin = Theme; diff --git a/packages/plugin-runtime-types/src/plugins/WebsocketRequestActionPlugin.ts b/packages/plugin-runtime-types/src/plugins/WebsocketRequestActionPlugin.ts index da566744..94a497dc 100644 --- a/packages/plugin-runtime-types/src/plugins/WebsocketRequestActionPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/WebsocketRequestActionPlugin.ts @@ -1,8 +1,8 @@ import type { CallWebsocketRequestActionArgs, WebsocketRequestAction, -} from '../bindings/gen_events'; -import type { Context } from './Context'; +} from "../bindings/gen_events"; +import type { Context } from "./Context"; export type WebsocketRequestActionPlugin = WebsocketRequestAction & { onSelect(ctx: Context, args: CallWebsocketRequestActionArgs): Promise | void; diff --git a/packages/plugin-runtime-types/src/plugins/WorkspaceActionPlugin.ts b/packages/plugin-runtime-types/src/plugins/WorkspaceActionPlugin.ts index 12707e4f..6c61edc2 100644 --- a/packages/plugin-runtime-types/src/plugins/WorkspaceActionPlugin.ts +++ b/packages/plugin-runtime-types/src/plugins/WorkspaceActionPlugin.ts @@ -1,5 +1,5 @@ -import type { CallWorkspaceActionArgs, WorkspaceAction } from '../bindings/gen_events'; -import type { Context } from './Context'; +import type { CallWorkspaceActionArgs, WorkspaceAction } from "../bindings/gen_events"; +import type { Context } from "./Context"; export type WorkspaceActionPlugin = WorkspaceAction & { onSelect(ctx: Context, args: CallWorkspaceActionArgs): Promise | void; diff --git a/packages/plugin-runtime-types/src/plugins/index.ts b/packages/plugin-runtime-types/src/plugins/index.ts index 94abd4e3..86c2b67d 100644 --- a/packages/plugin-runtime-types/src/plugins/index.ts +++ b/packages/plugin-runtime-types/src/plugins/index.ts @@ -1,23 +1,23 @@ -import type { AuthenticationPlugin } from './AuthenticationPlugin'; +import type { AuthenticationPlugin } from "./AuthenticationPlugin"; -import type { Context } from './Context'; -import type { FilterPlugin } from './FilterPlugin'; -import type { FolderActionPlugin } from './FolderActionPlugin'; -import type { GrpcRequestActionPlugin } from './GrpcRequestActionPlugin'; -import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin'; -import type { ImporterPlugin } from './ImporterPlugin'; -import type { TemplateFunctionPlugin } from './TemplateFunctionPlugin'; -import type { ThemePlugin } from './ThemePlugin'; -import type { WebsocketRequestActionPlugin } from './WebsocketRequestActionPlugin'; -import type { WorkspaceActionPlugin } from './WorkspaceActionPlugin'; +import type { Context } from "./Context"; +import type { FilterPlugin } from "./FilterPlugin"; +import type { FolderActionPlugin } from "./FolderActionPlugin"; +import type { GrpcRequestActionPlugin } from "./GrpcRequestActionPlugin"; +import type { HttpRequestActionPlugin } from "./HttpRequestActionPlugin"; +import type { ImporterPlugin } from "./ImporterPlugin"; +import type { TemplateFunctionPlugin } from "./TemplateFunctionPlugin"; +import type { ThemePlugin } from "./ThemePlugin"; +import type { WebsocketRequestActionPlugin } from "./WebsocketRequestActionPlugin"; +import type { WorkspaceActionPlugin } from "./WorkspaceActionPlugin"; export type { Context }; -export type { DynamicAuthenticationArg } from './AuthenticationPlugin'; -export type { CallPromptFormDynamicArgs, DynamicPromptFormArg } from './Context'; -export type { DynamicTemplateFunctionArg } from './TemplateFunctionPlugin'; +export type { DynamicAuthenticationArg } from "./AuthenticationPlugin"; +export type { CallPromptFormDynamicArgs, DynamicPromptFormArg } from "./Context"; +export type { DynamicTemplateFunctionArg } from "./TemplateFunctionPlugin"; export type { TemplateFunctionPlugin }; -export type { FolderActionPlugin } from './FolderActionPlugin'; -export type { WorkspaceActionPlugin } from './WorkspaceActionPlugin'; +export type { FolderActionPlugin } from "./FolderActionPlugin"; +export type { WorkspaceActionPlugin } from "./WorkspaceActionPlugin"; /** * The global structure of a Yaak plugin diff --git a/packages/plugin-runtime/src/EventChannel.ts b/packages/plugin-runtime/src/EventChannel.ts index b80802e8..fa0e7697 100644 --- a/packages/plugin-runtime/src/EventChannel.ts +++ b/packages/plugin-runtime/src/EventChannel.ts @@ -1,4 +1,4 @@ -import type { InternalEvent } from '@yaakapp/api'; +import type { InternalEvent } from "@yaakapp/api"; export class EventChannel { #listeners = new Set<(event: InternalEvent) => void>(); diff --git a/packages/plugin-runtime/src/PluginHandle.ts b/packages/plugin-runtime/src/PluginHandle.ts index a297596e..c3eba209 100644 --- a/packages/plugin-runtime/src/PluginHandle.ts +++ b/packages/plugin-runtime/src/PluginHandle.ts @@ -1,7 +1,7 @@ -import type { BootRequest, InternalEvent } from '@yaakapp/api'; -import type { PluginContext } from '@yaakapp-internal/plugins'; -import type { EventChannel } from './EventChannel'; -import { PluginInstance, type PluginWorkerData } from './PluginInstance'; +import type { BootRequest, InternalEvent } from "@yaakapp/api"; +import type { PluginContext } from "@yaakapp-internal/plugins"; +import type { EventChannel } from "./EventChannel"; +import { PluginInstance, type PluginWorkerData } from "./PluginInstance"; export class PluginHandle { #instance: PluginInstance; diff --git a/packages/plugin-runtime/src/PluginInstance.ts b/packages/plugin-runtime/src/PluginInstance.ts index d8459a20..031a6d0f 100644 --- a/packages/plugin-runtime/src/PluginInstance.ts +++ b/packages/plugin-runtime/src/PluginInstance.ts @@ -1,16 +1,16 @@ -import console from 'node:console'; -import { type Stats, statSync, watch } from 'node:fs'; -import path from 'node:path'; +import console from "node:console"; +import { type Stats, statSync, watch } from "node:fs"; +import path from "node:path"; import type { CallPromptFormDynamicArgs, Context, DynamicPromptFormArg, PluginDefinition, -} from '@yaakapp/api'; +} from "@yaakapp/api"; import { applyFormInputDefaults, validateTemplateFunctionArgs, -} from '@yaakapp-internal/lib/templateFunction'; +} from "@yaakapp-internal/lib/templateFunction"; import type { BootRequest, DeleteKeyValueResponse, @@ -45,10 +45,10 @@ import type { TemplateRenderResponse, UpsertModelResponse, WindowInfoResponse, -} from '@yaakapp-internal/plugins'; -import { applyDynamicFormInput } from './common'; -import { EventChannel } from './EventChannel'; -import { migrateTemplateFunctionSelectOptions } from './migrations'; +} from "@yaakapp-internal/plugins"; +import { applyDynamicFormInput } from "./common"; +import { EventChannel } from "./EventChannel"; +import { migrateTemplateFunctionSelectOptions } from "./migrations"; export interface PluginWorkerData { bootRequest: BootRequest; @@ -84,16 +84,16 @@ export class PluginInstance { this.#sendPayload( workerData.context, { - type: 'reload_response', + type: "reload_response", silent: false, }, null, ); } catch (err: unknown) { await ctx.toast.show({ - message: `Failed to initialize plugin ${this.#workerData.bootRequest.dir.split('/').pop()}: ${err instanceof Error ? err.message : String(err)}`, - color: 'notice', - icon: 'alert_triangle', + message: `Failed to initialize plugin ${this.#workerData.bootRequest.dir.split("/").pop()}: ${err instanceof Error ? err.message : String(err)}`, + color: "notice", + icon: "alert_triangle", timeout: 30000, }); } @@ -123,15 +123,15 @@ export class PluginInstance { const { context, payload, id: replyId } = event; try { - if (payload.type === 'boot_request') { + if (payload.type === "boot_request") { await this.#mod?.init?.(ctx); - this.#sendPayload(context, { type: 'boot_response' }, replyId); + this.#sendPayload(context, { type: "boot_response" }, replyId); return; } - if (payload.type === 'terminate_request') { + if (payload.type === "terminate_request") { const payload: InternalEventPayload = { - type: 'terminate_response', + type: "terminate_response", }; await this.terminate(); this.#sendPayload(context, payload, replyId); @@ -139,15 +139,15 @@ export class PluginInstance { } if ( - payload.type === 'import_request' && - typeof this.#mod?.importer?.onImport === 'function' + payload.type === "import_request" && + typeof this.#mod?.importer?.onImport === "function" ) { const reply = await this.#mod.importer.onImport(ctx, { text: payload.content, }); if (reply != null) { const replyPayload: InternalEventPayload = { - type: 'import_response', + type: "import_response", resources: reply.resources as ImportResources, }; this.#sendPayload(context, replyPayload, replyId); @@ -157,18 +157,18 @@ export class PluginInstance { } } - if (payload.type === 'filter_request' && typeof this.#mod?.filter?.onFilter === 'function') { + if (payload.type === "filter_request" && typeof this.#mod?.filter?.onFilter === "function") { const reply = await this.#mod.filter.onFilter(ctx, { filter: payload.filter, payload: payload.content, mimeType: payload.type, }); - this.#sendPayload(context, { type: 'filter_response', ...reply }, replyId); + this.#sendPayload(context, { type: "filter_response", ...reply }, replyId); return; } if ( - payload.type === 'get_grpc_request_actions_request' && + payload.type === "get_grpc_request_actions_request" && Array.isArray(this.#mod?.grpcRequestActions) ) { const reply: GrpcRequestAction[] = this.#mod.grpcRequestActions.map((a) => ({ @@ -177,7 +177,7 @@ export class PluginInstance { onSelect: undefined, })); const replyPayload: InternalEventPayload = { - type: 'get_grpc_request_actions_response', + type: "get_grpc_request_actions_response", pluginRefId: this.#workerData.pluginRefId, actions: reply, }; @@ -186,7 +186,7 @@ export class PluginInstance { } if ( - payload.type === 'get_http_request_actions_request' && + payload.type === "get_http_request_actions_request" && Array.isArray(this.#mod?.httpRequestActions) ) { const reply: HttpRequestAction[] = this.#mod.httpRequestActions.map((a) => ({ @@ -195,7 +195,7 @@ export class PluginInstance { onSelect: undefined, })); const replyPayload: InternalEventPayload = { - type: 'get_http_request_actions_response', + type: "get_http_request_actions_response", pluginRefId: this.#workerData.pluginRefId, actions: reply, }; @@ -204,7 +204,7 @@ export class PluginInstance { } if ( - payload.type === 'get_websocket_request_actions_request' && + payload.type === "get_websocket_request_actions_request" && Array.isArray(this.#mod?.websocketRequestActions) ) { const reply = this.#mod.websocketRequestActions.map((a) => ({ @@ -212,7 +212,7 @@ export class PluginInstance { onSelect: undefined, })); const replyPayload: InternalEventPayload = { - type: 'get_websocket_request_actions_response', + type: "get_websocket_request_actions_response", pluginRefId: this.#workerData.pluginRefId, actions: reply, }; @@ -221,7 +221,7 @@ export class PluginInstance { } if ( - payload.type === 'get_workspace_actions_request' && + payload.type === "get_workspace_actions_request" && Array.isArray(this.#mod?.workspaceActions) ) { const reply = this.#mod.workspaceActions.map((a) => ({ @@ -229,7 +229,7 @@ export class PluginInstance { onSelect: undefined, })); const replyPayload: InternalEventPayload = { - type: 'get_workspace_actions_response', + type: "get_workspace_actions_response", pluginRefId: this.#workerData.pluginRefId, actions: reply, }; @@ -238,7 +238,7 @@ export class PluginInstance { } if ( - payload.type === 'get_folder_actions_request' && + payload.type === "get_folder_actions_request" && Array.isArray(this.#mod?.folderActions) ) { const reply = this.#mod.folderActions.map((a) => ({ @@ -246,7 +246,7 @@ export class PluginInstance { onSelect: undefined, })); const replyPayload: InternalEventPayload = { - type: 'get_folder_actions_response', + type: "get_folder_actions_response", pluginRefId: this.#workerData.pluginRefId, actions: reply, }; @@ -254,9 +254,9 @@ export class PluginInstance { return; } - if (payload.type === 'get_themes_request' && Array.isArray(this.#mod?.themes)) { + if (payload.type === "get_themes_request" && Array.isArray(this.#mod?.themes)) { const replyPayload: InternalEventPayload = { - type: 'get_themes_response', + type: "get_themes_response", themes: this.#mod.themes, }; this.#sendPayload(context, replyPayload, replyId); @@ -264,7 +264,7 @@ export class PluginInstance { } if ( - payload.type === 'get_template_function_summary_request' && + payload.type === "get_template_function_summary_request" && Array.isArray(this.#mod?.templateFunctions) ) { const functions: TemplateFunction[] = this.#mod.templateFunctions.map( @@ -277,7 +277,7 @@ export class PluginInstance { }, ); const replyPayload: InternalEventPayload = { - type: 'get_template_function_summary_response', + type: "get_template_function_summary_response", pluginRefId: this.#workerData.pluginRefId, functions, }; @@ -286,7 +286,7 @@ export class PluginInstance { } if ( - payload.type === 'get_template_function_config_request' && + payload.type === "get_template_function_config_request" && Array.isArray(this.#mod?.templateFunctions) ) { const templateFunction = this.#mod.templateFunctions.find((f) => f.name === payload.name); @@ -301,11 +301,11 @@ export class PluginInstance { }; payload.values = applyFormInputDefaults(fn.args, payload.values); - const p = { ...payload, purpose: 'preview' } as const; + const p = { ...payload, purpose: "preview" } as const; const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, p); const replyPayload: InternalEventPayload = { - type: 'get_template_function_config_response', + type: "get_template_function_config_response", pluginRefId: this.#workerData.pluginRefId, function: { ...fn, args: stripDynamicCallbacks(resolvedArgs) }, }; @@ -313,9 +313,9 @@ export class PluginInstance { return; } - if (payload.type === 'get_http_authentication_summary_request' && this.#mod?.authentication) { + if (payload.type === "get_http_authentication_summary_request" && this.#mod?.authentication) { const replyPayload: InternalEventPayload = { - type: 'get_http_authentication_summary_response', + type: "get_http_authentication_summary_response", ...this.#mod.authentication, }; @@ -323,7 +323,7 @@ export class PluginInstance { return; } - if (payload.type === 'get_http_authentication_config_request' && this.#mod?.authentication) { + if (payload.type === "get_http_authentication_config_request" && this.#mod?.authentication) { const { args, actions } = this.#mod.authentication; payload.values = applyFormInputDefaults(args, payload.values); const resolvedArgs = await applyDynamicFormInput(ctx, args, payload); @@ -334,7 +334,7 @@ export class PluginInstance { } const replyPayload: InternalEventPayload = { - type: 'get_http_authentication_config_response', + type: "get_http_authentication_config_response", args: stripDynamicCallbacks(resolvedArgs), actions: resolvedActions, pluginRefId: this.#workerData.pluginRefId, @@ -344,15 +344,15 @@ export class PluginInstance { return; } - if (payload.type === 'call_http_authentication_request' && this.#mod?.authentication) { + if (payload.type === "call_http_authentication_request" && this.#mod?.authentication) { const auth = this.#mod.authentication; - if (typeof auth?.onApply === 'function') { + if (typeof auth?.onApply === "function") { const resolvedArgs = await applyDynamicFormInput(ctx, auth.args, payload); payload.values = applyFormInputDefaults(resolvedArgs, payload.values); this.#sendPayload( context, { - type: 'call_http_authentication_response', + type: "call_http_authentication_response", ...(await auth.onApply(ctx, payload)), }, replyId, @@ -362,11 +362,11 @@ export class PluginInstance { } if ( - payload.type === 'call_http_authentication_action_request' && + payload.type === "call_http_authentication_action_request" && this.#mod.authentication != null ) { const action = this.#mod.authentication.actions?.[payload.index]; - if (typeof action?.onSelect === 'function') { + if (typeof action?.onSelect === "function") { await action.onSelect(ctx, payload.args); this.#sendEmpty(context, replyId); return; @@ -374,11 +374,11 @@ export class PluginInstance { } if ( - payload.type === 'call_http_request_action_request' && + payload.type === "call_http_request_action_request" && Array.isArray(this.#mod.httpRequestActions) ) { const action = this.#mod.httpRequestActions[payload.index]; - if (typeof action?.onSelect === 'function') { + if (typeof action?.onSelect === "function") { await action.onSelect(ctx, payload.args); this.#sendEmpty(context, replyId); return; @@ -386,11 +386,11 @@ export class PluginInstance { } if ( - payload.type === 'call_websocket_request_action_request' && + payload.type === "call_websocket_request_action_request" && Array.isArray(this.#mod.websocketRequestActions) ) { const action = this.#mod.websocketRequestActions[payload.index]; - if (typeof action?.onSelect === 'function') { + if (typeof action?.onSelect === "function") { await action.onSelect(ctx, payload.args); this.#sendEmpty(context, replyId); return; @@ -398,20 +398,20 @@ export class PluginInstance { } if ( - payload.type === 'call_workspace_action_request' && + payload.type === "call_workspace_action_request" && Array.isArray(this.#mod.workspaceActions) ) { const action = this.#mod.workspaceActions[payload.index]; - if (typeof action?.onSelect === 'function') { + if (typeof action?.onSelect === "function") { await action.onSelect(ctx, payload.args); this.#sendEmpty(context, replyId); return; } } - if (payload.type === 'call_folder_action_request' && Array.isArray(this.#mod.folderActions)) { + if (payload.type === "call_folder_action_request" && Array.isArray(this.#mod.folderActions)) { const action = this.#mod.folderActions[payload.index]; - if (typeof action?.onSelect === 'function') { + if (typeof action?.onSelect === "function") { await action.onSelect(ctx, payload.args); this.#sendEmpty(context, replyId); return; @@ -419,11 +419,11 @@ export class PluginInstance { } if ( - payload.type === 'call_grpc_request_action_request' && + payload.type === "call_grpc_request_action_request" && Array.isArray(this.#mod.grpcRequestActions) ) { const action = this.#mod.grpcRequestActions[payload.index]; - if (typeof action?.onSelect === 'function') { + if (typeof action?.onSelect === "function") { await action.onSelect(ctx, payload.args); this.#sendEmpty(context, replyId); return; @@ -431,32 +431,32 @@ export class PluginInstance { } if ( - payload.type === 'call_template_function_request' && + payload.type === "call_template_function_request" && Array.isArray(this.#mod?.templateFunctions) ) { const fn = this.#mod.templateFunctions.find((a) => a.name === payload.name); if ( - payload.args.purpose === 'preview' && - (fn?.previewType === 'click' || fn?.previewType === 'none') + payload.args.purpose === "preview" && + (fn?.previewType === "click" || fn?.previewType === "none") ) { // Send empty render response this.#sendPayload( context, { - type: 'call_template_function_response', + type: "call_template_function_response", value: null, - error: 'Live preview disabled for this function', + error: "Live preview disabled for this function", }, replyId, ); - } else if (typeof fn?.onRender === 'function') { + } else if (typeof fn?.onRender === "function") { const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, payload.args); const values = applyFormInputDefaults(resolvedArgs, payload.args.values); const error = validateTemplateFunctionArgs(fn.name, resolvedArgs, values); - if (error && payload.args.purpose !== 'preview') { + if (error && payload.args.purpose !== "preview") { this.#sendPayload( context, - { type: 'call_template_function_response', value: null, error }, + { type: "call_template_function_response", value: null, error }, replyId, ); return; @@ -466,16 +466,19 @@ export class PluginInstance { const result = await fn.onRender(ctx, { ...payload.args, values }); this.#sendPayload( context, - { type: 'call_template_function_response', value: result ?? null }, + { type: "call_template_function_response", value: result ?? null }, replyId, ); } catch (err) { this.#sendPayload( context, { - type: 'call_template_function_response', + type: "call_template_function_response", value: null, - error: (err instanceof Error ? err.message : String(err)).replace(/^Error:\s*/g, ''), + error: (err instanceof Error ? err.message : String(err)).replace( + /^Error:\s*/g, + "", + ), }, replyId, ); @@ -484,9 +487,9 @@ export class PluginInstance { } } } catch (err) { - const error = (err instanceof Error ? err.message : String(err)).replace(/^Error:\s*/g, ''); - console.log('Plugin call threw exception', payload.type, '→', error); - this.#sendPayload(context, { type: 'error_response', error }, replyId); + const error = (err instanceof Error ? err.message : String(err)).replace(/^Error:\s*/g, ""); + console.log("Plugin call threw exception", payload.type, "→", error); + this.#sendPayload(context, { type: "error_response", error }, replyId); return; } @@ -495,11 +498,11 @@ export class PluginInstance { } #pathMod() { - return path.posix.join(this.#workerData.bootRequest.dir, 'build', 'index.js'); + return path.posix.join(this.#workerData.bootRequest.dir, "build", "index.js"); } #pathPkg() { - return path.join(this.#workerData.bootRequest.dir, 'package.json'); + return path.join(this.#workerData.bootRequest.dir, "package.json"); } #unimportModule() { @@ -546,10 +549,10 @@ export class PluginInstance { } #sendEmpty(context: PluginContext, replyId: string | null = null): string { - return this.#sendPayload(context, { type: 'empty_response' }, replyId); + return this.#sendPayload(context, { type: "empty_response" }, replyId); } - #sendForReply>( + #sendForReply>( context: PluginContext, payload: InternalEventPayload, ): Promise { @@ -600,7 +603,7 @@ export class PluginInstance { throw new Error("Can't get window context without an active window"); } const payload: InternalEventPayload = { - type: 'window_info_request', + type: "window_info_request", label: context.label, }; @@ -611,7 +614,7 @@ export class PluginInstance { clipboard: { copyText: async (text) => { await this.#sendForReply(context, { - type: 'copy_text_request', + type: "copy_text_request", text, }); }, @@ -619,7 +622,7 @@ export class PluginInstance { toast: { show: async (args) => { await this.#sendForReply(context, { - type: 'show_toast_request', + type: "show_toast_request", // Handle default here because null/undefined both convert to None in Rust translation timeout: args.timeout === undefined ? 5000 : args.timeout, ...args, @@ -638,11 +641,11 @@ export class PluginInstance { }, openUrl: async ({ onNavigate, onClose, ...args }) => { args.label = args.label || `${Math.random()}`; - const payload: InternalEventPayload = { type: 'open_window_request', ...args }; + const payload: InternalEventPayload = { type: "open_window_request", ...args }; const onEvent = (event: InternalEventPayload) => { - if (event.type === 'window_navigate_event') { + if (event.type === "window_navigate_event") { onNavigate?.(event); - } else if (event.type === 'window_close_event') { + } else if (event.type === "window_close_event") { onClose?.(); } }; @@ -650,7 +653,7 @@ export class PluginInstance { return { close: () => { const closePayload: InternalEventPayload = { - type: 'close_window_request', + type: "close_window_request", label: args.label, }; this.#sendPayload(context, closePayload, null); @@ -659,7 +662,7 @@ export class PluginInstance { }, openExternalUrl: async (url) => { await this.#sendForReply(context, { - type: 'open_external_url_request', + type: "open_external_url_request", url, }); }, @@ -667,7 +670,7 @@ export class PluginInstance { prompt: { text: async (args) => { const reply: PromptTextResponse = await this.#sendForReply(context, { - type: 'prompt_text_request', + type: "prompt_text_request", ...args, }); return reply.value; @@ -686,7 +689,7 @@ export class PluginInstance { // Build the event manually so we can get the event ID for keying const eventToSend = this.#buildEventToSend( context, - { type: 'prompt_form_request', ...args, inputs: strippedInputs }, + { type: "prompt_form_request", ...args, inputs: strippedInputs }, null, ); @@ -697,7 +700,7 @@ export class PluginInstance { const cb = (event: InternalEvent) => { if (event.replyId !== eventToSend.id) return; - if (event.payload.type === 'prompt_form_response') { + if (event.payload.type === "prompt_form_response") { const { done, values } = event.payload as PromptFormResponse; if (done) { // Final response — resolve the promise and clean up @@ -716,12 +719,12 @@ export class PluginInstance { const stripped = stripDynamicCallbacks(resolvedInputs); this.#sendPayload( context, - { type: 'prompt_form_request', ...args, inputs: stripped }, + { type: "prompt_form_request", ...args, inputs: stripped }, eventToSend.id, ); }) .catch((err) => { - console.error('Failed to resolve dynamic form inputs', err); + console.error("Failed to resolve dynamic form inputs", err); }); } } @@ -739,7 +742,7 @@ export class PluginInstance { httpResponse: { find: async (args) => { const payload = { - type: 'find_http_responses_request', + type: "find_http_responses_request", ...args, } as const; const { httpResponses } = await this.#sendForReply( @@ -752,7 +755,7 @@ export class PluginInstance { grpcRequest: { render: async (args) => { const payload = { - type: 'render_grpc_request_request', + type: "render_grpc_request_request", ...args, } as const; const { grpcRequest } = await this.#sendForReply( @@ -765,7 +768,7 @@ export class PluginInstance { httpRequest: { getById: async (args) => { const payload = { - type: 'get_http_request_by_id_request', + type: "get_http_request_by_id_request", ...args, } as const; const { httpRequest } = await this.#sendForReply( @@ -776,7 +779,7 @@ export class PluginInstance { }, send: async (args) => { const payload = { - type: 'send_http_request_request', + type: "send_http_request_request", ...args, } as const; const { httpResponse } = await this.#sendForReply( @@ -787,7 +790,7 @@ export class PluginInstance { }, render: async (args) => { const payload = { - type: 'render_http_request_request', + type: "render_http_request_request", ...args, } as const; const { httpRequest } = await this.#sendForReply( @@ -798,9 +801,9 @@ export class PluginInstance { }, list: async (args?: { folderId?: string }) => { const payload: InternalEventPayload = { - type: 'list_http_requests_request', + type: "list_http_requests_request", folderId: args?.folderId, - } satisfies ListHttpRequestsRequest & { type: 'list_http_requests_request' }; + } satisfies ListHttpRequestsRequest & { type: "list_http_requests_request" }; const { httpRequests } = await this.#sendForReply( context, payload, @@ -809,13 +812,13 @@ export class PluginInstance { }, create: async (args) => { const payload = { - type: 'upsert_model_request', + type: "upsert_model_request", model: { - name: '', - method: 'GET', + name: "", + method: "GET", ...args, - id: '', - model: 'http_request', + id: "", + model: "http_request", }, } as InternalEventPayload; const response = await this.#sendForReply(context, payload); @@ -823,9 +826,9 @@ export class PluginInstance { }, update: async (args) => { const payload = { - type: 'upsert_model_request', + type: "upsert_model_request", model: { - model: 'http_request', + model: "http_request", ...args, }, } as InternalEventPayload; @@ -834,8 +837,8 @@ export class PluginInstance { }, delete: async (args) => { const payload = { - type: 'delete_model_request', - model: 'http_request', + type: "delete_model_request", + model: "http_request", id: args.id, } as InternalEventPayload; const response = await this.#sendForReply(context, payload); @@ -844,23 +847,23 @@ export class PluginInstance { }, folder: { list: async () => { - const payload = { type: 'list_folders_request' } as const; + const payload = { type: "list_folders_request" } as const; const { folders } = await this.#sendForReply(context, payload); return folders; }, getById: async (args: { id: string }) => { - const payload = { type: 'list_folders_request' } as const; + const payload = { type: "list_folders_request" } as const; const { folders } = await this.#sendForReply(context, payload); return folders.find((f) => f.id === args.id) ?? null; }, create: async ({ name, ...args }) => { const payload = { - type: 'upsert_model_request', + type: "upsert_model_request", model: { ...args, - name: name ?? '', - id: '', - model: 'folder', + name: name ?? "", + id: "", + model: "folder", }, } as InternalEventPayload; const response = await this.#sendForReply(context, payload); @@ -868,9 +871,9 @@ export class PluginInstance { }, update: async (args) => { const payload = { - type: 'upsert_model_request', + type: "upsert_model_request", model: { - model: 'folder', + model: "folder", ...args, }, } as InternalEventPayload; @@ -879,8 +882,8 @@ export class PluginInstance { }, delete: async (args: { id: string }) => { const payload = { - type: 'delete_model_request', - model: 'folder', + type: "delete_model_request", + model: "folder", id: args.id, } as InternalEventPayload; const response = await this.#sendForReply(context, payload); @@ -890,14 +893,14 @@ export class PluginInstance { cookies: { getValue: async (args: GetCookieValueRequest) => { const payload = { - type: 'get_cookie_value_request', + type: "get_cookie_value_request", ...args, } as const; const { value } = await this.#sendForReply(context, payload); return value; }, listNames: async () => { - const payload = { type: 'list_cookie_names_request' } as const; + const payload = { type: "list_cookie_names_request" } as const; const { names } = await this.#sendForReply(context, payload); return names; }, @@ -908,7 +911,7 @@ export class PluginInstance { * (eg. object), it will be recursively rendered. */ render: async (args: TemplateRenderRequest) => { - const payload = { type: 'template_render_request', ...args } as const; + const payload = { type: "template_render_request", ...args } as const; const result = await this.#sendForReply(context, payload); // oxlint-disable-next-line no-explicit-any -- That's okay return result.data as any; @@ -916,34 +919,34 @@ export class PluginInstance { }, store: { get: async (key: string) => { - const payload = { type: 'get_key_value_request', key } as const; + const payload = { type: "get_key_value_request", key } as const; const result = await this.#sendForReply(context, payload); return result.value ? (JSON.parse(result.value) as T) : undefined; }, set: async (key: string, value: T) => { const valueStr = JSON.stringify(value); const payload: InternalEventPayload = { - type: 'set_key_value_request', + type: "set_key_value_request", key, value: valueStr, }; await this.#sendForReply(context, payload); }, delete: async (key: string) => { - const payload = { type: 'delete_key_value_request', key } as const; + const payload = { type: "delete_key_value_request", key } as const; const result = await this.#sendForReply(context, payload); return result.deleted; }, }, plugin: { reload: () => { - this.#sendPayload(context, { type: 'reload_response', silent: true }, null); + this.#sendPayload(context, { type: "reload_response", silent: true }, null); }, }, workspace: { list: async () => { const payload = { - type: 'list_open_workspaces_request', + type: "list_open_workspaces_request", } as InternalEventPayload; const response = await this.#sendForReply(context, payload); return response.workspaces.map((w) => { @@ -975,7 +978,7 @@ function stripDynamicCallbacks(inputs: { dynamic?: unknown }[]): FormInput[] { return inputs.map((input) => { // oxlint-disable-next-line no-explicit-any -- stripping dynamic from union type const { dynamic: _dynamic, ...rest } = input as any; - if ('inputs' in rest && Array.isArray(rest.inputs)) { + if ("inputs" in rest && Array.isArray(rest.inputs)) { rest.inputs = stripDynamicCallbacks(rest.inputs); } return rest as FormInput; @@ -983,8 +986,8 @@ function stripDynamicCallbacks(inputs: { dynamic?: unknown }[]): FormInput[] { } function genId(len = 5): string { - const alphabet = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ'; - let id = ''; + const alphabet = "01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"; + let id = ""; for (let i = 0; i < len; i++) { id += alphabet[Math.floor(Math.random() * alphabet.length)]; } @@ -1004,7 +1007,7 @@ function watchFile(filepath: string, cb: () => void) { const stat = statSync(filepath, { throwIfNoEntry: false }); if (stat == null || stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) { watchedFiles[filepath] = stat ?? null; - console.log('[plugin-runtime] watchFile triggered', filepath); + console.log("[plugin-runtime] watchFile triggered", filepath); cb(); } }); diff --git a/packages/plugin-runtime/src/common.ts b/packages/plugin-runtime/src/common.ts index f0d0b4b4..b37c60da 100644 --- a/packages/plugin-runtime/src/common.ts +++ b/packages/plugin-runtime/src/common.ts @@ -4,11 +4,11 @@ import type { DynamicAuthenticationArg, DynamicPromptFormArg, DynamicTemplateFunctionArg, -} from '@yaakapp/api'; +} from "@yaakapp/api"; import type { CallHttpAuthenticationActionArgs, CallTemplateFunctionArgs, -} from '@yaakapp-internal/plugins'; +} from "@yaakapp-internal/plugins"; type AnyDynamicArg = DynamicTemplateFunctionArg | DynamicAuthenticationArg | DynamicPromptFormArg; type AnyCallArgs = @@ -42,7 +42,7 @@ export async function applyDynamicFormInput( const resolvedArgs: AnyDynamicArg[] = []; for (const { dynamic, ...arg } of args) { const dynamicResult = - typeof dynamic === 'function' + typeof dynamic === "function" ? await dynamic( ctx, callArgs as CallTemplateFunctionArgs & @@ -56,7 +56,7 @@ export async function applyDynamicFormInput( ...dynamicResult, } as AnyDynamicArg; - if ('inputs' in newArg && Array.isArray(newArg.inputs)) { + if ("inputs" in newArg && Array.isArray(newArg.inputs)) { try { newArg.inputs = await applyDynamicFormInput( ctx, @@ -66,7 +66,7 @@ export async function applyDynamicFormInput( CallPromptFormDynamicArgs, ); } catch (e) { - console.error('Failed to apply dynamic form input', e); + console.error("Failed to apply dynamic form input", e); } } resolvedArgs.push(newArg); diff --git a/packages/plugin-runtime/src/index.ts b/packages/plugin-runtime/src/index.ts index 76af712b..6a6660bb 100644 --- a/packages/plugin-runtime/src/index.ts +++ b/packages/plugin-runtime/src/index.ts @@ -1,16 +1,16 @@ -import type { InternalEvent } from '@yaakapp/api'; -import WebSocket from 'ws'; -import { EventChannel } from './EventChannel'; -import { PluginHandle } from './PluginHandle'; +import type { InternalEvent } from "@yaakapp/api"; +import WebSocket from "ws"; +import { EventChannel } from "./EventChannel"; +import { PluginHandle } from "./PluginHandle"; const port = process.env.PORT; if (!port) { - throw new Error('Plugin runtime missing PORT'); + throw new Error("Plugin runtime missing PORT"); } const host = process.env.HOST; if (!host) { - throw new Error('Plugin runtime missing HOST'); + throw new Error("Plugin runtime missing HOST"); } const pluginToAppEvents = new EventChannel(); @@ -18,16 +18,16 @@ const plugins: Record = {}; const ws = new WebSocket(`ws://${host}:${port}`); -ws.on('message', async (e: Buffer) => { +ws.on("message", async (e: Buffer) => { try { await handleIncoming(e.toString()); } catch (err) { - console.log('Failed to handle incoming plugin event', err); + console.log("Failed to handle incoming plugin event", err); } }); -ws.on('open', () => console.log('Plugin runtime connected to websocket')); -ws.on('error', (err: unknown) => console.error('Plugin runtime websocket error', err)); -ws.on('close', (code: number) => console.log('Plugin runtime websocket closed', code)); +ws.on("open", () => console.log("Plugin runtime connected to websocket")); +ws.on("error", (err: unknown) => console.error("Plugin runtime websocket error", err)); +ws.on("close", (code: number) => console.log("Plugin runtime websocket closed", code)); // Listen for incoming events from plugins pluginToAppEvents.listen((e) => { @@ -38,7 +38,7 @@ pluginToAppEvents.listen((e) => { async function handleIncoming(msg: string) { const pluginEvent: InternalEvent = JSON.parse(msg); // Handle special event to bootstrap plugin - if (pluginEvent.payload.type === 'boot_request') { + if (pluginEvent.payload.type === "boot_request") { const plugin = new PluginHandle( pluginEvent.pluginRefId, pluginEvent.context, @@ -51,23 +51,23 @@ async function handleIncoming(msg: string) { // Once booted, forward all events to the plugin worker const plugin = plugins[pluginEvent.pluginRefId]; if (!plugin) { - console.warn('Failed to get plugin for event by', pluginEvent.pluginRefId); + console.warn("Failed to get plugin for event by", pluginEvent.pluginRefId); return; } - if (pluginEvent.payload.type === 'terminate_request') { + if (pluginEvent.payload.type === "terminate_request") { await plugin.terminate(); - console.log('Terminated plugin worker', pluginEvent.pluginRefId); + console.log("Terminated plugin worker", pluginEvent.pluginRefId); delete plugins[pluginEvent.pluginRefId]; } plugin.sendToWorker(pluginEvent); } -process.on('unhandledRejection', (reason, promise) => { - console.error('Unhandled Rejection at:', promise, 'reason:', reason); +process.on("unhandledRejection", (reason, promise) => { + console.error("Unhandled Rejection at:", promise, "reason:", reason); }); -process.on('uncaughtException', (error) => { - console.error('Uncaught Exception:', error); +process.on("uncaughtException", (error) => { + console.error("Uncaught Exception:", error); }); diff --git a/packages/plugin-runtime/src/interceptStdout.ts b/packages/plugin-runtime/src/interceptStdout.ts index 4729da31..bd8e4be0 100644 --- a/packages/plugin-runtime/src/interceptStdout.ts +++ b/packages/plugin-runtime/src/interceptStdout.ts @@ -1,5 +1,5 @@ /* oxlint-disable unbound-method */ -import process from 'node:process'; +import process from "node:process"; export function interceptStdout(intercept: (text: string) => string) { const old_stdout_write = process.stdout.write; @@ -25,5 +25,5 @@ export function interceptStdout(intercept: (text: string) => string) { } function interceptor(text: string, fn: (text: string) => string) { - return fn(text).replace(/\n$/, '') + (fn(text) && text.endsWith('\n') ? '\n' : ''); + return fn(text).replace(/\n$/, "") + (fn(text) && text.endsWith("\n") ? "\n" : ""); } diff --git a/packages/plugin-runtime/src/migrations.ts b/packages/plugin-runtime/src/migrations.ts index 322d24f9..45da919a 100644 --- a/packages/plugin-runtime/src/migrations.ts +++ b/packages/plugin-runtime/src/migrations.ts @@ -1,16 +1,16 @@ -import type { TemplateFunctionPlugin } from '@yaakapp/api'; +import type { TemplateFunctionPlugin } from "@yaakapp/api"; export function migrateTemplateFunctionSelectOptions( f: TemplateFunctionPlugin, ): TemplateFunctionPlugin { const migratedArgs = f.args.map((a) => { - if (a.type === 'select') { + if (a.type === "select") { // Migrate old options that had 'name' instead of 'label' type LegacyOption = { label?: string; value: string; name?: string }; a.options = a.options.map((o) => { const legacy = o as LegacyOption; return { - label: legacy.label ?? legacy.name ?? '', + label: legacy.label ?? legacy.name ?? "", value: legacy.value, }; }); diff --git a/packages/plugin-runtime/tests/common.test.ts b/packages/plugin-runtime/tests/common.test.ts index 10799ce5..4a072291 100644 --- a/packages/plugin-runtime/tests/common.test.ts +++ b/packages/plugin-runtime/tests/common.test.ts @@ -1,106 +1,106 @@ -import { applyFormInputDefaults } from '@yaakapp-internal/lib/templateFunction'; -import type { CallTemplateFunctionArgs } from '@yaakapp-internal/plugins'; -import type { Context, DynamicTemplateFunctionArg } from '@yaakapp/api'; -import { describe, expect, test } from 'vite-plus/test'; -import { applyDynamicFormInput } from '../src/common'; +import { applyFormInputDefaults } from "@yaakapp-internal/lib/templateFunction"; +import type { CallTemplateFunctionArgs } from "@yaakapp-internal/plugins"; +import type { Context, DynamicTemplateFunctionArg } from "@yaakapp/api"; +import { describe, expect, test } from "vite-plus/test"; +import { applyDynamicFormInput } from "../src/common"; -describe('applyFormInputDefaults', () => { - test('Works with top-level select', () => { +describe("applyFormInputDefaults", () => { + test("Works with top-level select", () => { const args: DynamicTemplateFunctionArg[] = [ { - type: 'select', - name: 'test', - options: [{ label: 'Option 1', value: 'one' }], - defaultValue: 'one', + type: "select", + name: "test", + options: [{ label: "Option 1", value: "one" }], + defaultValue: "one", }, ]; expect(applyFormInputDefaults(args, {})).toEqual({ - test: 'one', + test: "one", }); }); - test('Works with existing value', () => { + test("Works with existing value", () => { const args: DynamicTemplateFunctionArg[] = [ { - type: 'select', - name: 'test', - options: [{ label: 'Option 1', value: 'one' }], - defaultValue: 'one', + type: "select", + name: "test", + options: [{ label: "Option 1", value: "one" }], + defaultValue: "one", }, ]; - expect(applyFormInputDefaults(args, { test: 'explicit' })).toEqual({ - test: 'explicit', + expect(applyFormInputDefaults(args, { test: "explicit" })).toEqual({ + test: "explicit", }); }); - test('Works with recursive select', () => { + test("Works with recursive select", () => { const args: DynamicTemplateFunctionArg[] = [ - { type: 'text', name: 'dummy', defaultValue: 'top' }, + { type: "text", name: "dummy", defaultValue: "top" }, { - type: 'accordion', - label: 'Test', + type: "accordion", + label: "Test", inputs: [ - { type: 'text', name: 'name', defaultValue: 'hello' }, + { type: "text", name: "name", defaultValue: "hello" }, { - type: 'select', - name: 'test', - options: [{ label: 'Option 1', value: 'one' }], - defaultValue: 'one', + type: "select", + name: "test", + options: [{ label: "Option 1", value: "one" }], + defaultValue: "one", }, ], }, ]; expect(applyFormInputDefaults(args, {})).toEqual({ - dummy: 'top', - test: 'one', - name: 'hello', + dummy: "top", + test: "one", + name: "hello", }); }); - test('Works with dynamic options', () => { + test("Works with dynamic options", () => { const args: DynamicTemplateFunctionArg[] = [ { - type: 'select', - name: 'test', - defaultValue: 'one', + type: "select", + name: "test", + defaultValue: "one", options: [], dynamic() { - return { options: [{ label: 'Option 1', value: 'one' }] }; + return { options: [{ label: "Option 1", value: "one" }] }; }, }, ]; expect(applyFormInputDefaults(args, {})).toEqual({ - test: 'one', + test: "one", }); expect(applyFormInputDefaults(args, {})).toEqual({ - test: 'one', + test: "one", }); }); }); -describe('applyDynamicFormInput', () => { - test('Works with plain input', async () => { +describe("applyDynamicFormInput", () => { + test("Works with plain input", async () => { const ctx = {} as Context; const args: DynamicTemplateFunctionArg[] = [ - { type: 'text', name: 'name' }, - { type: 'checkbox', name: 'checked' }, + { type: "text", name: "name" }, + { type: "checkbox", name: "checked" }, ]; const callArgs: CallTemplateFunctionArgs = { values: {}, - purpose: 'preview', + purpose: "preview", }; expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([ - { type: 'text', name: 'name' }, - { type: 'checkbox', name: 'checked' }, + { type: "text", name: "name" }, + { type: "checkbox", name: "checked" }, ]); }); - test('Works with dynamic input', async () => { + test("Works with dynamic input", async () => { const ctx = {} as Context; const args: DynamicTemplateFunctionArg[] = [ { - type: 'text', - name: 'name', + type: "text", + name: "name", async dynamic(_ctx, _args) { return { hidden: true }; }, @@ -108,28 +108,28 @@ describe('applyDynamicFormInput', () => { ]; const callArgs: CallTemplateFunctionArgs = { values: {}, - purpose: 'preview', + purpose: "preview", }; expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([ - { type: 'text', name: 'name', hidden: true }, + { type: "text", name: "name", hidden: true }, ]); }); - test('Works with recursive dynamic input', async () => { + test("Works with recursive dynamic input", async () => { const ctx = {} as Context; const callArgs: CallTemplateFunctionArgs = { - values: { hello: 'world' }, - purpose: 'preview', + values: { hello: "world" }, + purpose: "preview", }; const args: DynamicTemplateFunctionArg[] = [ { - type: 'banner', + type: "banner", inputs: [ { - type: 'text', - name: 'name', + type: "text", + name: "name", async dynamic(_ctx, args) { - return { hidden: args.values.hello === 'world' }; + return { hidden: args.values.hello === "world" }; }, }, ], @@ -137,11 +137,11 @@ describe('applyDynamicFormInput', () => { ]; expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([ { - type: 'banner', + type: "banner", inputs: [ { - type: 'text', - name: 'name', + type: "text", + name: "name", hidden: true, }, ], diff --git a/plugins-external/faker/README.md b/plugins-external/faker/README.md index c7baaf82..5aee5f07 100644 --- a/plugins-external/faker/README.md +++ b/plugins-external/faker/README.md @@ -50,7 +50,7 @@ This will generate a random JSON body on every request: The plugin provides access to all FakerJS modules and their methods: | Category | Description | Example Methods | -|------------|---------------------------|--------------------------------------------| +| ---------- | ------------------------- | ------------------------------------------ | | `airline` | Airline-related data | `aircraftType`, `airline`, `airplane` | | `animal` | Animal names and types | `bear`, `bird`, `cat`, `dog`, `fish` | | `color` | Colors in various formats | `human`, `rgb`, `hex`, `hsl` | diff --git a/plugins-external/faker/package.json b/plugins-external/faker/package.json index c02a62e9..87630eed 100755 --- a/plugins-external/faker/package.json +++ b/plugins-external/faker/package.json @@ -1,8 +1,8 @@ { "name": "@yaak/faker", - "private": true, - "version": "1.1.1", "displayName": "Faker", + "version": "1.1.1", + "private": true, "description": "Template functions for generating fake data using FakerJS", "repository": { "type": "git", diff --git a/plugins-external/faker/src/index.ts b/plugins-external/faker/src/index.ts index 77f86831..2250e88c 100755 --- a/plugins-external/faker/src/index.ts +++ b/plugins-external/faker/src/index.ts @@ -1,34 +1,34 @@ -import { faker } from '@faker-js/faker'; -import type { DynamicTemplateFunctionArg, PluginDefinition } from '@yaakapp/api'; +import { faker } from "@faker-js/faker"; +import type { DynamicTemplateFunctionArg, PluginDefinition } from "@yaakapp/api"; const modules = [ - 'airline', - 'animal', - 'color', - 'commerce', - 'company', - 'database', - 'date', - 'finance', - 'git', - 'hacker', - 'image', - 'internet', - 'location', - 'lorem', - 'person', - 'music', - 'number', - 'phone', - 'science', - 'string', - 'system', - 'vehicle', - 'word', + "airline", + "animal", + "color", + "commerce", + "company", + "database", + "date", + "finance", + "git", + "hacker", + "image", + "internet", + "location", + "lorem", + "person", + "music", + "number", + "phone", + "science", + "string", + "system", + "vehicle", + "word", ]; function normalizeResult(result: unknown): string { - if (typeof result === 'string') return result; + if (typeof result === "string") return result; if (result instanceof Date) return result.toISOString(); return JSON.stringify(result); } @@ -37,20 +37,20 @@ function normalizeResult(result: unknown): string { function args(modName: string, fnName: string): DynamicTemplateFunctionArg[] { return [ { - type: 'banner', - color: 'info', + type: "banner", + color: "info", inputs: [ { - type: 'markdown', + type: "markdown", content: `Need help? View documentation for [\`${modName}.${fnName}(…)\`](https://fakerjs.dev/api/${encodeURIComponent(modName)}.html#${encodeURIComponent(fnName)})`, }, ], }, { - name: 'options', - label: 'Arguments', - type: 'editor', - language: 'json', + name: "options", + label: "Arguments", + type: "editor", + language: "json", optional: true, placeholder: 'e.g. { "min": 1, "max": 10 } or 10 or ["en","US"]', }, @@ -61,22 +61,22 @@ export const plugin: PluginDefinition = { templateFunctions: modules.flatMap((modName) => { const mod = faker[modName as keyof typeof faker]; return Object.keys(mod) - .filter((n) => n !== 'faker') + .filter((n) => n !== "faker") .map((fnName) => ({ - name: ['faker', modName, fnName].join('.'), + name: ["faker", modName, fnName].join("."), args: args(modName, fnName), async onRender(_ctx, args) { const fn = mod[fnName as keyof typeof mod] as (...a: unknown[]) => unknown; const options = args.values.options; // No options supplied - if (options == null || options === '') { + if (options == null || options === "") { return normalizeResult(fn()); } // Try JSON first let parsed: unknown = options; - if (typeof options === 'string') { + if (typeof options === "string") { try { parsed = JSON.parse(options); } catch { diff --git a/plugins-external/faker/tests/init.test.ts b/plugins-external/faker/tests/init.test.ts index ca779769..6b5b2ae3 100644 --- a/plugins-external/faker/tests/init.test.ts +++ b/plugins-external/faker/tests/init.test.ts @@ -1,8 +1,8 @@ -import { describe, expect, it } from 'vite-plus/test'; +import { describe, expect, it } from "vite-plus/test"; -describe('template-function-faker', () => { - it('exports all expected template functions', async () => { - const { plugin } = await import('../src/index'); +describe("template-function-faker", () => { + it("exports all expected template functions", async () => { + const { plugin } = await import("../src/index"); const names = plugin.templateFunctions?.map((fn) => fn.name).sort() ?? []; // Snapshot the full list of exported function names so we catch any @@ -10,13 +10,13 @@ describe('template-function-faker', () => { expect(names).toMatchSnapshot(); }); - it('renders date results as unquoted ISO strings', async () => { - const { plugin } = await import('../src/index'); - const fn = plugin.templateFunctions?.find((fn) => fn.name === 'faker.date.future'); + it("renders date results as unquoted ISO strings", async () => { + const { plugin } = await import("../src/index"); + const fn = plugin.templateFunctions?.find((fn) => fn.name === "faker.date.future"); // oxlint-disable-next-line unbound-method const onRender = fn?.onRender; - expect(onRender).toBeTypeOf('function'); + expect(onRender).toBeTypeOf("function"); if (onRender == null) { throw new Error("Expected template function 'faker.date.future' to define onRender"); } diff --git a/plugins-external/httpsnippet/README.md b/plugins-external/httpsnippet/README.md index 3b0f1765..effd9284 100644 --- a/plugins-external/httpsnippet/README.md +++ b/plugins-external/httpsnippet/README.md @@ -16,26 +16,26 @@ remembered for next time. Each language supports one or more libraries: -| Language | Libraries | -|---|---| -| C | libcurl | -| Clojure | clj-http | -| C# | HttpClient, RestSharp | -| Go | Native | -| HTTP | HTTP/1.1 | -| Java | AsyncHttp, NetHttp, OkHttp, Unirest | -| JavaScript | Axios, fetch, jQuery, XHR | -| Kotlin | OkHttp | -| Node.js | Axios, fetch, HTTP, Request, Unirest | -| Objective-C | NSURLSession | -| OCaml | CoHTTP | -| PHP | cURL, Guzzle, HTTP v1, HTTP v2 | -| PowerShell | Invoke-WebRequest, RestMethod | -| Python | http.client, Requests | -| R | httr | -| Ruby | Native | -| Shell | cURL, HTTPie, Wget | -| Swift | URLSession | +| Language | Libraries | +| ----------- | ------------------------------------ | +| C | libcurl | +| Clojure | clj-http | +| C# | HttpClient, RestSharp | +| Go | Native | +| HTTP | HTTP/1.1 | +| Java | AsyncHttp, NetHttp, OkHttp, Unirest | +| JavaScript | Axios, fetch, jQuery, XHR | +| Kotlin | OkHttp | +| Node.js | Axios, fetch, HTTP, Request, Unirest | +| Objective-C | NSURLSession | +| OCaml | CoHTTP | +| PHP | cURL, Guzzle, HTTP v1, HTTP v2 | +| PowerShell | Invoke-WebRequest, RestMethod | +| Python | http.client, Requests | +| R | httr | +| Ruby | Native | +| Shell | cURL, HTTPie, Wget | +| Swift | URLSession | ## Features diff --git a/plugins-external/httpsnippet/package.json b/plugins-external/httpsnippet/package.json index 9d2256e7..d6567d0f 100644 --- a/plugins-external/httpsnippet/package.json +++ b/plugins-external/httpsnippet/package.json @@ -1,10 +1,9 @@ { "name": "@yaak/httpsnippet", - "private": true, - "version": "1.0.3", "displayName": "HTTP Snippet", + "version": "1.0.3", + "private": true, "description": "Generate code snippets for HTTP requests in various languages and frameworks", - "minYaakVersion": "2026.2.0-beta.10", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", @@ -20,5 +19,6 @@ "devDependencies": { "@types/node": "^22.0.0", "typescript": "^5.9.3" - } + }, + "minYaakVersion": "2026.2.0-beta.10" } diff --git a/plugins-external/httpsnippet/src/index.ts b/plugins-external/httpsnippet/src/index.ts index bc67d762..87a1f1d4 100644 --- a/plugins-external/httpsnippet/src/index.ts +++ b/plugins-external/httpsnippet/src/index.ts @@ -1,11 +1,11 @@ -import { availableTargets, type HarRequest, HTTPSnippet } from '@readme/httpsnippet'; -import type { EditorLanguage, HttpRequest, PluginDefinition } from '@yaakapp/api'; +import { availableTargets, type HarRequest, HTTPSnippet } from "@readme/httpsnippet"; +import type { EditorLanguage, HttpRequest, PluginDefinition } from "@yaakapp/api"; // Get all available targets and build select options const targets = availableTargets(); // Targets to exclude from the language list -const excludedTargets = new Set(['json']); +const excludedTargets = new Set(["json"]); // Build language (target) options const languageOptions = targets @@ -17,8 +17,8 @@ const languageOptions = targets // Preferred clients per target (shown first in the list) const preferredClients: Record = { - javascript: 'fetch', - node: 'fetch', + javascript: "fetch", + node: "fetch", }; // Get client options for a given target key @@ -41,50 +41,50 @@ function getClientOptions(targetKey: string) { // Get default client for a target function getDefaultClient(targetKey: string): string { const options = getClientOptions(targetKey); - return options[0]?.value ?? ''; + return options[0]?.value ?? ""; } // Defaults -const defaultTarget = 'javascript'; +const defaultTarget = "javascript"; // Map httpsnippet target key to editor language for syntax highlighting const editorLanguageMap: Record = { - c: 'c', - clojure: 'clojure', - csharp: 'csharp', - go: 'go', - http: 'http', - java: 'java', - javascript: 'javascript', - kotlin: 'kotlin', - node: 'javascript', - objc: 'objective_c', - ocaml: 'ocaml', - php: 'php', - powershell: 'powershell', - python: 'python', - r: 'r', - ruby: 'ruby', - shell: 'shell', - swift: 'swift', + c: "c", + clojure: "clojure", + csharp: "csharp", + go: "go", + http: "http", + java: "java", + javascript: "javascript", + kotlin: "kotlin", + node: "javascript", + objc: "objective_c", + ocaml: "ocaml", + php: "php", + powershell: "powershell", + python: "python", + r: "r", + ruby: "ruby", + shell: "shell", + swift: "swift", }; function getEditorLanguage(targetKey: string): EditorLanguage { - return editorLanguageMap[targetKey] ?? 'text'; + return editorLanguageMap[targetKey] ?? "text"; } // Convert Yaak HttpRequest to HAR format function toHarRequest(request: Partial) { // Build URL with query parameters - let finalUrl = request.url || ''; + let finalUrl = request.url || ""; const urlParams = (request.urlParameters ?? []).filter((p) => p.enabled !== false && !!p.name); if (urlParams.length > 0) { - const [base, hash] = finalUrl.split('#'); - const separator = base?.includes('?') ? '&' : '?'; + const [base, hash] = finalUrl.split("#"); + const separator = base?.includes("?") ? "&" : "?"; const queryString = urlParams .map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`) - .join('&'); - finalUrl = base + separator + queryString + (hash ? `#${hash}` : ''); + .join("&"); + finalUrl = base + separator + queryString + (hash ? `#${hash}` : ""); } // Build headers array @@ -94,75 +94,75 @@ function toHarRequest(request: Partial) { // Handle authentication if (request.authentication?.disabled !== true) { - if (request.authenticationType === 'basic') { + if (request.authenticationType === "basic") { const credentials = btoa( - `${request.authentication?.username ?? ''}:${request.authentication?.password ?? ''}`, + `${request.authentication?.username ?? ""}:${request.authentication?.password ?? ""}`, ); - headers.push({ name: 'Authorization', value: `Basic ${credentials}` }); - } else if (request.authenticationType === 'bearer') { - const prefix = request.authentication?.prefix ?? 'Bearer'; - const token = request.authentication?.token ?? ''; - headers.push({ name: 'Authorization', value: `${prefix} ${token}`.trim() }); - } else if (request.authenticationType === 'apikey') { - if (request.authentication?.location === 'header') { + headers.push({ name: "Authorization", value: `Basic ${credentials}` }); + } else if (request.authenticationType === "bearer") { + const prefix = request.authentication?.prefix ?? "Bearer"; + const token = request.authentication?.token ?? ""; + headers.push({ name: "Authorization", value: `${prefix} ${token}`.trim() }); + } else if (request.authenticationType === "apikey") { + if (request.authentication?.location === "header") { headers.push({ - name: request.authentication?.key ?? 'X-Api-Key', - value: request.authentication?.value ?? '', + name: request.authentication?.key ?? "X-Api-Key", + value: request.authentication?.value ?? "", }); - } else if (request.authentication?.location === 'query') { - const sep = finalUrl.includes('?') ? '&' : '?'; + } else if (request.authentication?.location === "query") { + const sep = finalUrl.includes("?") ? "&" : "?"; finalUrl = [ finalUrl, sep, - encodeURIComponent(request.authentication?.key ?? 'token'), - '=', - encodeURIComponent(request.authentication?.value ?? ''), - ].join(''); + encodeURIComponent(request.authentication?.key ?? "token"), + "=", + encodeURIComponent(request.authentication?.value ?? ""), + ].join(""); } } } // Build HAR request object const har: Record = { - method: request.method || 'GET', + method: request.method || "GET", url: finalUrl, headers, }; // Handle request body - const bodyType = request.bodyType ?? 'none'; - if (bodyType !== 'none' && request.body) { - if (bodyType === 'application/x-www-form-urlencoded' && Array.isArray(request.body.form)) { + const bodyType = request.bodyType ?? "none"; + if (bodyType !== "none" && request.body) { + if (bodyType === "application/x-www-form-urlencoded" && Array.isArray(request.body.form)) { const params = request.body.form .filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name) .map((p: { name: string; value: string }) => ({ name: p.name, value: p.value })); har.postData = { - mimeType: 'application/x-www-form-urlencoded', + mimeType: "application/x-www-form-urlencoded", params, }; - } else if (bodyType === 'multipart/form-data' && Array.isArray(request.body.form)) { + } else if (bodyType === "multipart/form-data" && Array.isArray(request.body.form)) { const params = request.body.form .filter((p: { enabled?: boolean; name?: string }) => p.enabled !== false && !!p.name) .map((p: { name: string; value: string; file?: string; contentType?: string }) => { - const param: Record = { name: p.name, value: p.value || '' }; + const param: Record = { name: p.name, value: p.value || "" }; if (p.file) param.fileName = p.file; if (p.contentType) param.contentType = p.contentType; return param; }); har.postData = { - mimeType: 'multipart/form-data', + mimeType: "multipart/form-data", params, }; - } else if (bodyType === 'graphql' && typeof request.body.query === 'string') { + } else if (bodyType === "graphql" && typeof request.body.query === "string") { const body = { - query: request.body.query || '', + query: request.body.query || "", variables: maybeParseJSON(request.body.variables, undefined), }; har.postData = { - mimeType: 'application/json', + mimeType: "application/json", text: JSON.stringify(body), }; - } else if (typeof request.body.text === 'string') { + } else if (typeof request.body.text === "string") { har.postData = { mimeType: bodyType, text: request.body.text, @@ -174,7 +174,7 @@ function toHarRequest(request: Partial) { } function maybeParseJSON(v: unknown, fallback: T): unknown { - if (typeof v !== 'string') return fallback; + if (typeof v !== "string") return fallback; try { return JSON.parse(v); } catch { @@ -185,20 +185,20 @@ function maybeParseJSON(v: unknown, fallback: T): unknown { export const plugin: PluginDefinition = { httpRequestActions: [ { - label: 'Generate Code Snippet', - icon: 'copy', + label: "Generate Code Snippet", + icon: "copy", async onSelect(ctx, args) { // Render the request with variables resolved const renderedRequest = await ctx.httpRequest.render({ httpRequest: args.httpRequest, - purpose: 'send', + purpose: "send", }); // Convert to HAR format const harRequest = toHarRequest(renderedRequest) as HarRequest; // Get previously selected language or use defaults - const storedTarget = await ctx.store.get('selectedTarget'); + const storedTarget = await ctx.store.get("selectedTarget"); const initialTarget = storedTarget || defaultTarget; const storedClient = await ctx.store.get(`selectedClient:${initialTarget}`); const initialClient = storedClient || getDefaultClient(initialTarget); @@ -210,39 +210,39 @@ export const plugin: PluginDefinition = { target as Parameters[0], client as Parameters[1], ); - return (Array.isArray(result) ? result.join('\n') : result || '').replace(/\r\n/g, '\n'); + return (Array.isArray(result) ? result.join("\n") : result || "").replace(/\r\n/g, "\n"); }; // Generate initial code preview - let initialCode = ''; + let initialCode = ""; try { initialCode = generateSnippet(initialTarget, initialClient); } catch { - initialCode = '// Error generating snippet'; + initialCode = "// Error generating snippet"; } // Show dialog with language/library selectors and code preview const result = await ctx.prompt.form({ - id: 'httpsnippet', - title: 'Generate Code Snippet', - confirmText: 'Copy to Clipboard', - cancelText: 'Cancel', - size: 'md', + id: "httpsnippet", + title: "Generate Code Snippet", + confirmText: "Copy to Clipboard", + cancelText: "Cancel", + size: "md", inputs: [ { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'select', - name: 'target', - label: 'Language', + type: "select", + name: "target", + label: "Language", defaultValue: initialTarget, options: languageOptions, }, { - type: 'select', + type: "select", name: `client-${initialTarget}`, - label: 'Library', + label: "Library", defaultValue: initialClient, options: getClientOptions(initialTarget), dynamic(_ctx, { values }) { @@ -251,16 +251,16 @@ export const plugin: PluginDefinition = { return { name: `client-${targetKey}`, options, - defaultValue: options[0]?.value ?? '', + defaultValue: options[0]?.value ?? "", }; }, }, ], }, { - type: 'editor', - name: 'code', - label: 'Preview', + type: "editor", + name: "code", + label: "Preview", language: getEditorLanguage(initialTarget), defaultValue: initialCode, readOnly: true, @@ -274,7 +274,7 @@ export const plugin: PluginDefinition = { try { code = generateSnippet(targetKey, clientKey); } catch { - code = '// Error generating snippet'; + code = "// Error generating snippet"; } return { defaultValue: code, @@ -291,7 +291,7 @@ export const plugin: PluginDefinition = { const selectedClient = String( result[`client-${selectedTarget}`] || getDefaultClient(selectedTarget), ); - await ctx.store.set('selectedTarget', selectedTarget); + await ctx.store.set("selectedTarget", selectedTarget); await ctx.store.set(`selectedClient:${selectedTarget}`, selectedClient); // Generate snippet for the selected language @@ -299,15 +299,15 @@ export const plugin: PluginDefinition = { const codeText = generateSnippet(selectedTarget, selectedClient); await ctx.clipboard.copyText(codeText); await ctx.toast.show({ - message: 'Code snippet copied to clipboard', - icon: 'copy', - color: 'success', + message: "Code snippet copied to clipboard", + icon: "copy", + color: "success", }); } catch (err) { await ctx.toast.show({ message: `Failed to generate snippet: ${err instanceof Error ? err.message : String(err)}`, - icon: 'alert_triangle', - color: 'danger', + icon: "alert_triangle", + color: "danger", }); } } diff --git a/plugins-external/mcp-server/package.json b/plugins-external/mcp-server/package.json index ebeba65e..05ad04d2 100644 --- a/plugins-external/mcp-server/package.json +++ b/plugins-external/mcp-server/package.json @@ -1,10 +1,9 @@ { "name": "@yaak/mcp-server", - "private": true, - "version": "0.2.1", "displayName": "MCP Server", + "version": "0.2.1", + "private": true, "description": "Expose Yaak functionality via Model Context Protocol", - "minYaakVersion": "2026.1.0", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", @@ -24,5 +23,6 @@ "devDependencies": { "@types/node": "^25.0.3", "typescript": "^5.9.3" - } + }, + "minYaakVersion": "2026.1.0" } diff --git a/plugins-external/mcp-server/src/index.ts b/plugins-external/mcp-server/src/index.ts index b92bfd57..01c15a9f 100644 --- a/plugins-external/mcp-server/src/index.ts +++ b/plugins-external/mcp-server/src/index.ts @@ -1,7 +1,7 @@ -import type { Context, PluginDefinition } from '@yaakapp/api'; -import { createMcpServer } from './server.js'; +import type { Context, PluginDefinition } from "@yaakapp/api"; +import { createMcpServer } from "./server.js"; -const serverPort = parseInt(process.env.YAAK_PLUGIN_MCP_SERVER_PORT ?? '64343', 10); +const serverPort = parseInt(process.env.YAAK_PLUGIN_MCP_SERVER_PORT ?? "64343", 10); let mcpServer: Awaited> | null = null; @@ -9,16 +9,16 @@ export const plugin: PluginDefinition = { async init(ctx: Context) { // Start the server after waiting, so there's an active window open to do things // like show the startup toast. - console.log('Initializing MCP Server plugin'); + console.log("Initializing MCP Server plugin"); setTimeout(async () => { try { mcpServer = createMcpServer({ yaak: ctx }, serverPort); } catch (err) { - console.error('Failed to start MCP server:', err); + console.error("Failed to start MCP server:", err); void ctx.toast.show({ message: `Failed to start MCP Server: ${err instanceof Error ? err.message : String(err)}`, - icon: 'alert_triangle', - color: 'danger', + icon: "alert_triangle", + color: "danger", timeout: 10000, }); } @@ -26,7 +26,7 @@ export const plugin: PluginDefinition = { }, async dispose() { - console.log('Disposing MCP Server plugin'); + console.log("Disposing MCP Server plugin"); if (mcpServer) { await mcpServer.close(); diff --git a/plugins-external/mcp-server/src/server.ts b/plugins-external/mcp-server/src/server.ts index 12989a3b..d1f3e3c8 100644 --- a/plugins-external/mcp-server/src/server.ts +++ b/plugins-external/mcp-server/src/server.ts @@ -1,19 +1,19 @@ -import { StreamableHTTPTransport } from '@hono/mcp'; -import { serve } from '@hono/node-server'; -import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import { Hono } from 'hono'; -import { registerFolderTools } from './tools/folder.js'; -import { registerHttpRequestTools } from './tools/httpRequest.js'; -import { registerToastTools } from './tools/toast.js'; -import { registerWindowTools } from './tools/window.js'; -import { registerWorkspaceTools } from './tools/workspace.js'; -import type { McpServerContext } from './types.js'; +import { StreamableHTTPTransport } from "@hono/mcp"; +import { serve } from "@hono/node-server"; +import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import { Hono } from "hono"; +import { registerFolderTools } from "./tools/folder.js"; +import { registerHttpRequestTools } from "./tools/httpRequest.js"; +import { registerToastTools } from "./tools/toast.js"; +import { registerWindowTools } from "./tools/window.js"; +import { registerWorkspaceTools } from "./tools/workspace.js"; +import type { McpServerContext } from "./types.js"; export function createMcpServer(ctx: McpServerContext, port: number) { - console.log('Creating MCP server on port', port); + console.log("Creating MCP server on port", port); const mcpServer = new McpServer({ - name: 'yaak-mcp-server', - version: '0.1.0', + name: "yaak-mcp-server", + version: "0.1.0", }); // Register all tools @@ -26,14 +26,14 @@ export function createMcpServer(ctx: McpServerContext, port: number) { const app = new Hono(); const transport = new StreamableHTTPTransport(); - app.all('/mcp', async (c) => { + app.all("/mcp", async (c) => { if (!mcpServer.isConnected()) { // Connect the mcp with the transport await mcpServer.connect(transport); void ctx.yaak.toast.show({ message: `MCP Server connected`, - icon: 'info', - color: 'info', + icon: "info", + color: "info", timeout: 5000, }); } @@ -43,15 +43,15 @@ export function createMcpServer(ctx: McpServerContext, port: number) { const honoServer = serve( { port, - hostname: '127.0.0.1', + hostname: "127.0.0.1", fetch: app.fetch, }, (info) => { - console.log('Started MCP server on ', info.address); + console.log("Started MCP server on ", info.address); void ctx.yaak.toast.show({ message: `MCP Server running on http://127.0.0.1:${info.port}`, - icon: 'info', - color: 'secondary', + icon: "info", + color: "secondary", timeout: 10000, }); }, diff --git a/plugins-external/mcp-server/src/tools/folder.ts b/plugins-external/mcp-server/src/tools/folder.ts index f6bf068d..38aacfa3 100644 --- a/plugins-external/mcp-server/src/tools/folder.ts +++ b/plugins-external/mcp-server/src/tools/folder.ts @@ -1,20 +1,20 @@ -import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import * as z from 'zod'; -import type { McpServerContext } from '../types.js'; -import { getWorkspaceContext } from './helpers.js'; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import * as z from "zod"; +import type { McpServerContext } from "../types.js"; +import { getWorkspaceContext } from "./helpers.js"; import { authenticationSchema, authenticationTypeSchema, headersSchema, workspaceIdSchema, -} from './schemas.js'; +} from "./schemas.js"; export function registerFolderTools(server: McpServer, ctx: McpServerContext) { server.registerTool( - 'list_folders', + "list_folders", { - title: 'List Folders', - description: 'List all folders in a workspace', + title: "List Folders", + description: "List all folders in a workspace", inputSchema: { workspaceId: workspaceIdSchema, }, @@ -26,7 +26,7 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) { return { content: [ { - type: 'text' as const, + type: "text" as const, text: JSON.stringify(folders, null, 2), }, ], @@ -35,12 +35,12 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) { ); server.registerTool( - 'get_folder', + "get_folder", { - title: 'Get Folder', - description: 'Get details of a specific folder by ID', + title: "Get Folder", + description: "Get details of a specific folder by ID", inputSchema: { - id: z.string().describe('The folder ID'), + id: z.string().describe("The folder ID"), workspaceId: workspaceIdSchema, }, }, @@ -51,7 +51,7 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) { return { content: [ { - type: 'text' as const, + type: "text" as const, text: JSON.stringify(folder, null, 2), }, ], @@ -60,17 +60,17 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) { ); server.registerTool( - 'create_folder', + "create_folder", { - title: 'Create Folder', - description: 'Create a new folder in a workspace', + title: "Create Folder", + description: "Create a new folder in a workspace", inputSchema: { workspaceId: workspaceIdSchema, - name: z.string().describe('Folder name'), - folderId: z.string().optional().describe('Parent folder ID (for nested folders)'), - description: z.string().optional().describe('Folder description'), - sortPriority: z.number().optional().describe('Sort priority for ordering'), - headers: headersSchema.describe('Default headers to apply to requests in this folder'), + name: z.string().describe("Folder name"), + folderId: z.string().optional().describe("Parent folder ID (for nested folders)"), + description: z.string().optional().describe("Folder description"), + sortPriority: z.number().optional().describe("Sort priority for ordering"), + headers: headersSchema.describe("Default headers to apply to requests in this folder"), authenticationType: authenticationTypeSchema, authentication: authenticationSchema, }, @@ -79,7 +79,7 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) { const workspaceCtx = await getWorkspaceContext(ctx, ogWorkspaceId); const workspaceId = await workspaceCtx.yaak.window.workspaceId(); if (!workspaceId) { - throw new Error('No workspace is open'); + throw new Error("No workspace is open"); } const folder = await workspaceCtx.yaak.folder.create({ @@ -88,24 +88,24 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) { }); return { - content: [{ type: 'text' as const, text: JSON.stringify(folder, null, 2) }], + content: [{ type: "text" as const, text: JSON.stringify(folder, null, 2) }], }; }, ); server.registerTool( - 'update_folder', + "update_folder", { - title: 'Update Folder', - description: 'Update an existing folder', + title: "Update Folder", + description: "Update an existing folder", inputSchema: { - id: z.string().describe('Folder ID to update'), + id: z.string().describe("Folder ID to update"), workspaceId: workspaceIdSchema, - name: z.string().optional().describe('Folder name'), - folderId: z.string().optional().describe('Parent folder ID (for nested folders)'), - description: z.string().optional().describe('Folder description'), - sortPriority: z.number().optional().describe('Sort priority for ordering'), - headers: headersSchema.describe('Default headers to apply to requests in this folder'), + name: z.string().optional().describe("Folder name"), + folderId: z.string().optional().describe("Parent folder ID (for nested folders)"), + description: z.string().optional().describe("Folder description"), + sortPriority: z.number().optional().describe("Sort priority for ordering"), + headers: headersSchema.describe("Default headers to apply to requests in this folder"), authenticationType: authenticationTypeSchema, authentication: authenticationSchema, }, @@ -124,24 +124,24 @@ export function registerFolderTools(server: McpServer, ctx: McpServerContext) { id, }); return { - content: [{ type: 'text' as const, text: JSON.stringify(folder, null, 2) }], + content: [{ type: "text" as const, text: JSON.stringify(folder, null, 2) }], }; }, ); server.registerTool( - 'delete_folder', + "delete_folder", { - title: 'Delete Folder', - description: 'Delete a folder by ID', + title: "Delete Folder", + description: "Delete a folder by ID", inputSchema: { - id: z.string().describe('Folder ID to delete'), + id: z.string().describe("Folder ID to delete"), }, }, async ({ id }) => { const folder = await ctx.yaak.folder.delete({ id }); return { - content: [{ type: 'text' as const, text: `Deleted: ${folder.name} (${folder.id})` }], + content: [{ type: "text" as const, text: `Deleted: ${folder.name} (${folder.id})` }], }; }, ); diff --git a/plugins-external/mcp-server/src/tools/helpers.ts b/plugins-external/mcp-server/src/tools/helpers.ts index 05089be1..66dd546e 100644 --- a/plugins-external/mcp-server/src/tools/helpers.ts +++ b/plugins-external/mcp-server/src/tools/helpers.ts @@ -1,4 +1,4 @@ -import type { McpServerContext } from '../types.js'; +import type { McpServerContext } from "../types.js"; export async function getWorkspaceContext( ctx: McpServerContext, @@ -7,7 +7,7 @@ export async function getWorkspaceContext( const workspaces = await ctx.yaak.workspace.list(); if (!workspaceId && workspaces.length > 1) { - const workspaceList = workspaces.map((w, i) => `${i + 1}. ${w.name} (ID: ${w.id})`).join('\n'); + const workspaceList = workspaces.map((w, i) => `${i + 1}. ${w.name} (ID: ${w.id})`).join("\n"); throw new Error( `Multiple workspaces are open. Please specify which workspace to use.\n\n` + `Currently open workspaces:\n${workspaceList}\n\n` + @@ -19,7 +19,7 @@ export async function getWorkspaceContext( const workspace = workspaceId ? workspaces.find((w) => w.id === workspaceId) : workspaces[0]; if (!workspace) { - const workspaceList = workspaces.map((w) => `- ${w.name} (ID: ${w.id})`).join('\n'); + const workspaceList = workspaces.map((w) => `- ${w.name} (ID: ${w.id})`).join("\n"); throw new Error( `Workspace with ID "${workspaceId}" not found.\n\n` + `Available workspaces:\n${workspaceList}`, diff --git a/plugins-external/mcp-server/src/tools/httpRequest.ts b/plugins-external/mcp-server/src/tools/httpRequest.ts index 5effa654..a1aa342b 100644 --- a/plugins-external/mcp-server/src/tools/httpRequest.ts +++ b/plugins-external/mcp-server/src/tools/httpRequest.ts @@ -1,7 +1,7 @@ -import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import * as z from 'zod'; -import type { McpServerContext } from '../types.js'; -import { getWorkspaceContext } from './helpers.js'; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import * as z from "zod"; +import type { McpServerContext } from "../types.js"; +import { getWorkspaceContext } from "./helpers.js"; import { authenticationSchema, authenticationTypeSchema, @@ -10,14 +10,14 @@ import { headersSchema, urlParametersSchema, workspaceIdSchema, -} from './schemas.js'; +} from "./schemas.js"; export function registerHttpRequestTools(server: McpServer, ctx: McpServerContext) { server.registerTool( - 'list_http_requests', + "list_http_requests", { - title: 'List HTTP Requests', - description: 'List all HTTP requests in a workspace', + title: "List HTTP Requests", + description: "List all HTTP requests in a workspace", inputSchema: { workspaceId: workspaceIdSchema, }, @@ -29,7 +29,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex return { content: [ { - type: 'text' as const, + type: "text" as const, text: JSON.stringify(requests, null, 2), }, ], @@ -38,12 +38,12 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex ); server.registerTool( - 'get_http_request', + "get_http_request", { - title: 'Get HTTP Request', - description: 'Get details of a specific HTTP request by ID', + title: "Get HTTP Request", + description: "Get details of a specific HTTP request by ID", inputSchema: { - id: z.string().describe('The HTTP request ID'), + id: z.string().describe("The HTTP request ID"), workspaceId: workspaceIdSchema, }, }, @@ -54,7 +54,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex return { content: [ { - type: 'text' as const, + type: "text" as const, text: JSON.stringify(request, null, 2), }, ], @@ -63,13 +63,13 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex ); server.registerTool( - 'send_http_request', + "send_http_request", { - title: 'Send HTTP Request', - description: 'Send an HTTP request and get the response', + title: "Send HTTP Request", + description: "Send an HTTP request and get the response", inputSchema: { - id: z.string().describe('The HTTP request ID to send'), - environmentId: z.string().optional().describe('Optional environment ID to use'), + id: z.string().describe("The HTTP request ID to send"), + environmentId: z.string().optional().describe("Optional environment ID to use"), workspaceId: workspaceIdSchema, }, }, @@ -85,7 +85,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex return { content: [ { - type: 'text' as const, + type: "text" as const, text: JSON.stringify(response, null, 2), }, ], @@ -94,21 +94,21 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex ); server.registerTool( - 'create_http_request', + "create_http_request", { - title: 'Create HTTP Request', - description: 'Create a new HTTP request', + title: "Create HTTP Request", + description: "Create a new HTTP request", inputSchema: { workspaceId: workspaceIdSchema, name: z .string() .optional() - .describe('Request name (empty string to auto-generate from URL)'), - url: z.string().describe('Request URL'), - method: z.string().optional().describe('HTTP method (defaults to GET)'), - folderId: z.string().optional().describe('Parent folder ID'), - description: z.string().optional().describe('Request description'), - headers: headersSchema.describe('Request headers'), + .describe("Request name (empty string to auto-generate from URL)"), + url: z.string().describe("Request URL"), + method: z.string().optional().describe("HTTP method (defaults to GET)"), + folderId: z.string().optional().describe("Parent folder ID"), + description: z.string().optional().describe("Request description"), + headers: headersSchema.describe("Request headers"), urlParameters: urlParametersSchema, bodyType: bodyTypeSchema, body: bodySchema, @@ -120,7 +120,7 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex const workspaceCtx = await getWorkspaceContext(ctx, ogWorkspaceId); const workspaceId = await workspaceCtx.yaak.window.workspaceId(); if (!workspaceId) { - throw new Error('No workspace is open'); + throw new Error("No workspace is open"); } const httpRequest = await workspaceCtx.yaak.httpRequest.create({ @@ -129,25 +129,25 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex }); return { - content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }], + content: [{ type: "text" as const, text: JSON.stringify(httpRequest, null, 2) }], }; }, ); server.registerTool( - 'update_http_request', + "update_http_request", { - title: 'Update HTTP Request', - description: 'Update an existing HTTP request', + title: "Update HTTP Request", + description: "Update an existing HTTP request", inputSchema: { - id: z.string().describe('HTTP request ID to update'), + id: z.string().describe("HTTP request ID to update"), workspaceId: workspaceIdSchema, - name: z.string().optional().describe('Request name'), - url: z.string().optional().describe('Request URL'), - method: z.string().optional().describe('HTTP method'), - folderId: z.string().optional().describe('Parent folder ID'), - description: z.string().optional().describe('Request description'), - headers: headersSchema.describe('Request headers'), + name: z.string().optional().describe("Request name"), + url: z.string().optional().describe("Request URL"), + method: z.string().optional().describe("HTTP method"), + folderId: z.string().optional().describe("Parent folder ID"), + description: z.string().optional().describe("Request description"), + headers: headersSchema.describe("Request headers"), urlParameters: urlParametersSchema, bodyType: bodyTypeSchema, body: bodySchema, @@ -169,25 +169,25 @@ export function registerHttpRequestTools(server: McpServer, ctx: McpServerContex id, }); return { - content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }], + content: [{ type: "text" as const, text: JSON.stringify(httpRequest, null, 2) }], }; }, ); server.registerTool( - 'delete_http_request', + "delete_http_request", { - title: 'Delete HTTP Request', - description: 'Delete an HTTP request by ID', + title: "Delete HTTP Request", + description: "Delete an HTTP request by ID", inputSchema: { - id: z.string().describe('HTTP request ID to delete'), + id: z.string().describe("HTTP request ID to delete"), }, }, async ({ id }) => { const httpRequest = await ctx.yaak.httpRequest.delete({ id }); return { content: [ - { type: 'text' as const, text: `Deleted: ${httpRequest.name} (${httpRequest.id})` }, + { type: "text" as const, text: `Deleted: ${httpRequest.name} (${httpRequest.id})` }, ], }; }, diff --git a/plugins-external/mcp-server/src/tools/schemas.ts b/plugins-external/mcp-server/src/tools/schemas.ts index 7a1cb4ab..12d65432 100644 --- a/plugins-external/mcp-server/src/tools/schemas.ts +++ b/plugins-external/mcp-server/src/tools/schemas.ts @@ -1,9 +1,9 @@ -import * as z from 'zod'; +import * as z from "zod"; export const workspaceIdSchema = z .string() .optional() - .describe('Workspace ID (required if multiple workspaces are open)'); + .describe("Workspace ID (required if multiple workspaces are open)"); export const headersSchema = z .array( @@ -24,7 +24,7 @@ export const urlParametersSchema = z }), ) .optional() - .describe('URL query parameters'); + .describe("URL query parameters"); export const bodyTypeSchema = z .string() @@ -37,7 +37,7 @@ export const bodySchema = z .record(z.string(), z.any()) .optional() .describe( - 'Body content object. Structure varies by bodyType:\n' + + "Body content object. Structure varies by bodyType:\n" + '- "binary": { filePath: "/path/to/file" }\n' + '- "graphql": { query: "{ users { name } }", variables: "{\\"id\\": \\"123\\"}" }\n' + '- "application/x-www-form-urlencoded": { form: [{ name: "key", value: "val", enabled: true }] }\n' + @@ -56,7 +56,7 @@ export const authenticationSchema = z .record(z.string(), z.any()) .optional() .describe( - 'Authentication configuration object. Structure varies by authenticationType:\n' + + "Authentication configuration object. Structure varies by authenticationType:\n" + '- "basic": { username: "user", password: "pass" }\n' + '- "bearer": { token: "abc123", prefix: "Bearer" }\n' + '- "oauth2": { clientId: "...", clientSecret: "...", grantType: "authorization_code", authorizationUrl: "...", accessTokenUrl: "...", scope: "...", ... }\n' + diff --git a/plugins-external/mcp-server/src/tools/toast.ts b/plugins-external/mcp-server/src/tools/toast.ts index 624b4f0b..416648db 100644 --- a/plugins-external/mcp-server/src/tools/toast.ts +++ b/plugins-external/mcp-server/src/tools/toast.ts @@ -1,41 +1,41 @@ -import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import type { Color, Icon } from '@yaakapp/api'; -import * as z from 'zod'; -import type { McpServerContext } from '../types.js'; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { Color, Icon } from "@yaakapp/api"; +import * as z from "zod"; +import type { McpServerContext } from "../types.js"; const ICON_VALUES = [ - 'alert_triangle', - 'check', - 'check_circle', - 'chevron_down', - 'copy', - 'info', - 'pin', - 'search', - 'trash', + "alert_triangle", + "check", + "check_circle", + "chevron_down", + "copy", + "info", + "pin", + "search", + "trash", ] as const satisfies readonly Icon[]; const COLOR_VALUES = [ - 'primary', - 'secondary', - 'info', - 'success', - 'notice', - 'warning', - 'danger', + "primary", + "secondary", + "info", + "success", + "notice", + "warning", + "danger", ] as const satisfies readonly Color[]; export function registerToastTools(server: McpServer, ctx: McpServerContext) { server.registerTool( - 'show_toast', + "show_toast", { - title: 'Show Toast', - description: 'Show a toast notification in Yaak', + title: "Show Toast", + description: "Show a toast notification in Yaak", inputSchema: { - message: z.string().describe('The message to display'), - icon: z.enum(ICON_VALUES).optional().describe('Icon name'), - color: z.enum(COLOR_VALUES).optional().describe('Toast color'), - timeout: z.number().optional().describe('Timeout in milliseconds'), + message: z.string().describe("The message to display"), + icon: z.enum(ICON_VALUES).optional().describe("Icon name"), + color: z.enum(COLOR_VALUES).optional().describe("Toast color"), + timeout: z.number().optional().describe("Timeout in milliseconds"), }, }, async ({ message, icon, color, timeout }) => { @@ -49,7 +49,7 @@ export function registerToastTools(server: McpServer, ctx: McpServerContext) { return { content: [ { - type: 'text' as const, + type: "text" as const, text: `✓ Toast shown: "${message}"`, }, ], diff --git a/plugins-external/mcp-server/src/tools/window.ts b/plugins-external/mcp-server/src/tools/window.ts index 57b404cd..8164dc8a 100644 --- a/plugins-external/mcp-server/src/tools/window.ts +++ b/plugins-external/mcp-server/src/tools/window.ts @@ -1,13 +1,13 @@ -import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import type { McpServerContext } from '../types.js'; -import { getWorkspaceContext } from './helpers.js'; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServerContext } from "../types.js"; +import { getWorkspaceContext } from "./helpers.js"; export function registerWindowTools(server: McpServer, ctx: McpServerContext) { server.registerTool( - 'get_workspace_id', + "get_workspace_id", { - title: 'Get Workspace ID', - description: 'Get the current workspace ID', + title: "Get Workspace ID", + description: "Get the current workspace ID", }, async () => { const workspaceCtx = await getWorkspaceContext(ctx); @@ -16,8 +16,8 @@ export function registerWindowTools(server: McpServer, ctx: McpServerContext) { return { content: [ { - type: 'text' as const, - text: workspaceId || 'No workspace open', + type: "text" as const, + text: workspaceId || "No workspace open", }, ], }; @@ -25,10 +25,10 @@ export function registerWindowTools(server: McpServer, ctx: McpServerContext) { ); server.registerTool( - 'get_environment_id', + "get_environment_id", { - title: 'Get Environment ID', - description: 'Get the current environment ID', + title: "Get Environment ID", + description: "Get the current environment ID", }, async () => { const workspaceCtx = await getWorkspaceContext(ctx); @@ -37,8 +37,8 @@ export function registerWindowTools(server: McpServer, ctx: McpServerContext) { return { content: [ { - type: 'text' as const, - text: environmentId || 'No environment selected', + type: "text" as const, + text: environmentId || "No environment selected", }, ], }; diff --git a/plugins-external/mcp-server/src/tools/workspace.ts b/plugins-external/mcp-server/src/tools/workspace.ts index 6044da6a..b1b629c4 100644 --- a/plugins-external/mcp-server/src/tools/workspace.ts +++ b/plugins-external/mcp-server/src/tools/workspace.ts @@ -1,12 +1,12 @@ -import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; -import type { McpServerContext } from '../types.js'; +import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; +import type { McpServerContext } from "../types.js"; export function registerWorkspaceTools(server: McpServer, ctx: McpServerContext) { server.registerTool( - 'list_workspaces', + "list_workspaces", { - title: 'List Workspaces', - description: 'List all open workspaces in Yaak', + title: "List Workspaces", + description: "List all open workspaces in Yaak", }, async () => { const workspaces = await ctx.yaak.workspace.list(); @@ -14,7 +14,7 @@ export function registerWorkspaceTools(server: McpServer, ctx: McpServerContext) return { content: [ { - type: 'text' as const, + type: "text" as const, text: JSON.stringify(workspaces, null, 2), }, ], diff --git a/plugins-external/mcp-server/src/types.ts b/plugins-external/mcp-server/src/types.ts index fa57eb4f..72082f9b 100644 --- a/plugins-external/mcp-server/src/types.ts +++ b/plugins-external/mcp-server/src/types.ts @@ -1,4 +1,4 @@ -import type { Context } from '@yaakapp/api'; +import type { Context } from "@yaakapp/api"; export interface McpServerContext { yaak: Context; diff --git a/plugins/action-copy-curl/package.json b/plugins/action-copy-curl/package.json index 3ab20a0a..3e7ee118 100644 --- a/plugins/action-copy-curl/package.json +++ b/plugins/action-copy-curl/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/action-copy-curl", "displayName": "Copy as Curl", + "version": "0.1.0", + "private": true, "description": "Copy request as a curl command", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/action-copy-curl" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/action-copy-curl/src/index.ts b/plugins/action-copy-curl/src/index.ts index 35afeb71..87733100 100644 --- a/plugins/action-copy-curl/src/index.ts +++ b/plugins/action-copy-curl/src/index.ts @@ -1,23 +1,23 @@ -import type { HttpRequest, PluginDefinition } from '@yaakapp/api'; +import type { HttpRequest, PluginDefinition } from "@yaakapp/api"; -const NEWLINE = '\\\n '; +const NEWLINE = "\\\n "; export const plugin: PluginDefinition = { httpRequestActions: [ { - label: 'Copy as Curl', - icon: 'copy', + label: "Copy as Curl", + icon: "copy", async onSelect(ctx, args) { const rendered_request = await ctx.httpRequest.render({ httpRequest: args.httpRequest, - purpose: 'send', + purpose: "send", }); const data = await convertToCurl(rendered_request); await ctx.clipboard.copyText(data); await ctx.toast.show({ - message: 'Command copied to clipboard', - icon: 'copy', - color: 'success', + message: "Command copied to clipboard", + icon: "copy", + color: "success", }); }, }, @@ -25,40 +25,40 @@ export const plugin: PluginDefinition = { }; export async function convertToCurl(request: Partial) { - const xs = ['curl']; + const xs = ["curl"]; // Add method and URL all on first line - if (request.method) xs.push('-X', request.method); + if (request.method) xs.push("-X", request.method); // Build final URL with parameters (compatible with old curl) - let finalUrl = request.url || ''; + let finalUrl = request.url || ""; const urlParams = (request.urlParameters ?? []).filter(onlyEnabled); if (urlParams.length > 0) { // Build url - const [base, hash] = finalUrl.split('#'); - const separator = base?.includes('?') ? '&' : '?'; + const [base, hash] = finalUrl.split("#"); + const separator = base?.includes("?") ? "&" : "?"; const queryString = urlParams .map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`) - .join('&'); - finalUrl = base + separator + queryString + (hash ? `#${hash}` : ''); + .join("&"); + finalUrl = base + separator + queryString + (hash ? `#${hash}` : ""); } // Add API key authentication - if (request.authenticationType === 'apikey') { - if (request.authentication?.location === 'query') { - const sep = finalUrl.includes('?') ? '&' : '?'; + if (request.authenticationType === "apikey") { + if (request.authentication?.location === "query") { + const sep = finalUrl.includes("?") ? "&" : "?"; finalUrl = [ finalUrl, sep, - encodeURIComponent(request.authentication?.key ?? 'token'), - '=', - encodeURIComponent(request.authentication?.value ?? ''), - ].join(''); + encodeURIComponent(request.authentication?.key ?? "token"), + "=", + encodeURIComponent(request.authentication?.value ?? ""), + ].join(""); } else { request.headers = request.headers ?? []; request.headers.push({ - name: request.authentication?.key ?? 'X-Api-Key', - value: request.authentication?.value ?? '', + name: request.authentication?.key ?? "X-Api-Key", + value: request.authentication?.value ?? "", }); } } @@ -68,80 +68,80 @@ export async function convertToCurl(request: Partial) { // Add headers for (const h of (request.headers ?? []).filter(onlyEnabled)) { - xs.push('--header', quote(`${h.name}: ${h.value}`)); + xs.push("--header", quote(`${h.name}: ${h.value}`)); xs.push(NEWLINE); } // Add form params - const type = request.bodyType ?? 'none'; + const type = request.bodyType ?? "none"; if ( - (type === 'multipart/form-data' || type === 'application/x-www-form-urlencoded') && + (type === "multipart/form-data" || type === "application/x-www-form-urlencoded") && Array.isArray(request.body?.form) ) { - const flag = request.bodyType === 'multipart/form-data' ? '--form' : '--data'; + const flag = request.bodyType === "multipart/form-data" ? "--form" : "--data"; for (const p of (request.body?.form ?? []).filter(onlyEnabled)) { if (p.file) { let v = `${p.name}=@${p.file}`; - v += p.contentType ? `;type=${p.contentType}` : ''; + v += p.contentType ? `;type=${p.contentType}` : ""; xs.push(flag, v); } else { xs.push(flag, quote(`${p.name}=${p.value}`)); } xs.push(NEWLINE); } - } else if (type === 'graphql' && typeof request.body?.query === 'string') { + } else if (type === "graphql" && typeof request.body?.query === "string") { const body = { - query: request.body.query || '', + query: request.body.query || "", variables: maybeParseJSON(request.body.variables, undefined), }; - xs.push('--data', quote(JSON.stringify(body))); + xs.push("--data", quote(JSON.stringify(body))); xs.push(NEWLINE); - } else if (type !== 'none' && typeof request.body?.text === 'string') { - xs.push('--data', quote(request.body.text)); + } else if (type !== "none" && typeof request.body?.text === "string") { + xs.push("--data", quote(request.body.text)); xs.push(NEWLINE); } // Add basic/digest authentication if (request.authentication?.disabled !== true) { - if (request.authenticationType === 'basic' || request.authenticationType === 'digest') { - if (request.authenticationType === 'digest') xs.push('--digest'); + if (request.authenticationType === "basic" || request.authenticationType === "digest") { + if (request.authenticationType === "digest") xs.push("--digest"); xs.push( - '--user', + "--user", quote( - `${request.authentication?.username ?? ''}:${request.authentication?.password ?? ''}`, + `${request.authentication?.username ?? ""}:${request.authentication?.password ?? ""}`, ), ); xs.push(NEWLINE); } // Add bearer authentication - if (request.authenticationType === 'bearer') { + if (request.authenticationType === "bearer") { const value = - `${request.authentication?.prefix ?? 'Bearer'} ${request.authentication?.token ?? ''}`.trim(); - xs.push('--header', quote(`Authorization: ${value}`)); + `${request.authentication?.prefix ?? "Bearer"} ${request.authentication?.token ?? ""}`.trim(); + xs.push("--header", quote(`Authorization: ${value}`)); xs.push(NEWLINE); } - if (request.authenticationType === 'auth-aws-sig-v4') { + if (request.authenticationType === "auth-aws-sig-v4") { xs.push( - '--aws-sigv4', + "--aws-sigv4", [ - 'aws', - 'amz', - request.authentication?.region ?? '', - request.authentication?.service ?? '', - ].join(':'), + "aws", + "amz", + request.authentication?.region ?? "", + request.authentication?.service ?? "", + ].join(":"), ); xs.push(NEWLINE); xs.push( - '--user', + "--user", quote( - `${request.authentication?.accessKeyId ?? ''}:${request.authentication?.secretAccessKey ?? ''}`, + `${request.authentication?.accessKeyId ?? ""}:${request.authentication?.secretAccessKey ?? ""}`, ), ); if (request.authentication?.sessionToken) { xs.push(NEWLINE); - xs.push('--header', quote(`X-Amz-Security-Token: ${request.authentication.sessionToken}`)); + xs.push("--header", quote(`X-Amz-Security-Token: ${request.authentication.sessionToken}`)); } xs.push(NEWLINE); } @@ -152,7 +152,7 @@ export async function convertToCurl(request: Partial) { xs.splice(xs.length - 1, 1); } - return xs.join(' '); + return xs.join(" "); } function quote(arg: string): string { diff --git a/plugins/action-copy-curl/tests/index.test.ts b/plugins/action-copy-curl/tests/index.test.ts index 744501be..86fb301d 100644 --- a/plugins/action-copy-curl/tests/index.test.ts +++ b/plugins/action-copy-curl/tests/index.test.ts @@ -1,60 +1,60 @@ -import { describe, expect, test } from 'vite-plus/test'; -import { convertToCurl } from '../src'; +import { describe, expect, test } from "vite-plus/test"; +import { convertToCurl } from "../src"; -describe('exporter-curl', () => { - test('Exports GET with params', async () => { +describe("exporter-curl", () => { + test("Exports GET with params", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', + url: "https://yaak.app", urlParameters: [ - { name: 'a', value: 'aaa' }, - { name: 'b', value: 'bbb', enabled: true }, - { name: 'c', value: 'ccc', enabled: false }, + { name: "a", value: "aaa" }, + { name: "b", value: "bbb", enabled: true }, + { name: "c", value: "ccc", enabled: false }, ], }), - ).toEqual([`curl 'https://yaak.app?a=aaa&b=bbb'`].join(' \\n ')); + ).toEqual([`curl 'https://yaak.app?a=aaa&b=bbb'`].join(" \\n ")); }); - test('Exports GET with params and hash', async () => { + test("Exports GET with params and hash", async () => { expect( await convertToCurl({ - url: 'https://yaak.app/path#section', + url: "https://yaak.app/path#section", urlParameters: [ - { name: 'a', value: 'aaa' }, - { name: 'b', value: 'bbb', enabled: true }, - { name: 'c', value: 'ccc', enabled: false }, + { name: "a", value: "aaa" }, + { name: "b", value: "bbb", enabled: true }, + { name: "c", value: "ccc", enabled: false }, ], }), - ).toEqual([`curl 'https://yaak.app/path?a=aaa&b=bbb#section'`].join(' \\n ')); + ).toEqual([`curl 'https://yaak.app/path?a=aaa&b=bbb#section'`].join(" \\n ")); }); - test('Exports POST with url form data', async () => { + test("Exports POST with url form data", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'application/x-www-form-urlencoded', + url: "https://yaak.app", + method: "POST", + bodyType: "application/x-www-form-urlencoded", body: { form: [ - { name: 'a', value: 'aaa' }, - { name: 'b', value: 'bbb', enabled: true }, - { name: 'c', value: 'ccc', enabled: false }, + { name: "a", value: "aaa" }, + { name: "b", value: "bbb", enabled: true }, + { name: "c", value: "ccc", enabled: false }, ], }, }), ).toEqual( - [`curl -X POST 'https://yaak.app'`, `--data 'a=aaa'`, `--data 'b=bbb'`].join(' \\\n '), + [`curl -X POST 'https://yaak.app'`, `--data 'a=aaa'`, `--data 'b=bbb'`].join(" \\\n "), ); }); - test('Exports POST with GraphQL data', async () => { + test("Exports POST with GraphQL data", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'graphql', + url: "https://yaak.app", + method: "POST", + bodyType: "graphql", body: { - query: '{foo,bar}', + query: "{foo,bar}", variables: '{"a": "aaa", "b": "bbb"}', }, }), @@ -62,37 +62,37 @@ describe('exporter-curl', () => { [ `curl -X POST 'https://yaak.app'`, `--data '{"query":"{foo,bar}","variables":{"a":"aaa","b":"bbb"}}'`, - ].join(' \\\n '), + ].join(" \\\n "), ); }); - test('Exports POST with GraphQL data no variables', async () => { + test("Exports POST with GraphQL data no variables", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'graphql', + url: "https://yaak.app", + method: "POST", + bodyType: "graphql", body: { - query: '{foo,bar}', + query: "{foo,bar}", }, }), ).toEqual( - [`curl -X POST 'https://yaak.app'`, `--data '{"query":"{foo,bar}"}'`].join(' \\\n '), + [`curl -X POST 'https://yaak.app'`, `--data '{"query":"{foo,bar}"}'`].join(" \\\n "), ); }); - test('Exports PUT with multipart form', async () => { + test("Exports PUT with multipart form", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - method: 'PUT', - bodyType: 'multipart/form-data', + url: "https://yaak.app", + method: "PUT", + bodyType: "multipart/form-data", body: { form: [ - { name: 'a', value: 'aaa' }, - { name: 'b', value: 'bbb', enabled: true }, - { name: 'c', value: 'ccc', enabled: false }, - { name: 'f', file: '/foo/bar.png', contentType: 'image/png' }, + { name: "a", value: "aaa" }, + { name: "b", value: "bbb", enabled: true }, + { name: "c", value: "ccc", enabled: false }, + { name: "f", file: "/foo/bar.png", contentType: "image/png" }, ], }, }), @@ -101,314 +101,314 @@ describe('exporter-curl', () => { `curl -X PUT 'https://yaak.app'`, `--form 'a=aaa'`, `--form 'b=bbb'`, - '--form f=@/foo/bar.png;type=image/png', - ].join(' \\\n '), + "--form f=@/foo/bar.png;type=image/png", + ].join(" \\\n "), ); }); - test('Exports JSON body', async () => { + test("Exports JSON body", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'application/json', + url: "https://yaak.app", + method: "POST", + bodyType: "application/json", body: { text: `{"foo":"bar's"}`, }, - headers: [{ name: 'Content-Type', value: 'application/json' }], + headers: [{ name: "Content-Type", value: "application/json" }], }), ).toEqual( [ `curl -X POST 'https://yaak.app'`, `--header 'Content-Type: application/json'`, `--data '{"foo":"bar\\'s"}'`, - ].join(' \\\n '), + ].join(" \\\n "), ); }); - test('Exports multi-line JSON body', async () => { + test("Exports multi-line JSON body", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'application/json', + url: "https://yaak.app", + method: "POST", + bodyType: "application/json", body: { text: `{"foo":"bar",\n"baz":"qux"}`, }, - headers: [{ name: 'Content-Type', value: 'application/json' }], + headers: [{ name: "Content-Type", value: "application/json" }], }), ).toEqual( [ `curl -X POST 'https://yaak.app'`, `--header 'Content-Type: application/json'`, `--data '{"foo":"bar",\n"baz":"qux"}'`, - ].join(' \\\n '), + ].join(" \\\n "), ); }); - test('Exports headers', async () => { + test("Exports headers", async () => { expect( await convertToCurl({ headers: [ - { name: 'a', value: 'aaa' }, - { name: 'b', value: 'bbb', enabled: true }, - { name: 'c', value: 'ccc', enabled: false }, + { name: "a", value: "aaa" }, + { name: "b", value: "bbb", enabled: true }, + { name: "c", value: "ccc", enabled: false }, ], }), - ).toEqual([`curl ''`, `--header 'a: aaa'`, `--header 'b: bbb'`].join(' \\\n ')); + ).toEqual([`curl ''`, `--header 'a: aaa'`, `--header 'b: bbb'`].join(" \\\n ")); }); - test('Basic auth', async () => { + test("Basic auth", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'basic', + url: "https://yaak.app", + authenticationType: "basic", authentication: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, }), - ).toEqual([`curl 'https://yaak.app'`, `--user 'user:pass'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--user 'user:pass'`].join(" \\\n ")); }); - test('Basic auth disabled', async () => { + test("Basic auth disabled", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'basic', + url: "https://yaak.app", + authenticationType: "basic", authentication: { disabled: true, - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, }), - ).toEqual([`curl 'https://yaak.app'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`].join(" \\\n ")); }); - test('Broken basic auth', async () => { + test("Broken basic auth", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'basic', + url: "https://yaak.app", + authenticationType: "basic", authentication: {}, }), - ).toEqual([`curl 'https://yaak.app'`, `--user ':'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--user ':'`].join(" \\\n ")); }); - test('Digest auth', async () => { + test("Digest auth", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'digest', + url: "https://yaak.app", + authenticationType: "digest", authentication: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, }), - ).toEqual([`curl 'https://yaak.app'`, `--digest --user 'user:pass'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--digest --user 'user:pass'`].join(" \\\n ")); }); - test('Bearer auth', async () => { + test("Bearer auth", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'bearer', + url: "https://yaak.app", + authenticationType: "bearer", authentication: { - token: 'tok', + token: "tok", }, }), - ).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer tok'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer tok'`].join(" \\\n ")); }); - test('Bearer auth with custom prefix', async () => { + test("Bearer auth with custom prefix", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'bearer', + url: "https://yaak.app", + authenticationType: "bearer", authentication: { - token: 'abc123', - prefix: 'Token', + token: "abc123", + prefix: "Token", }, }), ).toEqual( - [`curl 'https://yaak.app'`, `--header 'Authorization: Token abc123'`].join(' \\\n '), + [`curl 'https://yaak.app'`, `--header 'Authorization: Token abc123'`].join(" \\\n "), ); }); - test('Bearer auth with empty prefix', async () => { + test("Bearer auth with empty prefix", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'bearer', + url: "https://yaak.app", + authenticationType: "bearer", authentication: { - token: 'xyz789', - prefix: '', + token: "xyz789", + prefix: "", }, }), - ).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: xyz789'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: xyz789'`].join(" \\\n ")); }); - test('Broken bearer auth', async () => { + test("Broken bearer auth", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'bearer', + url: "https://yaak.app", + authenticationType: "bearer", authentication: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, }), - ).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer'`].join(" \\\n ")); }); - test('AWS v4 auth', async () => { + test("AWS v4 auth", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'auth-aws-sig-v4', + url: "https://yaak.app", + authenticationType: "auth-aws-sig-v4", authentication: { - accessKeyId: 'ak', - secretAccessKey: 'sk', - sessionToken: '', - region: 'us-east-1', - service: 's3', + accessKeyId: "ak", + secretAccessKey: "sk", + sessionToken: "", + region: "us-east-1", + service: "s3", }, }), ).toEqual( - [`curl 'https://yaak.app'`, '--aws-sigv4 aws:amz:us-east-1:s3', `--user 'ak:sk'`].join( - ' \\\n ', + [`curl 'https://yaak.app'`, "--aws-sigv4 aws:amz:us-east-1:s3", `--user 'ak:sk'`].join( + " \\\n ", ), ); }); - test('AWS v4 auth with session', async () => { + test("AWS v4 auth with session", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'auth-aws-sig-v4', + url: "https://yaak.app", + authenticationType: "auth-aws-sig-v4", authentication: { - accessKeyId: 'ak', - secretAccessKey: 'sk', - sessionToken: 'st', - region: 'us-east-1', - service: 's3', + accessKeyId: "ak", + secretAccessKey: "sk", + sessionToken: "st", + region: "us-east-1", + service: "s3", }, }), ).toEqual( [ `curl 'https://yaak.app'`, - '--aws-sigv4 aws:amz:us-east-1:s3', + "--aws-sigv4 aws:amz:us-east-1:s3", `--user 'ak:sk'`, `--header 'X-Amz-Security-Token: st'`, - ].join(' \\\n '), + ].join(" \\\n "), ); }); - test('API key auth header', async () => { + test("API key auth header", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'apikey', + url: "https://yaak.app", + authenticationType: "apikey", authentication: { - location: 'header', - key: 'X-Header', - value: 'my-token', + location: "header", + key: "X-Header", + value: "my-token", }, }), - ).toEqual([`curl 'https://yaak.app'`, `--header 'X-Header: my-token'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--header 'X-Header: my-token'`].join(" \\\n ")); }); - test('API key auth header query', async () => { + test("API key auth header query", async () => { expect( await convertToCurl({ - url: 'https://yaak.app?hi=there', - urlParameters: [{ name: 'param', value: 'hi' }], - authenticationType: 'apikey', + url: "https://yaak.app?hi=there", + urlParameters: [{ name: "param", value: "hi" }], + authenticationType: "apikey", authentication: { - location: 'query', - key: 'foo', - value: 'bar', + location: "query", + key: "foo", + value: "bar", }, }), - ).toEqual([`curl 'https://yaak.app?hi=there¶m=hi&foo=bar'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app?hi=there¶m=hi&foo=bar'`].join(" \\\n ")); }); - test('API key auth header query with params', async () => { + test("API key auth header query with params", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - urlParameters: [{ name: 'param', value: 'hi' }], - authenticationType: 'apikey', + url: "https://yaak.app", + urlParameters: [{ name: "param", value: "hi" }], + authenticationType: "apikey", authentication: { - location: 'query', - key: 'foo', - value: 'bar', + location: "query", + key: "foo", + value: "bar", }, }), - ).toEqual([`curl 'https://yaak.app?param=hi&foo=bar'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app?param=hi&foo=bar'`].join(" \\\n ")); }); - test('API key auth header default', async () => { + test("API key auth header default", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'apikey', + url: "https://yaak.app", + authenticationType: "apikey", authentication: { - location: 'header', + location: "header", }, }), - ).toEqual([`curl 'https://yaak.app'`, `--header 'X-Api-Key: '`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`, `--header 'X-Api-Key: '`].join(" \\\n ")); }); - test('API key auth query', async () => { + test("API key auth query", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - authenticationType: 'apikey', + url: "https://yaak.app", + authenticationType: "apikey", authentication: { - location: 'query', - key: 'foo', - value: 'bar-baz', + location: "query", + key: "foo", + value: "bar-baz", }, }), - ).toEqual([`curl 'https://yaak.app?foo=bar-baz'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app?foo=bar-baz'`].join(" \\\n ")); }); - test('API key auth query with existing', async () => { + test("API key auth query with existing", async () => { expect( await convertToCurl({ - url: 'https://yaak.app?foo=bar&baz=qux', - authenticationType: 'apikey', + url: "https://yaak.app?foo=bar&baz=qux", + authenticationType: "apikey", authentication: { - location: 'query', - key: 'hi', - value: 'there', + location: "query", + key: "hi", + value: "there", }, }), - ).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&hi=there'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&hi=there'`].join(" \\\n ")); }); - test('API key auth query default', async () => { + test("API key auth query default", async () => { expect( await convertToCurl({ - url: 'https://yaak.app?foo=bar&baz=qux', - authenticationType: 'apikey', + url: "https://yaak.app?foo=bar&baz=qux", + authenticationType: "apikey", authentication: { - location: 'query', + location: "query", }, }), - ).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&token='`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&token='`].join(" \\\n ")); }); - test('Stale body data', async () => { + test("Stale body data", async () => { expect( await convertToCurl({ - url: 'https://yaak.app', - bodyType: 'none', + url: "https://yaak.app", + bodyType: "none", body: { - text: 'ignore me', + text: "ignore me", }, }), - ).toEqual([`curl 'https://yaak.app'`].join(' \\\n ')); + ).toEqual([`curl 'https://yaak.app'`].join(" \\\n ")); }); }); diff --git a/plugins/action-copy-grpcurl/README.md b/plugins/action-copy-grpcurl/README.md index 8865bee1..23355f7e 100644 --- a/plugins/action-copy-grpcurl/README.md +++ b/plugins/action-copy-grpcurl/README.md @@ -37,7 +37,6 @@ The plugin analyzes your gRPC request configuration and generates a properly for ### Simple Unary Call - ```bash grpcurl -plaintext \ -d '{"name": "John Doe"}' \ diff --git a/plugins/action-copy-grpcurl/package.json b/plugins/action-copy-grpcurl/package.json index 14407114..332773fb 100644 --- a/plugins/action-copy-grpcurl/package.json +++ b/plugins/action-copy-grpcurl/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/action-copy-grpcurl", "displayName": "Copy as gRPCurl", + "version": "0.1.0", + "private": true, "description": "Copy gRPC request as a grpcurl command", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/action-copy-grpcurl" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/action-copy-grpcurl/src/index.ts b/plugins/action-copy-grpcurl/src/index.ts index b332957d..1dd6378e 100644 --- a/plugins/action-copy-grpcurl/src/index.ts +++ b/plugins/action-copy-grpcurl/src/index.ts @@ -1,24 +1,24 @@ -import path from 'node:path'; -import type { GrpcRequest, PluginDefinition } from '@yaakapp/api'; +import path from "node:path"; +import type { GrpcRequest, PluginDefinition } from "@yaakapp/api"; -const NEWLINE = '\\\n '; +const NEWLINE = "\\\n "; export const plugin: PluginDefinition = { grpcRequestActions: [ { - label: 'Copy as gRPCurl', - icon: 'copy', + label: "Copy as gRPCurl", + icon: "copy", async onSelect(ctx, args) { const rendered_request = await ctx.grpcRequest.render({ grpcRequest: args.grpcRequest, - purpose: 'send', + purpose: "send", }); const data = await convert(rendered_request, args.protoFiles); await ctx.clipboard.copyText(data); await ctx.toast.show({ - message: 'Command copied to clipboard', - icon: 'copy', - color: 'success', + message: "Command copied to clipboard", + icon: "copy", + color: "success", }); }, }, @@ -26,14 +26,14 @@ export const plugin: PluginDefinition = { }; export async function convert(request: Partial, allProtoFiles: string[]) { - const xs = ['grpcurl']; + const xs = ["grpcurl"]; - if (request.url?.startsWith('http://')) { - xs.push('-plaintext'); + if (request.url?.startsWith("http://")) { + xs.push("-plaintext"); } - const protoIncludes = allProtoFiles.filter((f) => !f.endsWith('.proto')); - const protoFiles = allProtoFiles.filter((f) => f.endsWith('.proto')); + const protoIncludes = allProtoFiles.filter((f) => !f.endsWith(".proto")); + const protoFiles = allProtoFiles.filter((f) => f.endsWith(".proto")); const inferredIncludes = new Set(); for (const f of protoFiles) { @@ -41,59 +41,59 @@ export async function convert(request: Partial, allProtoFiles: stri if (protoDir) { inferredIncludes.add(protoDir); } else { - inferredIncludes.add(path.posix.join(f, '..')); - inferredIncludes.add(path.posix.join(f, '..', '..')); + inferredIncludes.add(path.posix.join(f, "..")); + inferredIncludes.add(path.posix.join(f, "..", "..")); } } for (const f of protoIncludes) { - xs.push('-import-path', quote(f)); + xs.push("-import-path", quote(f)); xs.push(NEWLINE); } for (const f of inferredIncludes.values()) { - xs.push('-import-path', quote(f)); + xs.push("-import-path", quote(f)); xs.push(NEWLINE); } for (const f of protoFiles) { - xs.push('-proto', quote(f)); + xs.push("-proto", quote(f)); xs.push(NEWLINE); } // Add headers for (const h of (request.metadata ?? []).filter(onlyEnabled)) { - xs.push('-H', quote(`${h.name}: ${h.value}`)); + xs.push("-H", quote(`${h.name}: ${h.value}`)); xs.push(NEWLINE); } // Add basic authentication if (request.authentication?.disabled !== true) { - if (request.authenticationType === 'basic') { - const user = request.authentication?.username ?? ''; - const pass = request.authentication?.password ?? ''; + if (request.authenticationType === "basic") { + const user = request.authentication?.username ?? ""; + const pass = request.authentication?.password ?? ""; const encoded = btoa(`${user}:${pass}`); - xs.push('-H', quote(`Authorization: Basic ${encoded}`)); + xs.push("-H", quote(`Authorization: Basic ${encoded}`)); xs.push(NEWLINE); - } else if (request.authenticationType === 'bearer') { + } else if (request.authenticationType === "bearer") { // Add bearer authentication - xs.push('-H', quote(`Authorization: Bearer ${request.authentication?.token ?? ''}`)); + xs.push("-H", quote(`Authorization: Bearer ${request.authentication?.token ?? ""}`)); xs.push(NEWLINE); - } else if (request.authenticationType === 'apikey') { - if (request.authentication?.location === 'query') { - const sep = request.url?.includes('?') ? '&' : '?'; + } else if (request.authenticationType === "apikey") { + if (request.authentication?.location === "query") { + const sep = request.url?.includes("?") ? "&" : "?"; request.url = [ request.url, sep, - encodeURIComponent(request.authentication?.key ?? 'token'), - '=', - encodeURIComponent(request.authentication?.value ?? ''), - ].join(''); + encodeURIComponent(request.authentication?.key ?? "token"), + "=", + encodeURIComponent(request.authentication?.value ?? ""), + ].join(""); } else { xs.push( - '-H', + "-H", quote( - `${request.authentication?.key ?? 'X-Api-Key'}: ${request.authentication?.value ?? ''}`, + `${request.authentication?.key ?? "X-Api-Key"}: ${request.authentication?.value ?? ""}`, ), ); } @@ -103,13 +103,13 @@ export async function convert(request: Partial, allProtoFiles: stri // Add form params if (request.message) { - xs.push('-d', quote(request.message)); + xs.push("-d", quote(request.message)); xs.push(NEWLINE); } // Add the server address if (request.url) { - const server = request.url.replace(/^https?:\/\//, ''); // remove protocol + const server = request.url.replace(/^https?:\/\//, ""); // remove protocol xs.push(server); xs.push(NEWLINE); } @@ -125,7 +125,7 @@ export async function convert(request: Partial, allProtoFiles: stri xs.splice(xs.length - 1, 1); } - return xs.join(' '); + return xs.join(" "); } function quote(arg: string): string { @@ -141,7 +141,7 @@ function findParentProtoDir(startPath: string): string | null { let dir = path.resolve(startPath); while (true) { - if (path.basename(dir) === 'proto') { + if (path.basename(dir) === "proto") { return dir; } diff --git a/plugins/action-copy-grpcurl/tests/index.test.ts b/plugins/action-copy-grpcurl/tests/index.test.ts index 38314c97..e4144a18 100644 --- a/plugins/action-copy-grpcurl/tests/index.test.ts +++ b/plugins/action-copy-grpcurl/tests/index.test.ts @@ -1,107 +1,107 @@ -import { describe, expect, test } from 'vite-plus/test'; -import { convert } from '../src'; +import { describe, expect, test } from "vite-plus/test"; +import { convert } from "../src"; -describe('exporter-curl', () => { - test('Simple example', async () => { +describe("exporter-curl", () => { + test("Simple example", async () => { expect( await convert( { - url: 'https://yaak.app', + url: "https://yaak.app", }, [], ), - ).toEqual(['grpcurl yaak.app'].join(' \\\n ')); + ).toEqual(["grpcurl yaak.app"].join(" \\\n ")); }); - test('Basic metadata', async () => { + test("Basic metadata", async () => { expect( await convert( { - url: 'https://yaak.app', + url: "https://yaak.app", metadata: [ - { name: 'aaa', value: 'AAA' }, - { enabled: true, name: 'bbb', value: 'BBB' }, - { enabled: false, name: 'disabled', value: 'ddd' }, + { name: "aaa", value: "AAA" }, + { enabled: true, name: "bbb", value: "BBB" }, + { enabled: false, name: "disabled", value: "ddd" }, ], }, [], ), - ).toEqual([`grpcurl -H 'aaa: AAA'`, `-H 'bbb: BBB'`, 'yaak.app'].join(' \\\n ')); + ).toEqual([`grpcurl -H 'aaa: AAA'`, `-H 'bbb: BBB'`, "yaak.app"].join(" \\\n ")); }); - test('Basic auth', async () => { + test("Basic auth", async () => { expect( await convert( { - url: 'https://yaak.app', - authenticationType: 'basic', + url: "https://yaak.app", + authenticationType: "basic", authentication: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, }, [], ), - ).toEqual([`grpcurl -H 'Authorization: Basic dXNlcjpwYXNz'`, 'yaak.app'].join(' \\\n ')); + ).toEqual([`grpcurl -H 'Authorization: Basic dXNlcjpwYXNz'`, "yaak.app"].join(" \\\n ")); }); - test('API key auth', async () => { + test("API key auth", async () => { expect( await convert( { - url: 'https://yaak.app', - authenticationType: 'apikey', + url: "https://yaak.app", + authenticationType: "apikey", authentication: { - key: 'X-Token', - value: 'tok', + key: "X-Token", + value: "tok", }, }, [], ), - ).toEqual([`grpcurl -H 'X-Token: tok'`, 'yaak.app'].join(' \\\n ')); + ).toEqual([`grpcurl -H 'X-Token: tok'`, "yaak.app"].join(" \\\n ")); }); - test('API key auth', async () => { + test("API key auth", async () => { expect( await convert( { - url: 'https://yaak.app', - authenticationType: 'apikey', + url: "https://yaak.app", + authenticationType: "apikey", authentication: { - location: 'query', - key: 'token', - value: 'tok 1', + location: "query", + key: "token", + value: "tok 1", }, }, [], ), - ).toEqual(['grpcurl', 'yaak.app?token=tok%201'].join(' \\\n ')); + ).toEqual(["grpcurl", "yaak.app?token=tok%201"].join(" \\\n ")); }); - test('Single proto file', async () => { - expect(await convert({ url: 'https://yaak.app' }, ['/foo/bar/baz.proto'])).toEqual( + test("Single proto file", async () => { + expect(await convert({ url: "https://yaak.app" }, ["/foo/bar/baz.proto"])).toEqual( [ `grpcurl -import-path '/foo/bar'`, `-import-path '/foo'`, `-proto '/foo/bar/baz.proto'`, - 'yaak.app', - ].join(' \\\n '), + "yaak.app", + ].join(" \\\n "), ); }); - test('Multiple proto files, same dir', async () => { + test("Multiple proto files, same dir", async () => { expect( - await convert({ url: 'https://yaak.app' }, ['/foo/bar/aaa.proto', '/foo/bar/bbb.proto']), + await convert({ url: "https://yaak.app" }, ["/foo/bar/aaa.proto", "/foo/bar/bbb.proto"]), ).toEqual( [ `grpcurl -import-path '/foo/bar'`, `-import-path '/foo'`, `-proto '/foo/bar/aaa.proto'`, `-proto '/foo/bar/bbb.proto'`, - 'yaak.app', - ].join(' \\\n '), + "yaak.app", + ].join(" \\\n "), ); }); - test('Multiple proto files, different dir', async () => { + test("Multiple proto files, different dir", async () => { expect( - await convert({ url: 'https://yaak.app' }, ['/aaa/bbb/ccc.proto', '/xxx/yyy/zzz.proto']), + await convert({ url: "https://yaak.app" }, ["/aaa/bbb/ccc.proto", "/xxx/yyy/zzz.proto"]), ).toEqual( [ `grpcurl -import-path '/aaa/bbb'`, @@ -110,23 +110,23 @@ describe('exporter-curl', () => { `-import-path '/xxx'`, `-proto '/aaa/bbb/ccc.proto'`, `-proto '/xxx/yyy/zzz.proto'`, - 'yaak.app', - ].join(' \\\n '), + "yaak.app", + ].join(" \\\n "), ); }); - test('Single include dir', async () => { - expect(await convert({ url: 'https://yaak.app' }, ['/aaa/bbb'])).toEqual( - [`grpcurl -import-path '/aaa/bbb'`, 'yaak.app'].join(' \\\n '), + test("Single include dir", async () => { + expect(await convert({ url: "https://yaak.app" }, ["/aaa/bbb"])).toEqual( + [`grpcurl -import-path '/aaa/bbb'`, "yaak.app"].join(" \\\n "), ); }); - test('Multiple include dir', async () => { - expect(await convert({ url: 'https://yaak.app' }, ['/aaa/bbb', '/xxx/yyy'])).toEqual( - [`grpcurl -import-path '/aaa/bbb'`, `-import-path '/xxx/yyy'`, 'yaak.app'].join(' \\\n '), + test("Multiple include dir", async () => { + expect(await convert({ url: "https://yaak.app" }, ["/aaa/bbb", "/xxx/yyy"])).toEqual( + [`grpcurl -import-path '/aaa/bbb'`, `-import-path '/xxx/yyy'`, "yaak.app"].join(" \\\n "), ); }); - test('Mixed proto and dirs', async () => { + test("Mixed proto and dirs", async () => { expect( - await convert({ url: 'https://yaak.app' }, ['/aaa/bbb', '/xxx/yyy', '/foo/bar.proto']), + await convert({ url: "https://yaak.app" }, ["/aaa/bbb", "/xxx/yyy", "/foo/bar.proto"]), ).toEqual( [ `grpcurl -import-path '/aaa/bbb'`, @@ -134,45 +134,45 @@ describe('exporter-curl', () => { `-import-path '/foo'`, `-import-path '/'`, `-proto '/foo/bar.proto'`, - 'yaak.app', - ].join(' \\\n '), + "yaak.app", + ].join(" \\\n "), ); }); - test('Sends data', async () => { + test("Sends data", async () => { expect( await convert( { - url: 'https://yaak.app', - message: JSON.stringify({ foo: 'bar', baz: 1.0 }, null, 2), + url: "https://yaak.app", + message: JSON.stringify({ foo: "bar", baz: 1.0 }, null, 2), }, - ['/foo.proto'], + ["/foo.proto"], ), ).toEqual( [ `grpcurl -import-path '/'`, `-proto '/foo.proto'`, `-d '{\n "foo": "bar",\n "baz": 1\n}'`, - 'yaak.app', - ].join(' \\\n '), + "yaak.app", + ].join(" \\\n "), ); }); - test('Sends data with unresolved template tags', async () => { + test("Sends data with unresolved template tags", async () => { expect( await convert( { - url: 'https://yaak.app', + url: "https://yaak.app", message: '{"timestamp": ${[ faker "timestamp" ]}, "foo": "bar"}', }, - ['/foo.proto'], + ["/foo.proto"], ), ).toEqual( [ `grpcurl -import-path '/'`, `-proto '/foo.proto'`, `-d '{"timestamp": \${[ faker "timestamp" ]}, "foo": "bar"}'`, - 'yaak.app', - ].join(' \\\n '), + "yaak.app", + ].join(" \\\n "), ); }); }); diff --git a/plugins/action-send-folder/package.json b/plugins/action-send-folder/package.json index 69bd09a4..34d4dd75 100644 --- a/plugins/action-send-folder/package.json +++ b/plugins/action-send-folder/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/action-send-folder", "displayName": "Send All", + "version": "0.1.0", + "private": true, "description": "Send all HTTP requests in a folder sequentially in tree order", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/action-send-folder" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/action-send-folder/src/index.ts b/plugins/action-send-folder/src/index.ts index f2741755..05ffae87 100644 --- a/plugins/action-send-folder/src/index.ts +++ b/plugins/action-send-folder/src/index.ts @@ -1,10 +1,10 @@ -import type { PluginDefinition } from '@yaakapp/api'; +import type { PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { folderActions: [ { - label: 'Send All', - icon: 'send_horizontal', + label: "Send All", + icon: "send_horizontal", async onSelect(ctx, args) { const targetFolder = args.folder; @@ -17,8 +17,8 @@ export const plugin: PluginDefinition = { // Build the send order to match tree ordering: // sort siblings by sortPriority then updatedAt, and traverse folders depth-first. const compareByOrder = ( - a: Pick, - b: Pick, + a: Pick<(typeof allFolders)[number], "sortPriority" | "updatedAt">, + b: Pick<(typeof allFolders)[number], "sortPriority" | "updatedAt">, ) => { if (a.sortPriority === b.sortPriority) { return a.updatedAt > b.updatedAt ? 1 : -1; @@ -26,7 +26,10 @@ export const plugin: PluginDefinition = { return a.sortPriority - b.sortPriority; }; - const childrenByFolderId = new Map>(); + const childrenByFolderId = new Map< + string, + Array<(typeof allFolders)[number] | (typeof allRequests)[number]> + >(); for (const folder of allFolders) { if (folder.folderId == null) continue; const children = childrenByFolderId.get(folder.folderId) ?? []; @@ -44,9 +47,9 @@ export const plugin: PluginDefinition = { const collectRequests = (folderId: string) => { const children = (childrenByFolderId.get(folderId) ?? []).slice().sort(compareByOrder); for (const child of children) { - if (child.model === 'folder') { + if (child.model === "folder") { collectRequests(child.id); - } else if (child.model === 'http_request') { + } else if (child.model === "http_request") { requestsToSend.push(child); } } @@ -55,9 +58,9 @@ export const plugin: PluginDefinition = { if (requestsToSend.length === 0) { await ctx.toast.show({ - message: 'No requests in folder', - icon: 'info', - color: 'info', + message: "No requests in folder", + icon: "info", + color: "info", }); return; } @@ -79,15 +82,15 @@ export const plugin: PluginDefinition = { // Show summary toast if (errorCount === 0) { await ctx.toast.show({ - message: `Sent ${successCount} request${successCount !== 1 ? 's' : ''}`, - icon: 'send_horizontal', - color: 'success', + message: `Sent ${successCount} request${successCount !== 1 ? "s" : ""}`, + icon: "send_horizontal", + color: "success", }); } else { await ctx.toast.show({ message: `Sent ${successCount}, failed ${errorCount}`, - icon: 'alert_triangle', - color: 'warning', + icon: "alert_triangle", + color: "warning", }); } }, diff --git a/plugins/auth-apikey/package.json b/plugins/auth-apikey/package.json index 36f20770..9423be00 100644 --- a/plugins/auth-apikey/package.json +++ b/plugins/auth-apikey/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-apikey", "displayName": "API Key Authentication", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using an API key", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-apikey" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/auth-apikey/src/index.ts b/plugins/auth-apikey/src/index.ts index 1e8122c7..bc8e4155 100644 --- a/plugins/auth-apikey/src/index.ts +++ b/plugins/auth-apikey/src/index.ts @@ -1,51 +1,51 @@ -import type { PluginDefinition } from '@yaakapp/api'; +import type { PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { authentication: { - name: 'apikey', - label: 'API Key', - shortLabel: 'API Key', + name: "apikey", + label: "API Key", + shortLabel: "API Key", args: [ { - type: 'select', - name: 'location', - label: 'Behavior', - defaultValue: 'header', + type: "select", + name: "location", + label: "Behavior", + defaultValue: "header", options: [ - { label: 'Insert Header', value: 'header' }, - { label: 'Append Query Parameter', value: 'query' }, + { label: "Insert Header", value: "header" }, + { label: "Append Query Parameter", value: "query" }, ], }, { - type: 'text', - name: 'key', - label: 'Key', + type: "text", + name: "key", + label: "Key", dynamic: (_ctx, { values }) => { - return values.location === 'query' + return values.location === "query" ? { - label: 'Parameter Name', - description: 'The name of the query parameter to add to the request', + label: "Parameter Name", + description: "The name of the query parameter to add to the request", } : { - label: 'Header Name', - description: 'The name of the header to add to the request', + label: "Header Name", + description: "The name of the header to add to the request", }; }, }, { - type: 'text', - name: 'value', - label: 'API Key', + type: "text", + name: "value", + label: "API Key", optional: true, password: true, }, ], async onApply(_ctx, { values }) { - const key = String(values.key ?? ''); - const value = String(values.value ?? ''); + const key = String(values.key ?? ""); + const value = String(values.value ?? ""); const location = String(values.location); - if (location === 'query') { + if (location === "query") { return { setQueryParameters: [{ name: key, value }] }; } return { setHeaders: [{ name: key, value }] }; diff --git a/plugins/auth-aws/README.md b/plugins/auth-aws/README.md index bdc546b9..df62698c 100644 --- a/plugins/auth-aws/README.md +++ b/plugins/auth-aws/README.md @@ -38,7 +38,7 @@ The plugin presents the following fields: - **Access Key ID** – Your AWS access key identifier - **Secret Access Key** – The secret associated with the access key -- **Session Token** *(optional)* – Used for temporary or assumed-role credentials (treated as secret) +- **Session Token** _(optional)_ – Used for temporary or assumed-role credentials (treated as secret) - **Region** – AWS region (e.g., `us-east-1`) - **Service** – AWS service identifier (e.g., `sts`, `s3`, `execute-api`) diff --git a/plugins/auth-aws/package.json b/plugins/auth-aws/package.json index a26c039a..62983c9b 100644 --- a/plugins/auth-aws/package.json +++ b/plugins/auth-aws/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-aws", "displayName": "AWS SigV4", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using AWS SigV4 signing", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-aws" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/auth-aws/src/index.ts b/plugins/auth-aws/src/index.ts index cba6ed31..b21b0205 100644 --- a/plugins/auth-aws/src/index.ts +++ b/plugins/auth-aws/src/index.ts @@ -1,56 +1,56 @@ -import { URL } from 'node:url'; -import type { PluginDefinition } from '@yaakapp/api'; -import type { CallHttpAuthenticationResponse } from '@yaakapp-internal/plugins'; -import type { Request } from 'aws4'; -import aws4 from 'aws4'; +import { URL } from "node:url"; +import type { PluginDefinition } from "@yaakapp/api"; +import type { CallHttpAuthenticationResponse } from "@yaakapp-internal/plugins"; +import type { Request } from "aws4"; +import aws4 from "aws4"; export const plugin: PluginDefinition = { authentication: { - name: 'awsv4', - label: 'AWS Signature', - shortLabel: 'AWS v4', + name: "awsv4", + label: "AWS Signature", + shortLabel: "AWS v4", args: [ - { name: 'accessKeyId', label: 'Access Key ID', type: 'text', password: true }, + { name: "accessKeyId", label: "Access Key ID", type: "text", password: true }, { - name: 'secretAccessKey', - label: 'Secret Access Key', - type: 'text', + name: "secretAccessKey", + label: "Secret Access Key", + type: "text", password: true, }, { - name: 'service', - label: 'Service Name', - type: 'text', - defaultValue: 'sts', - placeholder: 'sts', - description: 'The service that is receiving the request (sts, s3, sqs, ...)', + name: "service", + label: "Service Name", + type: "text", + defaultValue: "sts", + placeholder: "sts", + description: "The service that is receiving the request (sts, s3, sqs, ...)", }, { - name: 'region', - label: 'Region', - type: 'text', - placeholder: 'us-east-1', - description: 'The region that is receiving the request (defaults to us-east-1)', + name: "region", + label: "Region", + type: "text", + placeholder: "us-east-1", + description: "The region that is receiving the request (defaults to us-east-1)", optional: true, }, { - name: 'sessionToken', - label: 'Session Token', - type: 'text', + name: "sessionToken", + label: "Session Token", + type: "text", password: true, optional: true, - description: 'Only required if you are using temporary credentials', + description: "Only required if you are using temporary credentials", }, ], onApply(_ctx, { values, ...args }): CallHttpAuthenticationResponse { - const accessKeyId = String(values.accessKeyId || ''); - const secretAccessKey = String(values.secretAccessKey || ''); - const sessionToken = String(values.sessionToken || '') || undefined; + const accessKeyId = String(values.accessKeyId || ""); + const secretAccessKey = String(values.secretAccessKey || ""); + const sessionToken = String(values.sessionToken || "") || undefined; const url = new URL(args.url); - const headers: NonNullable = {}; - for (const headerName of ['content-type', 'host', 'x-amz-date', 'x-amz-security-token']) { + const headers: NonNullable = {}; + for (const headerName of ["content-type", "host", "x-amz-date", "x-amz-security-token"]) { const v = args.headers.find((h) => h.name.toLowerCase() === headerName); if (v != null) { headers[headerName] = v.value; @@ -61,8 +61,8 @@ export const plugin: PluginDefinition = { { host: url.host, method: args.method, - path: url.pathname + (url.search || ''), - service: String(values.service || 'sts'), + path: url.pathname + (url.search || ""), + service: String(values.service || "sts"), region: values.region ? String(values.region) : undefined, headers, doNotEncodePath: true, @@ -80,8 +80,8 @@ export const plugin: PluginDefinition = { return { setHeaders: Object.entries(signature.headers) - .filter(([name]) => name !== 'content-type') // Don't add this because we already have it - .map(([name, value]) => ({ name, value: String(value || '') })), + .filter(([name]) => name !== "content-type") // Don't add this because we already have it + .map(([name, value]) => ({ name, value: String(value || "") })), }; }, }, diff --git a/plugins/auth-basic/README.md b/plugins/auth-basic/README.md index 2ea322b0..438e2b23 100644 --- a/plugins/auth-basic/README.md +++ b/plugins/auth-basic/README.md @@ -1,4 +1,4 @@ -# Basic Authentication +# Basic Authentication A simple Basic Authentication plugin that implements HTTP Basic Auth according to [RFC 7617](https://datatracker.ietf.org/doc/html/rfc7617), enabling secure diff --git a/plugins/auth-basic/package.json b/plugins/auth-basic/package.json index b349b09e..3b11780a 100644 --- a/plugins/auth-basic/package.json +++ b/plugins/auth-basic/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-basic", "displayName": "Basic Authentication", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using Basic Auth", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-basic" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/auth-basic/src/index.ts b/plugins/auth-basic/src/index.ts index d7a5783a..63ac2138 100644 --- a/plugins/auth-basic/src/index.ts +++ b/plugins/auth-basic/src/index.ts @@ -1,30 +1,30 @@ -import type { PluginDefinition } from '@yaakapp/api'; +import type { PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { authentication: { - name: 'basic', - label: 'Basic Auth', - shortLabel: 'Basic', + name: "basic", + label: "Basic Auth", + shortLabel: "Basic", args: [ { - type: 'text', - name: 'username', - label: 'Username', + type: "text", + name: "username", + label: "Username", optional: true, }, { - type: 'text', - name: 'password', - label: 'Password', + type: "text", + name: "password", + label: "Password", optional: true, password: true, }, ], async onApply(_ctx, { values }) { - const username = values.username ?? ''; - const password = values.password ?? ''; - const value = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`; - return { setHeaders: [{ name: 'Authorization', value }] }; + const username = values.username ?? ""; + const password = values.password ?? ""; + const value = `Basic ${Buffer.from(`${username}:${password}`).toString("base64")}`; + return { setHeaders: [{ name: "Authorization", value }] }; }, }, }; diff --git a/plugins/auth-basic/tests/index.test.ts b/plugins/auth-basic/tests/index.test.ts index e577a313..f56bf038 100644 --- a/plugins/auth-basic/tests/index.test.ts +++ b/plugins/auth-basic/tests/index.test.ts @@ -1,77 +1,87 @@ -import type { Context } from '@yaakapp/api'; -import { describe, expect, test } from 'vite-plus/test'; -import { plugin } from '../src'; +import type { Context } from "@yaakapp/api"; +import { describe, expect, test } from "vite-plus/test"; +import { plugin } from "../src"; const ctx = {} as Context; -describe('auth-basic', () => { - test('Both username and password', async () => { +describe("auth-basic", () => { + test("Both username and password", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { username: 'user', password: 'pass' }, + values: { username: "user", password: "pass" }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), ).toEqual({ - setHeaders: [{ name: 'Authorization', value: `Basic ${Buffer.from('user:pass').toString('base64')}` }], + setHeaders: [ + { name: "Authorization", value: `Basic ${Buffer.from("user:pass").toString("base64")}` }, + ], }); }); - test('Empty password', async () => { + test("Empty password", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { username: 'apikey', password: '' }, + values: { username: "apikey", password: "" }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), ).toEqual({ - setHeaders: [{ name: 'Authorization', value: `Basic ${Buffer.from('apikey:').toString('base64')}` }], + setHeaders: [ + { name: "Authorization", value: `Basic ${Buffer.from("apikey:").toString("base64")}` }, + ], }); }); - test('Missing password (undefined)', async () => { + test("Missing password (undefined)", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { username: 'apikey' }, + values: { username: "apikey" }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), ).toEqual({ - setHeaders: [{ name: 'Authorization', value: `Basic ${Buffer.from('apikey:').toString('base64')}` }], + setHeaders: [ + { name: "Authorization", value: `Basic ${Buffer.from("apikey:").toString("base64")}` }, + ], }); }); - test('Missing username (undefined)', async () => { + test("Missing username (undefined)", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { password: 'secret' }, + values: { password: "secret" }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), ).toEqual({ - setHeaders: [{ name: 'Authorization', value: `Basic ${Buffer.from(':secret').toString('base64')}` }], + setHeaders: [ + { name: "Authorization", value: `Basic ${Buffer.from(":secret").toString("base64")}` }, + ], }); }); - test('No values (both undefined)', async () => { + test("No values (both undefined)", async () => { expect( await plugin.authentication?.onApply(ctx, { values: {}, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), ).toEqual({ - setHeaders: [{ name: 'Authorization', value: `Basic ${Buffer.from(':').toString('base64')}` }], + setHeaders: [ + { name: "Authorization", value: `Basic ${Buffer.from(":").toString("base64")}` }, + ], }); }); }); diff --git a/plugins/auth-bearer/package.json b/plugins/auth-bearer/package.json index fc16e54c..260bc07e 100644 --- a/plugins/auth-bearer/package.json +++ b/plugins/auth-bearer/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-bearer", "displayName": "Bearer Authentication", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using bearer authentication", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-bearer" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/auth-bearer/src/index.ts b/plugins/auth-bearer/src/index.ts index 399ed865..c1c893b6 100644 --- a/plugins/auth-bearer/src/index.ts +++ b/plugins/auth-bearer/src/index.ts @@ -1,26 +1,26 @@ -import type { PluginDefinition } from '@yaakapp/api'; -import type { CallHttpAuthenticationRequest } from '@yaakapp-internal/plugins'; +import type { PluginDefinition } from "@yaakapp/api"; +import type { CallHttpAuthenticationRequest } from "@yaakapp-internal/plugins"; export const plugin: PluginDefinition = { authentication: { - name: 'bearer', - label: 'Bearer Token', - shortLabel: 'Bearer', + name: "bearer", + label: "Bearer Token", + shortLabel: "Bearer", args: [ { - type: 'text', - name: 'token', - label: 'Token', + type: "text", + name: "token", + label: "Token", optional: true, password: true, }, { - type: 'text', - name: 'prefix', - label: 'Prefix', + type: "text", + name: "prefix", + label: "Prefix", optional: true, - placeholder: '', - defaultValue: 'Bearer', + placeholder: "", + defaultValue: "Bearer", description: 'The prefix to use for the Authorization header, which will be of the format " ".', }, @@ -31,9 +31,9 @@ export const plugin: PluginDefinition = { }, }; -function generateAuthorizationHeader(values: CallHttpAuthenticationRequest['values']) { - const token = String(values.token || '').trim(); - const prefix = String(values.prefix || '').trim(); +function generateAuthorizationHeader(values: CallHttpAuthenticationRequest["values"]) { + const token = String(values.token || "").trim(); + const prefix = String(values.prefix || "").trim(); const value = `${prefix} ${token}`.trim(); - return { name: 'Authorization', value }; + return { name: "Authorization", value }; } diff --git a/plugins/auth-bearer/tests/index.test.ts b/plugins/auth-bearer/tests/index.test.ts index 225f854c..a46653c7 100644 --- a/plugins/auth-bearer/tests/index.test.ts +++ b/plugins/auth-bearer/tests/index.test.ts @@ -1,67 +1,67 @@ -import type { Context } from '@yaakapp/api'; -import { describe, expect, test } from 'vite-plus/test'; -import { plugin } from '../src'; +import type { Context } from "@yaakapp/api"; +import { describe, expect, test } from "vite-plus/test"; +import { plugin } from "../src"; const ctx = {} as Context; -describe('auth-bearer', () => { - test('No values', async () => { +describe("auth-bearer", () => { + test("No values", async () => { expect( await plugin.authentication?.onApply(ctx, { values: {}, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), - ).toEqual({ setHeaders: [{ name: 'Authorization', value: '' }] }); + ).toEqual({ setHeaders: [{ name: "Authorization", value: "" }] }); }); - test('Only token', async () => { + test("Only token", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { token: 'my-token' }, + values: { token: "my-token" }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), - ).toEqual({ setHeaders: [{ name: 'Authorization', value: 'my-token' }] }); + ).toEqual({ setHeaders: [{ name: "Authorization", value: "my-token" }] }); }); - test('Only prefix', async () => { + test("Only prefix", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { prefix: 'Hello' }, + values: { prefix: "Hello" }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), - ).toEqual({ setHeaders: [{ name: 'Authorization', value: 'Hello' }] }); + ).toEqual({ setHeaders: [{ name: "Authorization", value: "Hello" }] }); }); - test('Prefix and token', async () => { + test("Prefix and token", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { prefix: 'Hello', token: 'my-token' }, + values: { prefix: "Hello", token: "my-token" }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), - ).toEqual({ setHeaders: [{ name: 'Authorization', value: 'Hello my-token' }] }); + ).toEqual({ setHeaders: [{ name: "Authorization", value: "Hello my-token" }] }); }); - test('Extra spaces', async () => { + test("Extra spaces", async () => { expect( await plugin.authentication?.onApply(ctx, { - values: { prefix: '\t Hello ', token: ' \nmy-token ' }, + values: { prefix: "\t Hello ", token: " \nmy-token " }, headers: [], - url: 'https://yaak.app', - method: 'POST', - contextId: '111', + url: "https://yaak.app", + method: "POST", + contextId: "111", }), - ).toEqual({ setHeaders: [{ name: 'Authorization', value: 'Hello my-token' }] }); + ).toEqual({ setHeaders: [{ name: "Authorization", value: "Hello my-token" }] }); }); }); diff --git a/plugins/auth-jwt/package.json b/plugins/auth-jwt/package.json index 687031f8..1d0e438c 100644 --- a/plugins/auth-jwt/package.json +++ b/plugins/auth-jwt/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-jwt", "displayName": "JSON Web Tokens", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using JSON web tokens (JWT)", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-jwt" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/auth-jwt/src/index.ts b/plugins/auth-jwt/src/index.ts index 158d7fda..0c4bc6e8 100644 --- a/plugins/auth-jwt/src/index.ts +++ b/plugins/auth-jwt/src/index.ts @@ -1,104 +1,104 @@ -import type { PluginDefinition } from '@yaakapp/api'; -import jwt from 'jsonwebtoken'; +import type { PluginDefinition } from "@yaakapp/api"; +import jwt from "jsonwebtoken"; const algorithms = [ - 'HS256', - 'HS384', - 'HS512', - 'RS256', - 'RS384', - 'RS512', - 'PS256', - 'PS384', - 'PS512', - 'ES256', - 'ES384', - 'ES512', - 'none', + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "PS256", + "PS384", + "PS512", + "ES256", + "ES384", + "ES512", + "none", ] as const; const defaultAlgorithm = algorithms[0]; export const plugin: PluginDefinition = { authentication: { - name: 'jwt', - label: 'JWT Bearer', - shortLabel: 'JWT', + name: "jwt", + label: "JWT Bearer", + shortLabel: "JWT", args: [ { - type: 'select', - name: 'algorithm', - label: 'Algorithm', + type: "select", + name: "algorithm", + label: "Algorithm", hideLabel: true, defaultValue: defaultAlgorithm, - options: algorithms.map((value) => ({ label: value === 'none' ? 'None' : value, value })), + options: algorithms.map((value) => ({ label: value === "none" ? "None" : value, value })), }, { - type: 'text', - name: 'secret', - label: 'Secret or Private Key', + type: "text", + name: "secret", + label: "Secret or Private Key", password: true, optional: true, multiLine: true, }, { - type: 'checkbox', - name: 'secretBase64', - label: 'Secret is base64 encoded', + type: "checkbox", + name: "secretBase64", + label: "Secret is base64 encoded", }, { - type: 'editor', - name: 'payload', - label: 'JWT Payload', - language: 'json', + type: "editor", + name: "payload", + label: "JWT Payload", + language: "json", defaultValue: '{\n "foo": "bar"\n}', - placeholder: '{ }', + placeholder: "{ }", }, { - type: 'accordion', - label: 'Advanced', + type: "accordion", + label: "Advanced", inputs: [ { - type: 'editor', - name: 'headers', - label: 'JWT Header', - description: 'Merged with auto-generated header fields like alg (e.g., kid)', - language: 'json', - defaultValue: '{}', - placeholder: '{ }', + type: "editor", + name: "headers", + label: "JWT Header", + description: "Merged with auto-generated header fields like alg (e.g., kid)", + language: "json", + defaultValue: "{}", + placeholder: "{ }", optional: true, }, { - type: 'select', - name: 'location', - label: 'Behavior', - defaultValue: 'header', + type: "select", + name: "location", + label: "Behavior", + defaultValue: "header", options: [ - { label: 'Insert Header', value: 'header' }, - { label: 'Append Query Parameter', value: 'query' }, + { label: "Insert Header", value: "header" }, + { label: "Append Query Parameter", value: "query" }, ], }, { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'text', - name: 'name', - label: 'Header Name', - defaultValue: 'Authorization', + type: "text", + name: "name", + label: "Header Name", + defaultValue: "Authorization", optional: true, - description: 'The name of the header to add to the request', + description: "The name of the header to add to the request", }, { - type: 'text', - name: 'headerPrefix', - label: 'Header Prefix', + type: "text", + name: "headerPrefix", + label: "Header Prefix", optional: true, - defaultValue: 'Bearer', + defaultValue: "Bearer", }, ], dynamic(_ctx, args) { - if (args.values.location === 'query') { + if (args.values.location === "query") { return { hidden: true, }; @@ -106,14 +106,14 @@ export const plugin: PluginDefinition = { }, }, { - type: 'text', - name: 'name', - label: 'Parameter Name', - description: 'The name of the query parameter to add to the request', - defaultValue: 'token', + type: "text", + name: "name", + label: "Parameter Name", + description: "The name of the query parameter to add to the request", + defaultValue: "token", optional: true, dynamic(_ctx, args) { - if (args.values.location !== 'query') { + if (args.values.location !== "query") { return { hidden: true, }; @@ -125,7 +125,7 @@ export const plugin: PluginDefinition = { ], async onApply(_ctx, { values }) { const { algorithm, secret: _secret, secretBase64, payload, headers } = values; - const secret = secretBase64 ? Buffer.from(`${_secret}`, 'base64') : `${_secret}`; + const secret = secretBase64 ? Buffer.from(`${_secret}`, "base64") : `${_secret}`; const parsedHeaders = headers ? JSON.parse(`${headers}`) : undefined; @@ -135,12 +135,12 @@ export const plugin: PluginDefinition = { header: parsedHeaders as jwt.JwtHeader | undefined, }); - if (values.location === 'query') { - const paramName = String(values.name || 'token'); + if (values.location === "query") { + const paramName = String(values.name || "token"); return { setQueryParameters: [{ name: paramName, value: token }] }; } - const headerPrefix = values.headerPrefix != null ? values.headerPrefix : 'Bearer'; - const headerName = String(values.name || 'Authorization'); + const headerPrefix = values.headerPrefix != null ? values.headerPrefix : "Bearer"; + const headerName = String(values.name || "Authorization"); const headerValue = `${headerPrefix} ${token}`.trim(); return { setHeaders: [{ name: headerName, value: headerValue }] }; }, diff --git a/plugins/auth-ntlm/package.json b/plugins/auth-ntlm/package.json index 9e2a1f4f..6348eeee 100644 --- a/plugins/auth-ntlm/package.json +++ b/plugins/auth-ntlm/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-ntlm", "displayName": "NTLM Authentication", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using NTLM authentication", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-ntlm" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/auth-ntlm/src/index.ts b/plugins/auth-ntlm/src/index.ts index acf355d4..9adfa091 100644 --- a/plugins/auth-ntlm/src/index.ts +++ b/plugins/auth-ntlm/src/index.ts @@ -1,11 +1,11 @@ -import type { PluginDefinition } from '@yaakapp/api'; +import type { PluginDefinition } from "@yaakapp/api"; -import { ntlm } from 'httpntlm'; +import { ntlm } from "httpntlm"; function extractNtlmChallenge(headers: Array<{ name: string; value: string }>): string | null { const authValues = headers - .filter((h) => h.name.toLowerCase() === 'www-authenticate') - .flatMap((h) => h.value.split(',')) + .filter((h) => h.name.toLowerCase() === "www-authenticate") + .flatMap((h) => h.value.split(",")) .map((v) => v.trim()) .filter(Boolean); @@ -14,40 +14,40 @@ function extractNtlmChallenge(headers: Array<{ name: string; value: string }>): export const plugin: PluginDefinition = { authentication: { - name: 'windows', - label: 'NTLM Auth', - shortLabel: 'NTLM', + name: "windows", + label: "NTLM Auth", + shortLabel: "NTLM", args: [ { - type: 'banner', - color: 'info', + type: "banner", + color: "info", inputs: [ { - type: 'markdown', + type: "markdown", content: - 'NTLM is still in beta. Please submit any issues to [Feedback](https://yaak.app/feedback).', + "NTLM is still in beta. Please submit any issues to [Feedback](https://yaak.app/feedback).", }, ], }, { - type: 'text', - name: 'username', - label: 'Username', + type: "text", + name: "username", + label: "Username", optional: true, }, { - type: 'text', - name: 'password', - label: 'Password', + type: "text", + name: "password", + label: "Password", optional: true, password: true, }, { - type: 'accordion', - label: 'Advanced', + type: "accordion", + label: "Advanced", inputs: [ - { name: 'domain', label: 'Domain', type: 'text', optional: true }, - { name: 'workstation', label: 'Workstation', type: 'text', optional: true }, + { name: "domain", label: "Domain", type: "text", optional: true }, + { name: "workstation", label: "Workstation", type: "text", optional: true }, ], }, ], @@ -72,15 +72,15 @@ export const plugin: PluginDefinition = { method, url, headers: [ - { name: 'Authorization', value: type1 }, - { name: 'Connection', value: 'keep-alive' }, + { name: "Authorization", value: type1 }, + { name: "Connection", value: "keep-alive" }, ], }, }); const ntlmChallenge = extractNtlmChallenge(negotiateResponse.headers); if (ntlmChallenge == null) { - throw new Error('Unable to find NTLM challenge in WWW-Authenticate response headers'); + throw new Error("Unable to find NTLM challenge in WWW-Authenticate response headers"); } const type2 = ntlm.parseType2Message(ntlmChallenge, (err: Error | null) => { @@ -88,7 +88,7 @@ export const plugin: PluginDefinition = { }); const type3 = ntlm.createType3Message(type2, options); - return { setHeaders: [{ name: 'Authorization', value: type3 }] }; + return { setHeaders: [{ name: "Authorization", value: type3 }] }; }, }, }; diff --git a/plugins/auth-ntlm/src/modules.d.ts b/plugins/auth-ntlm/src/modules.d.ts index 0524954a..68a4916e 100644 --- a/plugins/auth-ntlm/src/modules.d.ts +++ b/plugins/auth-ntlm/src/modules.d.ts @@ -1 +1 @@ -declare module 'httpntlm'; +declare module "httpntlm"; diff --git a/plugins/auth-ntlm/tests/index.test.ts b/plugins/auth-ntlm/tests/index.test.ts index 0ee52069..c6c1b60b 100644 --- a/plugins/auth-ntlm/tests/index.test.ts +++ b/plugins/auth-ntlm/tests/index.test.ts @@ -1,5 +1,5 @@ -import type { Context } from '@yaakapp/api'; -import { beforeEach, describe, expect, test, vi } from 'vite-plus/test'; +import type { Context } from "@yaakapp/api"; +import { beforeEach, describe, expect, test, vi } from "vite-plus/test"; const ntlmMock = vi.hoisted(() => ({ createType1Message: vi.fn(), @@ -7,26 +7,26 @@ const ntlmMock = vi.hoisted(() => ({ createType3Message: vi.fn(), })); -vi.mock('httpntlm', () => ({ ntlm: ntlmMock })); +vi.mock("httpntlm", () => ({ ntlm: ntlmMock })); -import { plugin } from '../src'; +import { plugin } from "../src"; -describe('auth-ntlm', () => { +describe("auth-ntlm", () => { beforeEach(() => { ntlmMock.createType1Message.mockReset(); ntlmMock.parseType2Message.mockReset(); ntlmMock.createType3Message.mockReset(); - ntlmMock.createType1Message.mockReturnValue('NTLM TYPE1'); + ntlmMock.createType1Message.mockReturnValue("NTLM TYPE1"); // oxlint-disable-next-line no-explicit-any ntlmMock.parseType2Message.mockReturnValue({} as any); - ntlmMock.createType3Message.mockReturnValue('NTLM TYPE3'); + ntlmMock.createType3Message.mockReturnValue("NTLM TYPE3"); }); - test('uses NTLM challenge when Negotiate and NTLM headers are separate', async () => { + test("uses NTLM challenge when Negotiate and NTLM headers are separate", async () => { const send = vi.fn().mockResolvedValue({ headers: [ - { name: 'WWW-Authenticate', value: 'Negotiate' }, - { name: 'WWW-Authenticate', value: 'NTLM TlRMTVNTUAACAAAAAA==' }, + { name: "WWW-Authenticate", value: "Negotiate" }, + { name: "WWW-Authenticate", value: "NTLM TlRMTVNTUAACAAAAAA==" }, ], }); const ctx = { httpRequest: { send } } as unknown as Context; @@ -34,41 +34,41 @@ describe('auth-ntlm', () => { const result = await plugin.authentication?.onApply(ctx, { values: {}, headers: [], - url: 'https://example.local/resource', - method: 'GET', - contextId: 'ctx', + url: "https://example.local/resource", + method: "GET", + contextId: "ctx", }); expect(ntlmMock.parseType2Message).toHaveBeenCalledWith( - 'NTLM TlRMTVNTUAACAAAAAA==', + "NTLM TlRMTVNTUAACAAAAAA==", expect.any(Function), ); - expect(result).toEqual({ setHeaders: [{ name: 'Authorization', value: 'NTLM TYPE3' }] }); + expect(result).toEqual({ setHeaders: [{ name: "Authorization", value: "NTLM TYPE3" }] }); }); - test('uses NTLM challenge when auth schemes are comma-separated in one header', async () => { + test("uses NTLM challenge when auth schemes are comma-separated in one header", async () => { const send = vi.fn().mockResolvedValue({ - headers: [{ name: 'www-authenticate', value: 'Negotiate, NTLM TlRMTVNTUAACAAAAAA==' }], + headers: [{ name: "www-authenticate", value: "Negotiate, NTLM TlRMTVNTUAACAAAAAA==" }], }); const ctx = { httpRequest: { send } } as unknown as Context; await plugin.authentication?.onApply(ctx, { values: {}, headers: [], - url: 'https://example.local/resource', - method: 'GET', - contextId: 'ctx', + url: "https://example.local/resource", + method: "GET", + contextId: "ctx", }); expect(ntlmMock.parseType2Message).toHaveBeenCalledWith( - 'NTLM TlRMTVNTUAACAAAAAA==', + "NTLM TlRMTVNTUAACAAAAAA==", expect.any(Function), ); }); - test('throws a clear error when NTLM challenge is missing', async () => { + test("throws a clear error when NTLM challenge is missing", async () => { const send = vi.fn().mockResolvedValue({ - headers: [{ name: 'WWW-Authenticate', value: 'Negotiate' }], + headers: [{ name: "WWW-Authenticate", value: "Negotiate" }], }); const ctx = { httpRequest: { send } } as unknown as Context; @@ -76,10 +76,10 @@ describe('auth-ntlm', () => { plugin.authentication?.onApply(ctx, { values: {}, headers: [], - url: 'https://example.local/resource', - method: 'GET', - contextId: 'ctx', + url: "https://example.local/resource", + method: "GET", + contextId: "ctx", }), - ).rejects.toThrow('Unable to find NTLM challenge in WWW-Authenticate response headers'); + ).rejects.toThrow("Unable to find NTLM challenge in WWW-Authenticate response headers"); }); }); diff --git a/plugins/auth-oauth1/package.json b/plugins/auth-oauth1/package.json index 355346a2..25f40aab 100644 --- a/plugins/auth-oauth1/package.json +++ b/plugins/auth-oauth1/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-oauth1", "displayName": "OAuth 1.0", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using OAuth 1.0a", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-oauth1" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/auth-oauth1/src/index.ts b/plugins/auth-oauth1/src/index.ts index 85eb4a84..e104151e 100644 --- a/plugins/auth-oauth1/src/index.ts +++ b/plugins/auth-oauth1/src/index.ts @@ -1,26 +1,26 @@ -import crypto from 'node:crypto'; -import type { Context, GetHttpAuthenticationConfigRequest, PluginDefinition } from '@yaakapp/api'; -import OAuth from 'oauth-1.0a'; +import crypto from "node:crypto"; +import type { Context, GetHttpAuthenticationConfigRequest, PluginDefinition } from "@yaakapp/api"; +import OAuth from "oauth-1.0a"; const signatures = { - HMAC_SHA1: 'HMAC-SHA1', - HMAC_SHA256: 'HMAC-SHA256', - HMAC_SHA512: 'HMAC-SHA512', - RSA_SHA1: 'RSA-SHA1', - RSA_SHA256: 'RSA-SHA256', - RSA_SHA512: 'RSA-SHA512', - PLAINTEXT: 'PLAINTEXT', + HMAC_SHA1: "HMAC-SHA1", + HMAC_SHA256: "HMAC-SHA256", + HMAC_SHA512: "HMAC-SHA512", + RSA_SHA1: "RSA-SHA1", + RSA_SHA256: "RSA-SHA256", + RSA_SHA512: "RSA-SHA512", + PLAINTEXT: "PLAINTEXT", } as const; const defaultSig = signatures.HMAC_SHA1; -const pkSigs = Object.values(signatures).filter((k) => k.startsWith('RSA-')); +const pkSigs = Object.values(signatures).filter((k) => k.startsWith("RSA-")); const nonPkSigs = Object.values(signatures).filter((k) => !pkSigs.includes(k)); type SigMethod = (typeof signatures)[keyof typeof signatures]; function hiddenIfNot( sigMethod: SigMethod[], - ...other: ((values: GetHttpAuthenticationConfigRequest['values']) => boolean)[] + ...other: ((values: GetHttpAuthenticationConfigRequest["values"]) => boolean)[] ) { return (_ctx: Context, { values }: GetHttpAuthenticationConfigRequest) => { const hasGrantType = sigMethod.find((t) => t === String(values.signatureMethod ?? defaultSig)); @@ -32,78 +32,78 @@ function hiddenIfNot( export const plugin: PluginDefinition = { authentication: { - name: 'oauth1', - label: 'OAuth 1.0', - shortLabel: 'OAuth 1', + name: "oauth1", + label: "OAuth 1.0", + shortLabel: "OAuth 1", args: [ { - type: 'banner', - color: 'info', + type: "banner", + color: "info", inputs: [ { - type: 'markdown', + type: "markdown", content: - 'OAuth 1.0 is still in beta. Please submit any issues to [Feedback](https://yaak.app/feedback).', + "OAuth 1.0 is still in beta. Please submit any issues to [Feedback](https://yaak.app/feedback).", }, ], }, { - name: 'signatureMethod', - label: 'Signature Method', - type: 'select', + name: "signatureMethod", + label: "Signature Method", + type: "select", defaultValue: defaultSig, options: Object.values(signatures).map((v) => ({ label: v, value: v })), }, - { name: 'consumerKey', label: 'Consumer Key', type: 'text', password: true, optional: true }, + { name: "consumerKey", label: "Consumer Key", type: "text", password: true, optional: true }, { - name: 'consumerSecret', - label: 'Consumer Secret', - type: 'text', + name: "consumerSecret", + label: "Consumer Secret", + type: "text", password: true, optional: true, }, { - name: 'tokenKey', - label: 'Access Token', - type: 'text', + name: "tokenKey", + label: "Access Token", + type: "text", password: true, optional: true, }, { - name: 'tokenSecret', - label: 'Token Secret', - type: 'text', + name: "tokenSecret", + label: "Token Secret", + type: "text", password: true, optional: true, dynamic: hiddenIfNot(nonPkSigs), }, { - name: 'privateKey', - label: 'Private Key (RSA-SHA1)', - type: 'text', + name: "privateKey", + label: "Private Key (RSA-SHA1)", + type: "text", multiLine: true, optional: true, password: true, placeholder: - '-----BEGIN RSA PRIVATE KEY-----\nPrivate key in PEM format\n-----END RSA PRIVATE KEY-----', + "-----BEGIN RSA PRIVATE KEY-----\nPrivate key in PEM format\n-----END RSA PRIVATE KEY-----", dynamic: hiddenIfNot(pkSigs), }, { - type: 'accordion', - label: 'Advanced', + type: "accordion", + label: "Advanced", inputs: [ - { name: 'callback', label: 'Callback Url', type: 'text', optional: true }, - { name: 'verifier', label: 'Verifier', type: 'text', optional: true, password: true }, - { name: 'timestamp', label: 'Timestamp', type: 'text', optional: true }, - { name: 'nonce', label: 'Nonce', type: 'text', optional: true }, + { name: "callback", label: "Callback Url", type: "text", optional: true }, + { name: "verifier", label: "Verifier", type: "text", optional: true, password: true }, + { name: "timestamp", label: "Timestamp", type: "text", optional: true }, + { name: "nonce", label: "Nonce", type: "text", optional: true }, { - name: 'version', - label: 'OAuth Version', - type: 'text', + name: "version", + label: "OAuth Version", + type: "text", optional: true, - defaultValue: '1.0', + defaultValue: "1.0", }, - { name: 'realm', label: 'Realm', type: 'text', optional: true }, + { name: "realm", label: "Realm", type: "text", optional: true }, ], }, ], @@ -115,12 +115,12 @@ export const plugin: PluginDefinition = { setHeaders?: { name: string; value: string }[]; setQueryParameters?: { name: string; value: string }[]; } { - const consumerKey = String(values.consumerKey || ''); - const consumerSecret = String(values.consumerSecret || ''); + const consumerKey = String(values.consumerKey || ""); + const consumerSecret = String(values.consumerSecret || ""); const signatureMethod = String(values.signatureMethod || signatures.HMAC_SHA1) as SigMethod; - const version = String(values.version || '1.0'); - const realm = String(values.realm || '') || undefined; + const version = String(values.version || "1.0"); + const realm = String(values.realm || "") || undefined; const oauth = new OAuth({ consumer: { key: consumerKey, secret: consumerSecret }, @@ -131,13 +131,13 @@ export const plugin: PluginDefinition = { }); if (pkSigs.includes(signatureMethod)) { - oauth.getSigningKey = (tokenSecret?: string) => tokenSecret || ''; + oauth.getSigningKey = (tokenSecret?: string) => tokenSecret || ""; } const requestUrl = new URL(url); // Base request options passed to oauth-1.0a - const requestData: Omit & { + const requestData: Omit & { data: Record; } = { method, @@ -148,7 +148,7 @@ export const plugin: PluginDefinition = { // (1) Include existing query params in signature base string for (const key of requestUrl.searchParams.keys()) { - if (key.startsWith('oauth_')) continue; + if (key.startsWith("oauth_")) continue; const all = requestUrl.searchParams.getAll(key); const first = all[0]; if (first == null) continue; @@ -165,8 +165,8 @@ export const plugin: PluginDefinition = { if (pkSigs.includes(signatureMethod)) { token = { - key: String(values.tokenKey || ''), - secret: String(values.privateKey || ''), + key: String(values.tokenKey || ""), + secret: String(values.privateKey || ""), }; } else if (values.tokenKey && values.tokenSecret) { token = { key: String(values.tokenKey), secret: String(values.tokenSecret) }; @@ -176,7 +176,7 @@ export const plugin: PluginDefinition = { const authParams = oauth.authorize(requestData, token as OAuth.Token | undefined); const { Authorization } = oauth.toHeader(authParams); - return { setHeaders: [{ name: 'Authorization', value: Authorization }] }; + return { setHeaders: [{ name: "Authorization", value: Authorization }] }; }, }, }; @@ -185,26 +185,26 @@ function hashFunction(signatureMethod: SigMethod) { switch (signatureMethod) { case signatures.HMAC_SHA1: return (base: string, key: string) => - crypto.createHmac('sha1', key).update(base).digest('base64'); + crypto.createHmac("sha1", key).update(base).digest("base64"); case signatures.HMAC_SHA256: return (base: string, key: string) => - crypto.createHmac('sha256', key).update(base).digest('base64'); + crypto.createHmac("sha256", key).update(base).digest("base64"); case signatures.HMAC_SHA512: return (base: string, key: string) => - crypto.createHmac('sha512', key).update(base).digest('base64'); + crypto.createHmac("sha512", key).update(base).digest("base64"); case signatures.RSA_SHA1: return (base: string, privateKey: string) => - crypto.createSign('RSA-SHA1').update(base).sign(privateKey, 'base64'); + crypto.createSign("RSA-SHA1").update(base).sign(privateKey, "base64"); case signatures.RSA_SHA256: return (base: string, privateKey: string) => - crypto.createSign('RSA-SHA256').update(base).sign(privateKey, 'base64'); + crypto.createSign("RSA-SHA256").update(base).sign(privateKey, "base64"); case signatures.RSA_SHA512: return (base: string, privateKey: string) => - crypto.createSign('RSA-SHA512').update(base).sign(privateKey, 'base64'); + crypto.createSign("RSA-SHA512").update(base).sign(privateKey, "base64"); case signatures.PLAINTEXT: return (base: string) => base; default: return (base: string, key: string) => - crypto.createHmac('sha1', key).update(base).digest('base64'); + crypto.createHmac("sha1", key).update(base).digest("base64"); } } diff --git a/plugins/auth-oauth2/README.md b/plugins/auth-oauth2/README.md index 6fe41a17..773a949c 100644 --- a/plugins/auth-oauth2/README.md +++ b/plugins/auth-oauth2/README.md @@ -25,7 +25,7 @@ The most secure and commonly used OAuth 2.0 flow for web applications. ### Client Credentials Flow -Ideal for server-to-server authentication where no user interaction is required. +Ideal for server-to-server authentication where no user interaction is required. ### Implicit Flow diff --git a/plugins/auth-oauth2/package.json b/plugins/auth-oauth2/package.json index 8238cab4..f36330cf 100644 --- a/plugins/auth-oauth2/package.json +++ b/plugins/auth-oauth2/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/auth-oauth2", "displayName": "OAuth 2.0", + "version": "0.1.0", + "private": true, "description": "Authenticate requests using OAuth 2.0", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/auth-oauth2" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/auth-oauth2/src/callbackServer.ts b/plugins/auth-oauth2/src/callbackServer.ts index c0b0ef3c..b40b476b 100644 --- a/plugins/auth-oauth2/src/callbackServer.ts +++ b/plugins/auth-oauth2/src/callbackServer.ts @@ -1,8 +1,8 @@ -import type { IncomingMessage, ServerResponse } from 'node:http'; -import http from 'node:http'; -import type { Context } from '@yaakapp/api'; +import type { IncomingMessage, ServerResponse } from "node:http"; +import http from "node:http"; +import type { Context } from "@yaakapp/api"; -export const HOSTED_CALLBACK_URL_BASE = 'https://oauth.yaak.app/redirect'; +export const HOSTED_CALLBACK_URL_BASE = "https://oauth.yaak.app/redirect"; export const DEFAULT_LOCALHOST_PORT = 8765; const CALLBACK_TIMEOUT_MS = 5 * 60 * 1000; // 5 minutes @@ -36,12 +36,12 @@ export function startCallbackServer(options: { }): Promise { // Stop any previously active server before starting a new one if (activeServer) { - console.log('[oauth2] Stopping previous callback server before starting new one'); + console.log("[oauth2] Stopping previous callback server before starting new one"); activeServer.stop(); activeServer = null; } - const { port = 0, path = '/callback', timeoutMs = CALLBACK_TIMEOUT_MS } = options; + const { port = 0, path = "/callback", timeoutMs = CALLBACK_TIMEOUT_MS } = options; return new Promise((resolve, reject) => { let callbackResolve: ((url: string) => void) | null = null; @@ -50,33 +50,33 @@ export function startCallbackServer(options: { let stopped = false; const server = http.createServer((req: IncomingMessage, res: ServerResponse) => { - const reqUrl = new URL(req.url ?? '/', `http://${req.headers.host}`); + const reqUrl = new URL(req.url ?? "/", `http://${req.headers.host}`); // Only handle the callback path if (reqUrl.pathname !== path && reqUrl.pathname !== `${path}/`) { - res.writeHead(404, { 'Content-Type': 'text/plain' }); - res.end('Not Found'); + res.writeHead(404, { "Content-Type": "text/plain" }); + res.end("Not Found"); return; } - if (req.method === 'POST') { + if (req.method === "POST") { // POST: read JSON body with the final callback URL and resolve - let body = ''; - req.on('data', (chunk: Buffer) => { + let body = ""; + req.on("data", (chunk: Buffer) => { body += chunk.toString(); }); - req.on('end', () => { + req.on("end", () => { try { const { url: callbackUrl } = JSON.parse(body); - if (!callbackUrl || typeof callbackUrl !== 'string') { - res.writeHead(400, { 'Content-Type': 'text/plain' }); - res.end('Missing url in request body'); + if (!callbackUrl || typeof callbackUrl !== "string") { + res.writeHead(400, { "Content-Type": "text/plain" }); + res.end("Missing url in request body"); return; } // Send success response - res.writeHead(200, { 'Content-Type': 'text/plain' }); - res.end('OK'); + res.writeHead(200, { "Content-Type": "text/plain" }); + res.end("OK"); // Resolve the callback promise if (callbackResolve) { @@ -88,19 +88,19 @@ export function startCallbackServer(options: { // Stop the server after a short delay to ensure response is sent setTimeout(() => stopServer(), 100); } catch { - res.writeHead(400, { 'Content-Type': 'text/plain' }); - res.end('Invalid JSON'); + res.writeHead(400, { "Content-Type": "text/plain" }); + res.end("Invalid JSON"); } }); return; } // GET: serve intermediate page that reads the fragment and POSTs back - res.writeHead(200, { 'Content-Type': 'text/html' }); + res.writeHead(200, { "Content-Type": "text/html" }); res.end(getFragmentForwardingHtml()); }); - server.on('error', (err: Error) => { + server.on("error", (err: Error) => { if (!stopped) { reject(err); } @@ -123,16 +123,16 @@ export function startCallbackServer(options: { server.close(); if (callbackReject) { - callbackReject(new Error('Callback server stopped')); + callbackReject(new Error("Callback server stopped")); callbackResolve = null; callbackReject = null; } }; - server.listen(port, '127.0.0.1', () => { + server.listen(port, "127.0.0.1", () => { const address = server.address(); - if (!address || typeof address === 'string') { - reject(new Error('Failed to get server address')); + if (!address || typeof address === "string") { + reject(new Error("Failed to get server address")); return; } @@ -147,7 +147,7 @@ export function startCallbackServer(options: { waitForCallback: () => { return new Promise((res, rej) => { if (stopped) { - rej(new Error('Callback server already stopped')); + rej(new Error("Callback server already stopped")); return; } @@ -157,7 +157,7 @@ export function startCallbackServer(options: { // Set timeout timeoutHandle = setTimeout(() => { if (callbackReject) { - callbackReject(new Error('Authorization timed out')); + callbackReject(new Error("Authorization timed out")); callbackResolve = null; callbackReject = null; } @@ -193,7 +193,7 @@ export function buildHostedCallbackRedirectUri(localPort: number): string { */ export function stopActiveServer(): void { if (activeServer) { - console.log('[oauth2] Stopping active callback server during dispose'); + console.log("[oauth2] Stopping active callback server during dispose"); activeServer.stop(); activeServer = null; } @@ -210,7 +210,7 @@ export async function getRedirectUrlViaExternalBrowser( ctx: Context, authorizationUrl: URL, options: { - callbackType: 'localhost' | 'hosted'; + callbackType: "localhost" | "hosted"; callbackPort?: number; }, ): Promise<{ callbackUrl: string; redirectUri: string }> { @@ -222,31 +222,31 @@ export async function getRedirectUrlViaExternalBrowser( const server = await startCallbackServer({ port, - path: '/callback', + path: "/callback", }); try { // Determine the redirect URI to send to the OAuth provider let oauthRedirectUri: string; - if (callbackType === 'hosted') { + if (callbackType === "hosted") { oauthRedirectUri = buildHostedCallbackRedirectUri(server.port); - console.log('[oauth2] Using hosted callback redirect:', oauthRedirectUri); + console.log("[oauth2] Using hosted callback redirect:", oauthRedirectUri); } else { oauthRedirectUri = server.redirectUri; - console.log('[oauth2] Using localhost callback redirect:', oauthRedirectUri); + console.log("[oauth2] Using localhost callback redirect:", oauthRedirectUri); } // Set the redirect URI on the authorization URL - authorizationUrl.searchParams.set('redirect_uri', oauthRedirectUri); + authorizationUrl.searchParams.set("redirect_uri", oauthRedirectUri); const authorizationUrlStr = authorizationUrl.toString(); - console.log('[oauth2] Opening external browser:', authorizationUrlStr); + console.log("[oauth2] Opening external browser:", authorizationUrlStr); // Show toast to inform user await ctx.toast.show({ - message: 'Opening browser for authorization...', - icon: 'info', + message: "Opening browser for authorization...", + icon: "info", timeout: 3000, }); @@ -254,10 +254,10 @@ export async function getRedirectUrlViaExternalBrowser( await ctx.window.openExternalUrl(authorizationUrlStr); // Wait for the callback - console.log('[oauth2] Waiting for callback on', server.redirectUri); + console.log("[oauth2] Waiting for callback on", server.redirectUri); const callbackUrl = await server.waitForCallback(); - console.log('[oauth2] Received callback:', callbackUrl); + console.log("[oauth2] Received callback:", callbackUrl); return { callbackUrl, redirectUri: oauthRedirectUri }; } finally { diff --git a/plugins/auth-oauth2/src/fetchAccessToken.ts b/plugins/auth-oauth2/src/fetchAccessToken.ts index 05b524e8..ccd28f26 100644 --- a/plugins/auth-oauth2/src/fetchAccessToken.ts +++ b/plugins/auth-oauth2/src/fetchAccessToken.ts @@ -1,6 +1,6 @@ -import { readFileSync } from 'node:fs'; -import type { Context, HttpRequest, HttpUrlParameter } from '@yaakapp/api'; -import type { AccessTokenRawResponse } from './store'; +import { readFileSync } from "node:fs"; +import type { Context, HttpRequest, HttpUrlParameter } from "@yaakapp/api"; +import type { AccessTokenRawResponse } from "./store"; export async function fetchAccessToken( ctx: Context, @@ -14,58 +14,58 @@ export async function fetchAccessToken( } & ({ clientAssertion: string } | { clientSecret: string; credentialsInBody: boolean }), ): Promise { const { clientId, grantType, accessTokenUrl, scope, audience, params } = args; - console.log('[oauth2] Getting access token', accessTokenUrl); + console.log("[oauth2] Getting access token", accessTokenUrl); const httpRequest: Partial = { - method: 'POST', + method: "POST", url: accessTokenUrl, - bodyType: 'application/x-www-form-urlencoded', + bodyType: "application/x-www-form-urlencoded", body: { - form: [{ name: 'grant_type', value: grantType }, ...params], + form: [{ name: "grant_type", value: grantType }, ...params], }, headers: [ - { name: 'User-Agent', value: 'yaak' }, + { name: "User-Agent", value: "yaak" }, { - name: 'Accept', - value: 'application/x-www-form-urlencoded, application/json', + name: "Accept", + value: "application/x-www-form-urlencoded, application/json", }, - { name: 'Content-Type', value: 'application/x-www-form-urlencoded' }, + { name: "Content-Type", value: "application/x-www-form-urlencoded" }, ], }; - if (scope) httpRequest.body?.form.push({ name: 'scope', value: scope }); - if (audience) httpRequest.body?.form.push({ name: 'audience', value: audience }); + if (scope) httpRequest.body?.form.push({ name: "scope", value: scope }); + if (audience) httpRequest.body?.form.push({ name: "audience", value: audience }); - if ('clientAssertion' in args) { - httpRequest.body?.form.push({ name: 'client_id', value: clientId }); + if ("clientAssertion" in args) { + httpRequest.body?.form.push({ name: "client_id", value: clientId }); httpRequest.body?.form.push({ - name: 'client_assertion_type', - value: 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer', + name: "client_assertion_type", + value: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer", }); httpRequest.body?.form.push({ - name: 'client_assertion', + name: "client_assertion", value: args.clientAssertion, }); } else if (args.credentialsInBody) { - httpRequest.body?.form.push({ name: 'client_id', value: clientId }); + httpRequest.body?.form.push({ name: "client_id", value: clientId }); httpRequest.body?.form.push({ - name: 'client_secret', + name: "client_secret", value: args.clientSecret, }); } else { - const value = `Basic ${Buffer.from(`${clientId}:${args.clientSecret}`).toString('base64')}`; - httpRequest.headers?.push({ name: 'Authorization', value }); + const value = `Basic ${Buffer.from(`${clientId}:${args.clientSecret}`).toString("base64")}`; + httpRequest.headers?.push({ name: "Authorization", value }); } - httpRequest.authenticationType = 'none'; // Don't inherit workspace auth + httpRequest.authenticationType = "none"; // Don't inherit workspace auth const resp = await ctx.httpRequest.send({ httpRequest }); - console.log('[oauth2] Got access token response', resp.status); + console.log("[oauth2] Got access token response", resp.status); if (resp.error) { throw new Error(`Failed to fetch access token: ${resp.error}`); } - const body = resp.bodyPath ? readFileSync(resp.bodyPath, 'utf8') : ''; + const body = resp.bodyPath ? readFileSync(resp.bodyPath, "utf8") : ""; if (resp.status < 200 || resp.status >= 300) { throw new Error(`Failed to fetch access token with status=${resp.status} and body=${body}`); diff --git a/plugins/auth-oauth2/src/getOrRefreshAccessToken.ts b/plugins/auth-oauth2/src/getOrRefreshAccessToken.ts index be643b59..e63ce5f8 100644 --- a/plugins/auth-oauth2/src/getOrRefreshAccessToken.ts +++ b/plugins/auth-oauth2/src/getOrRefreshAccessToken.ts @@ -1,8 +1,8 @@ -import { readFileSync } from 'node:fs'; -import type { Context, HttpRequest } from '@yaakapp/api'; -import type { AccessToken, AccessTokenRawResponse, TokenStoreArgs } from './store'; -import { deleteToken, getToken, storeToken } from './store'; -import { isTokenExpired } from './util'; +import { readFileSync } from "node:fs"; +import type { Context, HttpRequest } from "@yaakapp/api"; +import type { AccessToken, AccessTokenRawResponse, TokenStoreArgs } from "./store"; +import { deleteToken, getToken, storeToken } from "./store"; +import { isTokenExpired } from "./util"; export async function getOrRefreshAccessToken( ctx: Context, @@ -42,33 +42,33 @@ export async function getOrRefreshAccessToken( // Access token is expired, so get a new one const httpRequest: Partial = { - method: 'POST', + method: "POST", url: accessTokenUrl, - bodyType: 'application/x-www-form-urlencoded', + bodyType: "application/x-www-form-urlencoded", body: { form: [ - { name: 'grant_type', value: 'refresh_token' }, - { name: 'refresh_token', value: token.response.refresh_token }, + { name: "grant_type", value: "refresh_token" }, + { name: "refresh_token", value: token.response.refresh_token }, ], }, headers: [ - { name: 'User-Agent', value: 'yaak' }, - { name: 'Accept', value: 'application/x-www-form-urlencoded, application/json' }, - { name: 'Content-Type', value: 'application/x-www-form-urlencoded' }, + { name: "User-Agent", value: "yaak" }, + { name: "Accept", value: "application/x-www-form-urlencoded, application/json" }, + { name: "Content-Type", value: "application/x-www-form-urlencoded" }, ], }; - if (scope) httpRequest.body?.form.push({ name: 'scope', value: scope }); + if (scope) httpRequest.body?.form.push({ name: "scope", value: scope }); if (credentialsInBody) { - httpRequest.body?.form.push({ name: 'client_id', value: clientId }); - httpRequest.body?.form.push({ name: 'client_secret', value: clientSecret }); + httpRequest.body?.form.push({ name: "client_id", value: clientId }); + httpRequest.body?.form.push({ name: "client_secret", value: clientSecret }); } else { - const value = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString('base64')}`; - httpRequest.headers?.push({ name: 'Authorization', value }); + const value = `Basic ${Buffer.from(`${clientId}:${clientSecret}`).toString("base64")}`; + httpRequest.headers?.push({ name: "Authorization", value }); } - httpRequest.authenticationType = 'none'; // Don't inherit workspace auth + httpRequest.authenticationType = "none"; // Don't inherit workspace auth const resp = await ctx.httpRequest.send({ httpRequest }); if (resp.error) { @@ -78,14 +78,14 @@ export async function getOrRefreshAccessToken( if (resp.status >= 400 && resp.status < 500) { // Client errors (4xx) indicate the refresh token is invalid, expired, or revoked // Delete the token and return null to trigger a fresh authorization flow - console.log('[oauth2] Refresh token request failed with client error, deleting token'); + console.log("[oauth2] Refresh token request failed with client error, deleting token"); await deleteToken(ctx, tokenArgs); return null; } - const body = resp.bodyPath ? readFileSync(resp.bodyPath, 'utf8') : ''; + const body = resp.bodyPath ? readFileSync(resp.bodyPath, "utf8") : ""; - console.log('[oauth2] Got refresh token response', resp.status); + console.log("[oauth2] Got refresh token response", resp.status); if (resp.status < 200 || resp.status >= 300) { throw new Error(`Failed to refresh access token with status=${resp.status} and body=${body}`); diff --git a/plugins/auth-oauth2/src/grants/authorizationCode.ts b/plugins/auth-oauth2/src/grants/authorizationCode.ts index 3138141f..220a1002 100644 --- a/plugins/auth-oauth2/src/grants/authorizationCode.ts +++ b/plugins/auth-oauth2/src/grants/authorizationCode.ts @@ -1,17 +1,17 @@ -import { createHash, randomBytes } from 'node:crypto'; -import type { Context } from '@yaakapp/api'; -import { getRedirectUrlViaExternalBrowser } from '../callbackServer'; -import { fetchAccessToken } from '../fetchAccessToken'; -import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken'; -import type { AccessToken, TokenStoreArgs } from '../store'; -import { getDataDirKey, storeToken } from '../store'; -import { extractCode } from '../util'; +import { createHash, randomBytes } from "node:crypto"; +import type { Context } from "@yaakapp/api"; +import { getRedirectUrlViaExternalBrowser } from "../callbackServer"; +import { fetchAccessToken } from "../fetchAccessToken"; +import { getOrRefreshAccessToken } from "../getOrRefreshAccessToken"; +import type { AccessToken, TokenStoreArgs } from "../store"; +import { getDataDirKey, storeToken } from "../store"; +import { extractCode } from "../util"; -export const PKCE_SHA256 = 'S256'; -export const PKCE_PLAIN = 'plain'; +export const PKCE_SHA256 = "S256"; +export const PKCE_PLAIN = "plain"; export const DEFAULT_PKCE_METHOD = PKCE_SHA256; -export type CallbackType = 'localhost' | 'hosted'; +export type CallbackType = "localhost" | "hosted"; export interface ExternalBrowserOptions { useExternalBrowser: boolean; @@ -50,7 +50,7 @@ export async function getAuthorizationCode( challengeMethod: string; codeVerifier: string; } | null; - tokenName: 'access_token' | 'id_token'; + tokenName: "access_token" | "id_token"; externalBrowser?: ExternalBrowserOptions; }, ): Promise { @@ -74,21 +74,21 @@ export async function getAuthorizationCode( let authorizationUrl: URL; try { - authorizationUrl = new URL(`${authorizationUrlRaw ?? ''}`); + authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`); } catch { throw new Error(`Invalid authorization URL "${authorizationUrlRaw}"`); } - authorizationUrl.searchParams.set('response_type', 'code'); - authorizationUrl.searchParams.set('client_id', clientId); - if (scope) authorizationUrl.searchParams.set('scope', scope); - if (state) authorizationUrl.searchParams.set('state', state); - if (audience) authorizationUrl.searchParams.set('audience', audience); + authorizationUrl.searchParams.set("response_type", "code"); + authorizationUrl.searchParams.set("client_id", clientId); + if (scope) authorizationUrl.searchParams.set("scope", scope); + if (state) authorizationUrl.searchParams.set("state", state); + if (audience) authorizationUrl.searchParams.set("audience", audience); if (pkce) { authorizationUrl.searchParams.set( - 'code_challenge', + "code_challenge", pkceCodeChallenge(pkce.codeVerifier, pkce.challengeMethod), ); - authorizationUrl.searchParams.set('code_challenge_method', pkce.challengeMethod); + authorizationUrl.searchParams.set("code_challenge_method", pkce.challengeMethod); } let code: string; @@ -103,21 +103,21 @@ export async function getAuthorizationCode( // Pass null to skip redirect URI matching — the callback came from our own local server const extractedCode = extractCode(result.callbackUrl, null); if (!extractedCode) { - throw new Error('No authorization code found in callback URL'); + throw new Error("No authorization code found in callback URL"); } code = extractedCode; actualRedirectUri = result.redirectUri; } else { // Use embedded browser flow (original behavior) if (redirectUri) { - authorizationUrl.searchParams.set('redirect_uri', redirectUri); + authorizationUrl.searchParams.set("redirect_uri", redirectUri); } code = await getCodeViaEmbeddedBrowser(ctx, contextId, authorizationUrl, redirectUri); } - console.log('[oauth2] Code found'); + console.log("[oauth2] Code found"); const response = await fetchAccessToken(ctx, { - grantType: 'authorization_code', + grantType: "authorization_code", accessTokenUrl, clientId, clientSecret, @@ -125,9 +125,9 @@ export async function getAuthorizationCode( audience, credentialsInBody, params: [ - { name: 'code', value: code }, - ...(pkce ? [{ name: 'code_verifier', value: pkce.codeVerifier }] : []), - ...(actualRedirectUri ? [{ name: 'redirect_uri', value: actualRedirectUri }] : []), + { name: "code", value: code }, + ...(pkce ? [{ name: "code_verifier", value: pkce.codeVerifier }] : []), + ...(actualRedirectUri ? [{ name: "redirect_uri", value: actualRedirectUri }] : []), ], }); @@ -146,7 +146,7 @@ async function getCodeViaEmbeddedBrowser( ): Promise { const dataDirKey = await getDataDirKey(ctx, contextId); const authorizationUrlStr = authorizationUrl.toString(); - console.log('[oauth2] Authorizing via embedded browser', authorizationUrlStr); + console.log("[oauth2] Authorizing via embedded browser", authorizationUrlStr); // oxlint-disable-next-line no-async-promise-executor -- Required for this pattern return new Promise(async (resolve, reject) => { @@ -154,10 +154,10 @@ async function getCodeViaEmbeddedBrowser( const { close } = await ctx.window.openUrl({ dataDirKey, url: authorizationUrlStr, - label: 'oauth-authorization-url', + label: "oauth-authorization-url", async onClose() { if (!foundCode) { - reject(new Error('Authorization window closed')); + reject(new Error("Authorization window closed")); } }, async onNavigate({ url: urlStr }) { @@ -187,21 +187,21 @@ export function genPkceCodeVerifier() { } function pkceCodeChallenge(verifier: string, method: string) { - if (method === 'plain') { + if (method === "plain") { return verifier; } - const hash = encodeForPkce(createHash('sha256').update(verifier).digest()); + const hash = encodeForPkce(createHash("sha256").update(verifier).digest()); return hash - .replace(/=/g, '') // Remove padding '=' - .replace(/\+/g, '-') // Replace '+' with '-' - .replace(/\//g, '_'); // Replace '/' with '_' + .replace(/=/g, "") // Remove padding '=' + .replace(/\+/g, "-") // Replace '+' with '-' + .replace(/\//g, "_"); // Replace '/' with '_' } function encodeForPkce(bytes: Buffer) { return bytes - .toString('base64') - .replace(/=/g, '') // Remove padding '=' - .replace(/\+/g, '-') // Replace '+' with '-' - .replace(/\//g, '_'); // Replace '/' with '_' + .toString("base64") + .replace(/=/g, "") // Remove padding '=' + .replace(/\+/g, "-") // Replace '+' with '-' + .replace(/\//g, "_"); // Replace '/' with '_' } diff --git a/plugins/auth-oauth2/src/grants/clientCredentials.ts b/plugins/auth-oauth2/src/grants/clientCredentials.ts index a0dca5f6..d8b34fa8 100644 --- a/plugins/auth-oauth2/src/grants/clientCredentials.ts +++ b/plugins/auth-oauth2/src/grants/clientCredentials.ts @@ -1,25 +1,25 @@ -import { createPrivateKey, randomUUID } from 'node:crypto'; -import type { Context } from '@yaakapp/api'; -import jwt, { type Algorithm } from 'jsonwebtoken'; -import { fetchAccessToken } from '../fetchAccessToken'; -import type { TokenStoreArgs } from '../store'; -import { getToken, storeToken } from '../store'; -import { isTokenExpired } from '../util'; +import { createPrivateKey, randomUUID } from "node:crypto"; +import type { Context } from "@yaakapp/api"; +import jwt, { type Algorithm } from "jsonwebtoken"; +import { fetchAccessToken } from "../fetchAccessToken"; +import type { TokenStoreArgs } from "../store"; +import { getToken, storeToken } from "../store"; +import { isTokenExpired } from "../util"; export const jwtAlgorithms = [ - 'HS256', - 'HS384', - 'HS512', - 'RS256', - 'RS384', - 'RS512', - 'PS256', - 'PS384', - 'PS512', - 'ES256', - 'ES384', - 'ES512', - 'none', + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "PS256", + "PS384", + "PS512", + "ES256", + "ES384", + "ES512", + "none", ] as const; export const defaultJwtAlgorithm = jwtAlgorithms[0]; @@ -40,7 +40,7 @@ function buildClientAssertionJwt(params: { }): string { const { clientId, accessTokenUrl, secret, algorithm } = params; - const isHmac = algorithm.startsWith('HS') || algorithm === 'none'; + const isHmac = algorithm.startsWith("HS") || algorithm === "none"; // Resolve the signing key depending on format let signingKey: jwt.Secret; @@ -51,25 +51,25 @@ function buildClientAssertionJwt(params: { if (isHmac) { // HMAC algorithms use the raw secret (string or Buffer) signingKey = secret; - } else if (trimmed.startsWith('{')) { + } else if (trimmed.startsWith("{")) { // Looks like JSON - treat as JWK. There is surely a better way to detect JWK vs a raw secret, but this should work in most cases. // oxlint-disable-next-line no-explicit-any let jwk: any; try { jwk = JSON.parse(trimmed); } catch { - throw new Error('Client Assertion secret looks like JSON but is not valid'); + throw new Error("Client Assertion secret looks like JSON but is not valid"); } kid = jwk?.kid; - signingKey = createPrivateKey({ key: jwk, format: 'jwk' }); - } else if (trimmed.startsWith('-----')) { + signingKey = createPrivateKey({ key: jwk, format: "jwk" }); + } else if (trimmed.startsWith("-----")) { // PEM-encoded key - signingKey = createPrivateKey({ key: trimmed, format: 'pem' }); + signingKey = createPrivateKey({ key: trimmed, format: "pem" }); } else { throw new Error( - 'Client Assertion secret must be a JWK JSON object, a PEM-encoded key ' + - '(starting with -----), or a raw secret for HMAC algorithms.', + "Client Assertion secret must be a JWK JSON object, a PEM-encoded key " + + "(starting with -----), or a raw secret for HMAC algorithms.", ); } @@ -84,7 +84,7 @@ function buildClientAssertionJwt(params: { }; // Build the JWT header; include "kid" when available - const header: jwt.JwtHeader = { alg: algorithm, typ: 'JWT' }; + const header: jwt.JwtHeader = { alg: algorithm, typ: "JWT" }; if (kid) { header.kid = kid; } @@ -135,9 +135,9 @@ export async function getClientCredentials( const common: Omit< Parameters[1], - 'clientAssertion' | 'clientSecret' | 'credentialsInBody' + "clientAssertion" | "clientSecret" | "credentialsInBody" > = { - grantType: 'client_credentials', + grantType: "client_credentials", accessTokenUrl, audience, clientId, @@ -146,7 +146,7 @@ export async function getClientCredentials( }; const fetchParams: Parameters[1] = - clientCredentialsMethod === 'client_assertion' + clientCredentialsMethod === "client_assertion" ? { ...common, clientAssertion: buildClientAssertionJwt({ @@ -154,7 +154,7 @@ export async function getClientCredentials( algorithm: clientAssertionAlgorithm as Algorithm, accessTokenUrl, secret: clientAssertionSecretBase64 - ? Buffer.from(clientAssertionSecret, 'base64').toString('utf-8') + ? Buffer.from(clientAssertionSecret, "base64").toString("utf-8") : clientAssertionSecret, }), } diff --git a/plugins/auth-oauth2/src/grants/implicit.ts b/plugins/auth-oauth2/src/grants/implicit.ts index 5b24e562..af0cd06a 100644 --- a/plugins/auth-oauth2/src/grants/implicit.ts +++ b/plugins/auth-oauth2/src/grants/implicit.ts @@ -1,9 +1,9 @@ -import type { Context } from '@yaakapp/api'; -import { getRedirectUrlViaExternalBrowser } from '../callbackServer'; -import type { AccessToken, AccessTokenRawResponse } from '../store'; -import { getDataDirKey, getToken, storeToken } from '../store'; -import { isTokenExpired } from '../util'; -import type { ExternalBrowserOptions } from './authorizationCode'; +import type { Context } from "@yaakapp/api"; +import { getRedirectUrlViaExternalBrowser } from "../callbackServer"; +import type { AccessToken, AccessTokenRawResponse } from "../store"; +import { getDataDirKey, getToken, storeToken } from "../store"; +import { isTokenExpired } from "../util"; +import type { ExternalBrowserOptions } from "./authorizationCode"; export async function getImplicit( ctx: Context, @@ -26,7 +26,7 @@ export async function getImplicit( scope: string | null; state: string | null; audience: string | null; - tokenName: 'access_token' | 'id_token'; + tokenName: "access_token" | "id_token"; externalBrowser?: ExternalBrowserOptions; }, ): Promise { @@ -43,18 +43,18 @@ export async function getImplicit( let authorizationUrl: URL; try { - authorizationUrl = new URL(`${authorizationUrlRaw ?? ''}`); + authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`); } catch { throw new Error(`Invalid authorization URL "${authorizationUrlRaw}"`); } - authorizationUrl.searchParams.set('response_type', responseType); - authorizationUrl.searchParams.set('client_id', clientId); - if (scope) authorizationUrl.searchParams.set('scope', scope); - if (state) authorizationUrl.searchParams.set('state', state); - if (audience) authorizationUrl.searchParams.set('audience', audience); - if (responseType.includes('id_token')) { + authorizationUrl.searchParams.set("response_type", responseType); + authorizationUrl.searchParams.set("client_id", clientId); + if (scope) authorizationUrl.searchParams.set("scope", scope); + if (state) authorizationUrl.searchParams.set("state", state); + if (audience) authorizationUrl.searchParams.set("audience", audience); + if (responseType.includes("id_token")) { authorizationUrl.searchParams.set( - 'nonce', + "nonce", String(Math.floor(Math.random() * 9999999999999) + 1), ); } @@ -71,7 +71,7 @@ export async function getImplicit( } else { // Use embedded browser flow (original behavior) if (redirectUri) { - authorizationUrl.searchParams.set('redirect_uri', redirectUri); + authorizationUrl.searchParams.set("redirect_uri", redirectUri); } newToken = await getTokenViaEmbeddedBrowser( ctx, @@ -99,11 +99,11 @@ async function getTokenViaEmbeddedBrowser( accessTokenUrl: null; authorizationUrl: string; }, - tokenName: 'access_token' | 'id_token', + tokenName: "access_token" | "id_token", ): Promise { const dataDirKey = await getDataDirKey(ctx, contextId); const authorizationUrlStr = authorizationUrl.toString(); - console.log('[oauth2] Authorizing via embedded browser (implicit)', authorizationUrlStr); + console.log("[oauth2] Authorizing via embedded browser (implicit)", authorizationUrlStr); // oxlint-disable-next-line no-async-promise-executor -- Required for this pattern return new Promise(async (resolve, reject) => { @@ -111,16 +111,16 @@ async function getTokenViaEmbeddedBrowser( const { close } = await ctx.window.openUrl({ dataDirKey, url: authorizationUrlStr, - label: 'oauth-authorization-url', + label: "oauth-authorization-url", async onClose() { if (!foundAccessToken) { - reject(new Error('Authorization window closed')); + reject(new Error("Authorization window closed")); } }, async onNavigate({ url: urlStr }) { const url = new URL(urlStr); - if (url.searchParams.has('error')) { - return reject(Error(`Failed to authorize: ${url.searchParams.get('error')}`)); + if (url.searchParams.has("error")) { + return reject(Error(`Failed to authorize: ${url.searchParams.get("error")}`)); } const hash = url.hash.slice(1); @@ -158,13 +158,13 @@ async function extractImplicitToken( accessTokenUrl: null; authorizationUrl: string; }, - tokenName: 'access_token' | 'id_token', + tokenName: "access_token" | "id_token", ): Promise { const url = new URL(callbackUrl); // Check for errors - if (url.searchParams.has('error')) { - throw new Error(`Failed to authorize: ${url.searchParams.get('error')}`); + if (url.searchParams.has("error")) { + throw new Error(`Failed to authorize: ${url.searchParams.get("error")}`); } // Extract token from fragment @@ -179,18 +179,18 @@ async function extractImplicitToken( // Build response from params (prefer fragment, fall back to query) const response: AccessTokenRawResponse = { - access_token: params.get('access_token') ?? url.searchParams.get('access_token') ?? '', - token_type: params.get('token_type') ?? url.searchParams.get('token_type') ?? undefined, - expires_in: params.has('expires_in') - ? parseInt(params.get('expires_in') ?? '0', 10) - : url.searchParams.has('expires_in') - ? parseInt(url.searchParams.get('expires_in') ?? '0', 10) + access_token: params.get("access_token") ?? url.searchParams.get("access_token") ?? "", + token_type: params.get("token_type") ?? url.searchParams.get("token_type") ?? undefined, + expires_in: params.has("expires_in") + ? parseInt(params.get("expires_in") ?? "0", 10) + : url.searchParams.has("expires_in") + ? parseInt(url.searchParams.get("expires_in") ?? "0", 10) : undefined, - scope: params.get('scope') ?? url.searchParams.get('scope') ?? undefined, + scope: params.get("scope") ?? url.searchParams.get("scope") ?? undefined, }; // Include id_token if present - const idToken = params.get('id_token') ?? url.searchParams.get('id_token'); + const idToken = params.get("id_token") ?? url.searchParams.get("id_token"); if (idToken) { response.id_token = idToken; } diff --git a/plugins/auth-oauth2/src/grants/password.ts b/plugins/auth-oauth2/src/grants/password.ts index c96b9848..02f3daa2 100644 --- a/plugins/auth-oauth2/src/grants/password.ts +++ b/plugins/auth-oauth2/src/grants/password.ts @@ -1,8 +1,8 @@ -import type { Context } from '@yaakapp/api'; -import { fetchAccessToken } from '../fetchAccessToken'; -import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken'; -import type { AccessToken, TokenStoreArgs } from '../store'; -import { storeToken } from '../store'; +import type { Context } from "@yaakapp/api"; +import { fetchAccessToken } from "../fetchAccessToken"; +import { getOrRefreshAccessToken } from "../getOrRefreshAccessToken"; +import type { AccessToken, TokenStoreArgs } from "../store"; +import { storeToken } from "../store"; export async function getPassword( ctx: Context, @@ -50,11 +50,11 @@ export async function getPassword( clientSecret, scope, audience, - grantType: 'password', + grantType: "password", credentialsInBody, params: [ - { name: 'username', value: username }, - { name: 'password', value: password }, + { name: "username", value: username }, + { name: "password", value: password }, ], }); diff --git a/plugins/auth-oauth2/src/index.ts b/plugins/auth-oauth2/src/index.ts index bf9f5f28..edf6821b 100644 --- a/plugins/auth-oauth2/src/index.ts +++ b/plugins/auth-oauth2/src/index.ts @@ -4,13 +4,13 @@ import type { GetHttpAuthenticationConfigRequest, JsonPrimitive, PluginDefinition, -} from '@yaakapp/api'; -import type { Algorithm } from 'jsonwebtoken'; +} from "@yaakapp/api"; +import type { Algorithm } from "jsonwebtoken"; import { buildHostedCallbackRedirectUri, DEFAULT_LOCALHOST_PORT, stopActiveServer, -} from './callbackServer'; +} from "./callbackServer"; import { type CallbackType, DEFAULT_PKCE_METHOD, @@ -18,31 +18,31 @@ import { getAuthorizationCode, PKCE_PLAIN, PKCE_SHA256, -} from './grants/authorizationCode'; +} from "./grants/authorizationCode"; import { defaultJwtAlgorithm, getClientCredentials, jwtAlgorithms, -} from './grants/clientCredentials'; -import { getImplicit } from './grants/implicit'; -import { getPassword } from './grants/password'; -import type { AccessToken, TokenStoreArgs } from './store'; -import { deleteToken, getToken, resetDataDirKey } from './store'; +} from "./grants/clientCredentials"; +import { getImplicit } from "./grants/implicit"; +import { getPassword } from "./grants/password"; +import type { AccessToken, TokenStoreArgs } from "./store"; +import { deleteToken, getToken, resetDataDirKey } from "./store"; -type GrantType = 'authorization_code' | 'implicit' | 'password' | 'client_credentials'; +type GrantType = "authorization_code" | "implicit" | "password" | "client_credentials"; const grantTypes: FormInputSelectOption[] = [ - { label: 'Authorization Code', value: 'authorization_code' }, - { label: 'Implicit', value: 'implicit' }, - { label: 'Resource Owner Password Credential', value: 'password' }, - { label: 'Client Credentials', value: 'client_credentials' }, + { label: "Authorization Code", value: "authorization_code" }, + { label: "Implicit", value: "implicit" }, + { label: "Resource Owner Password Credential", value: "password" }, + { label: "Client Credentials", value: "client_credentials" }, ]; const defaultGrantType = grantTypes[0]?.value; function hiddenIfNot( grantTypes: GrantType[], - ...other: ((values: GetHttpAuthenticationConfigRequest['values']) => boolean)[] + ...other: ((values: GetHttpAuthenticationConfigRequest["values"]) => boolean)[] ) { return (_ctx: Context, { values }: GetHttpAuthenticationConfigRequest) => { const hasGrantType = grantTypes.find((t) => t === String(values.grantType ?? defaultGrantType)); @@ -53,37 +53,37 @@ function hiddenIfNot( } const authorizationUrls = [ - 'https://github.com/login/oauth/authorize', - 'https://account.box.com/api/oauth2/authorize', - 'https://accounts.google.com/o/oauth2/v2/auth', - 'https://api.imgur.com/oauth2/authorize', - 'https://bitly.com/oauth/authorize', - 'https://gitlab.example.com/oauth/authorize', - 'https://medium.com/m/oauth/authorize', - 'https://public-api.wordpress.com/oauth2/authorize', - 'https://slack.com/oauth/authorize', - 'https://todoist.com/oauth/authorize', - 'https://www.dropbox.com/oauth2/authorize', - 'https://www.linkedin.com/oauth/v2/authorization', - 'https://MY_SHOP.myshopify.com/admin/oauth/access_token', - 'https://appcenter.intuit.com/app/connect/oauth2/authorize', + "https://github.com/login/oauth/authorize", + "https://account.box.com/api/oauth2/authorize", + "https://accounts.google.com/o/oauth2/v2/auth", + "https://api.imgur.com/oauth2/authorize", + "https://bitly.com/oauth/authorize", + "https://gitlab.example.com/oauth/authorize", + "https://medium.com/m/oauth/authorize", + "https://public-api.wordpress.com/oauth2/authorize", + "https://slack.com/oauth/authorize", + "https://todoist.com/oauth/authorize", + "https://www.dropbox.com/oauth2/authorize", + "https://www.linkedin.com/oauth/v2/authorization", + "https://MY_SHOP.myshopify.com/admin/oauth/access_token", + "https://appcenter.intuit.com/app/connect/oauth2/authorize", ]; const accessTokenUrls = [ - 'https://github.com/login/oauth/access_token', - 'https://api-ssl.bitly.com/oauth/access_token', - 'https://api.box.com/oauth2/token', - 'https://api.dropboxapi.com/oauth2/token', - 'https://api.imgur.com/oauth2/token', - 'https://api.medium.com/v1/tokens', - 'https://gitlab.example.com/oauth/token', - 'https://public-api.wordpress.com/oauth2/token', - 'https://slack.com/api/oauth.access', - 'https://todoist.com/oauth/access_token', - 'https://www.googleapis.com/oauth2/v4/token', - 'https://www.linkedin.com/oauth/v2/accessToken', - 'https://MY_SHOP.myshopify.com/admin/oauth/authorize', - 'https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer', + "https://github.com/login/oauth/access_token", + "https://api-ssl.bitly.com/oauth/access_token", + "https://api.box.com/oauth2/token", + "https://api.dropboxapi.com/oauth2/token", + "https://api.imgur.com/oauth2/token", + "https://api.medium.com/v1/tokens", + "https://gitlab.example.com/oauth/token", + "https://public-api.wordpress.com/oauth2/token", + "https://slack.com/api/oauth.access", + "https://todoist.com/oauth/access_token", + "https://www.googleapis.com/oauth2/v4/token", + "https://www.linkedin.com/oauth/v2/accessToken", + "https://MY_SHOP.myshopify.com/admin/oauth/authorize", + "https://oauth.platform.intuit.com/oauth2/v1/tokens/bearer", ]; export const plugin: PluginDefinition = { @@ -91,59 +91,59 @@ export const plugin: PluginDefinition = { stopActiveServer(); }, authentication: { - name: 'oauth2', - label: 'OAuth 2.0', - shortLabel: 'OAuth 2', + name: "oauth2", + label: "OAuth 2.0", + shortLabel: "OAuth 2", actions: [ { - label: 'Copy Current Token', + label: "Copy Current Token", async onSelect(ctx, { contextId, values }) { const tokenArgs: TokenStoreArgs = { contextId, - authorizationUrl: stringArg(values, 'authorizationUrl'), - accessTokenUrl: stringArg(values, 'accessTokenUrl'), - clientId: stringArg(values, 'clientId'), + authorizationUrl: stringArg(values, "authorizationUrl"), + accessTokenUrl: stringArg(values, "accessTokenUrl"), + clientId: stringArg(values, "clientId"), }; const token = await getToken(ctx, tokenArgs); if (token == null) { await ctx.toast.show({ - message: 'No token to copy', - color: 'warning', + message: "No token to copy", + color: "warning", }); } else { await ctx.clipboard.copyText(token.response.access_token); await ctx.toast.show({ - message: 'Token copied to clipboard', - icon: 'copy', - color: 'success', + message: "Token copied to clipboard", + icon: "copy", + color: "success", }); } }, }, { - label: 'Delete Token', + label: "Delete Token", async onSelect(ctx, { contextId, values }) { const tokenArgs: TokenStoreArgs = { contextId, - authorizationUrl: stringArg(values, 'authorizationUrl'), - accessTokenUrl: stringArg(values, 'accessTokenUrl'), - clientId: stringArg(values, 'clientId'), + authorizationUrl: stringArg(values, "authorizationUrl"), + accessTokenUrl: stringArg(values, "accessTokenUrl"), + clientId: stringArg(values, "clientId"), }; if (await deleteToken(ctx, tokenArgs)) { await ctx.toast.show({ - message: 'Token deleted', - color: 'success', + message: "Token deleted", + color: "success", }); } else { await ctx.toast.show({ - message: 'No token to delete', - color: 'warning', + message: "No token to delete", + color: "warning", }); } }, }, { - label: 'Clear Window Session', + label: "Clear Window Session", async onSelect(ctx, { contextId }) { await resetDataDirKey(ctx, contextId); }, @@ -151,85 +151,85 @@ export const plugin: PluginDefinition = { ], args: [ { - type: 'select', - name: 'grantType', - label: 'Grant Type', + type: "select", + name: "grantType", + label: "Grant Type", defaultValue: defaultGrantType, options: grantTypes, }, { - type: 'select', - name: 'clientCredentialsMethod', - label: 'Authentication Method', + type: "select", + name: "clientCredentialsMethod", + label: "Authentication Method", description: '"Client Secret" sends client_secret. \n' + '"Client Assertion" sends a signed JWT.', - defaultValue: 'client_secret', + defaultValue: "client_secret", options: [ - { label: 'Client Secret', value: 'client_secret' }, - { label: 'Client Assertion', value: 'client_assertion' }, + { label: "Client Secret", value: "client_secret" }, + { label: "Client Assertion", value: "client_assertion" }, ], - dynamic: hiddenIfNot(['client_credentials']), + dynamic: hiddenIfNot(["client_credentials"]), }, { - type: 'text', - name: 'clientId', - label: 'Client ID', + type: "text", + name: "clientId", + label: "Client ID", optional: true, }, { - type: 'text', - name: 'clientSecret', - label: 'Client Secret', + type: "text", + name: "clientSecret", + label: "Client Secret", optional: true, password: true, dynamic: hiddenIfNot( - ['authorization_code', 'password', 'client_credentials'], - (values) => values.clientCredentialsMethod === 'client_secret', + ["authorization_code", "password", "client_credentials"], + (values) => values.clientCredentialsMethod === "client_secret", ), }, { - type: 'select', - name: 'clientAssertionAlgorithm', - label: 'JWT Algorithm', + type: "select", + name: "clientAssertionAlgorithm", + label: "JWT Algorithm", defaultValue: defaultJwtAlgorithm, options: jwtAlgorithms.map((value) => ({ - label: value === 'none' ? 'None' : value, + label: value === "none" ? "None" : value, value, })), dynamic: hiddenIfNot( - ['client_credentials'], - ({ clientCredentialsMethod }) => clientCredentialsMethod === 'client_assertion', + ["client_credentials"], + ({ clientCredentialsMethod }) => clientCredentialsMethod === "client_assertion", ), }, { - type: 'text', - name: 'clientAssertionSecret', - label: 'JWT Secret', + type: "text", + name: "clientAssertionSecret", + label: "JWT Secret", description: - 'Can be HMAC, PEM or JWK. Make sure you pick the correct algorithm type above.', + "Can be HMAC, PEM or JWK. Make sure you pick the correct algorithm type above.", password: true, optional: true, multiLine: true, dynamic: hiddenIfNot( - ['client_credentials'], - ({ clientCredentialsMethod }) => clientCredentialsMethod === 'client_assertion', + ["client_credentials"], + ({ clientCredentialsMethod }) => clientCredentialsMethod === "client_assertion", ), }, { - type: 'checkbox', - name: 'clientAssertionSecretBase64', - label: 'JWT secret is base64 encoded', + type: "checkbox", + name: "clientAssertionSecretBase64", + label: "JWT secret is base64 encoded", dynamic: hiddenIfNot( - ['client_credentials'], - ({ clientCredentialsMethod }) => clientCredentialsMethod === 'client_assertion', + ["client_credentials"], + ({ clientCredentialsMethod }) => clientCredentialsMethod === "client_assertion", ), }, { - type: 'text', - name: 'authorizationUrl', + type: "text", + name: "authorizationUrl", optional: true, - label: 'Authorization URL', - dynamic: hiddenIfNot(['authorization_code', 'implicit']), + label: "Authorization URL", + dynamic: hiddenIfNot(["authorization_code", "implicit"]), placeholder: authorizationUrls[0], completionOptions: authorizationUrls.map((url) => ({ label: url, @@ -237,103 +237,103 @@ export const plugin: PluginDefinition = { })), }, { - type: 'text', - name: 'accessTokenUrl', + type: "text", + name: "accessTokenUrl", optional: true, - label: 'Access Token URL', + label: "Access Token URL", placeholder: accessTokenUrls[0], - dynamic: hiddenIfNot(['authorization_code', 'password', 'client_credentials']), + dynamic: hiddenIfNot(["authorization_code", "password", "client_credentials"]), completionOptions: accessTokenUrls.map((url) => ({ label: url, value: url, })), }, { - type: 'banner', + type: "banner", inputs: [ { - type: 'checkbox', - name: 'useExternalBrowser', - label: 'Use External Browser', + type: "checkbox", + name: "useExternalBrowser", + label: "Use External Browser", description: - 'Open authorization URL in your system browser instead of the embedded browser. ' + - 'Useful when the OAuth provider blocks embedded browsers or you need existing browser sessions.', - dynamic: hiddenIfNot(['authorization_code', 'implicit']), + "Open authorization URL in your system browser instead of the embedded browser. " + + "Useful when the OAuth provider blocks embedded browsers or you need existing browser sessions.", + dynamic: hiddenIfNot(["authorization_code", "implicit"]), }, { - type: 'text', - name: 'redirectUri', - label: 'Redirect URI (can be any valid URL)', - placeholder: 'https://mysite.example.com/oauth/callback', + type: "text", + name: "redirectUri", + label: "Redirect URI (can be any valid URL)", + placeholder: "https://mysite.example.com/oauth/callback", description: - 'URI the OAuth provider redirects to after authorization. Yaak intercepts this automatically in its embedded browser so any valid URI will work.', + "URI the OAuth provider redirects to after authorization. Yaak intercepts this automatically in its embedded browser so any valid URI will work.", optional: true, dynamic: hiddenIfNot( - ['authorization_code', 'implicit'], + ["authorization_code", "implicit"], ({ useExternalBrowser }) => !useExternalBrowser, ), }, { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'select', - name: 'callbackType', - label: 'Callback Type', + type: "select", + name: "callbackType", + label: "Callback Type", description: '"Hosted Redirect" uses an external Yaak-hosted endpoint. "Localhost" starts a local server to receive the callback.', - defaultValue: 'hosted', + defaultValue: "hosted", options: [ - { label: 'Hosted Redirect', value: 'hosted' }, - { label: 'Localhost', value: 'localhost' }, + { label: "Hosted Redirect", value: "hosted" }, + { label: "Localhost", value: "localhost" }, ], dynamic: hiddenIfNot( - ['authorization_code', 'implicit'], + ["authorization_code", "implicit"], ({ useExternalBrowser }) => !!useExternalBrowser, ), }, { - type: 'text', - name: 'callbackPort', - label: 'Callback Port', + type: "text", + name: "callbackPort", + label: "Callback Port", placeholder: `${DEFAULT_LOCALHOST_PORT}`, description: - 'Port for the local callback server. Defaults to ' + + "Port for the local callback server. Defaults to " + DEFAULT_LOCALHOST_PORT + - ' if empty.', + " if empty.", optional: true, dynamic: hiddenIfNot( - ['authorization_code', 'implicit'], + ["authorization_code", "implicit"], ({ useExternalBrowser }) => !!useExternalBrowser, ), }, ], }, { - type: 'banner', - color: 'info', + type: "banner", + color: "info", inputs: [ { - type: 'markdown', - content: 'Redirect URI to Register', + type: "markdown", + content: "Redirect URI to Register", async dynamic(_ctx, { values }) { const grantType = String(values.grantType ?? defaultGrantType); const useExternalBrowser = !!values.useExternalBrowser; - const callbackType = (stringArg(values, 'callbackType') || - 'localhost') as CallbackType; + const callbackType = (stringArg(values, "callbackType") || + "localhost") as CallbackType; // Only show for authorization_code and implicit with external browser enabled if ( - !['authorization_code', 'implicit'].includes(grantType) || + !["authorization_code", "implicit"].includes(grantType) || !useExternalBrowser ) { return { hidden: true }; } // Compute the redirect URI based on callback type - const port = intArg(values, 'callbackPort') || DEFAULT_LOCALHOST_PORT; + const port = intArg(values, "callbackPort") || DEFAULT_LOCALHOST_PORT; let redirectUri: string; - if (callbackType === 'hosted') { + if (callbackType === "hosted") { redirectUri = buildHostedCallbackRedirectUri(port); } else { redirectUri = `http://127.0.0.1:${port}/callback`; @@ -350,149 +350,149 @@ export const plugin: PluginDefinition = { ], }, { - type: 'text', - name: 'state', - label: 'State', + type: "text", + name: "state", + label: "State", optional: true, - dynamic: hiddenIfNot(['authorization_code', 'implicit']), + dynamic: hiddenIfNot(["authorization_code", "implicit"]), }, - { type: 'text', name: 'scope', label: 'Scope', optional: true }, - { type: 'text', name: 'audience', label: 'Audience', optional: true }, + { type: "text", name: "scope", label: "Scope", optional: true }, + { type: "text", name: "audience", label: "Audience", optional: true }, { - type: 'select', - name: 'tokenName', - label: 'Token for authorization', + type: "select", + name: "tokenName", + label: "Token for authorization", description: 'Select which token to send in the "Authorization: Bearer" header. Most APIs expect ' + - 'access_token, but some (like OpenID Connect) require id_token.', - defaultValue: 'access_token', + "access_token, but some (like OpenID Connect) require id_token.", + defaultValue: "access_token", options: [ - { label: 'access_token', value: 'access_token' }, - { label: 'id_token', value: 'id_token' }, + { label: "access_token", value: "access_token" }, + { label: "id_token", value: "id_token" }, ], - dynamic: hiddenIfNot(['authorization_code', 'implicit']), + dynamic: hiddenIfNot(["authorization_code", "implicit"]), }, { - type: 'banner', + type: "banner", inputs: [ { - type: 'checkbox', - name: 'usePkce', - label: 'Use PKCE', - dynamic: hiddenIfNot(['authorization_code']), + type: "checkbox", + name: "usePkce", + label: "Use PKCE", + dynamic: hiddenIfNot(["authorization_code"]), }, { - type: 'select', - name: 'pkceChallengeMethod', - label: 'Code Challenge Method', + type: "select", + name: "pkceChallengeMethod", + label: "Code Challenge Method", options: [ - { label: 'SHA-256', value: PKCE_SHA256 }, - { label: 'Plain', value: PKCE_PLAIN }, + { label: "SHA-256", value: PKCE_SHA256 }, + { label: "Plain", value: PKCE_PLAIN }, ], defaultValue: DEFAULT_PKCE_METHOD, - dynamic: hiddenIfNot(['authorization_code'], ({ usePkce }) => !!usePkce), + dynamic: hiddenIfNot(["authorization_code"], ({ usePkce }) => !!usePkce), }, { - type: 'text', - name: 'pkceCodeChallenge', - label: 'Code Verifier', - placeholder: 'Automatically generated when not set', + type: "text", + name: "pkceCodeChallenge", + label: "Code Verifier", + placeholder: "Automatically generated when not set", optional: true, - dynamic: hiddenIfNot(['authorization_code'], ({ usePkce }) => !!usePkce), + dynamic: hiddenIfNot(["authorization_code"], ({ usePkce }) => !!usePkce), }, ], }, { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'text', - name: 'username', - label: 'Username', + type: "text", + name: "username", + label: "Username", optional: true, - dynamic: hiddenIfNot(['password']), + dynamic: hiddenIfNot(["password"]), }, { - type: 'text', - name: 'password', - label: 'Password', + type: "text", + name: "password", + label: "Password", password: true, optional: true, - dynamic: hiddenIfNot(['password']), + dynamic: hiddenIfNot(["password"]), }, ], }, { - type: 'select', - name: 'responseType', - label: 'Response Type', - defaultValue: 'token', + type: "select", + name: "responseType", + label: "Response Type", + defaultValue: "token", options: [ - { label: 'Access Token', value: 'token' }, - { label: 'ID Token', value: 'id_token' }, - { label: 'ID and Access Token', value: 'id_token token' }, + { label: "Access Token", value: "token" }, + { label: "ID Token", value: "id_token" }, + { label: "ID and Access Token", value: "id_token token" }, ], - dynamic: hiddenIfNot(['implicit']), + dynamic: hiddenIfNot(["implicit"]), }, { - type: 'accordion', - label: 'Advanced', + type: "accordion", + label: "Advanced", inputs: [ { - type: 'text', - name: 'headerName', - label: 'Header Name', - defaultValue: 'Authorization', + type: "text", + name: "headerName", + label: "Header Name", + defaultValue: "Authorization", }, { - type: 'text', - name: 'headerPrefix', - label: 'Header Prefix', + type: "text", + name: "headerPrefix", + label: "Header Prefix", optional: true, - defaultValue: 'Bearer', + defaultValue: "Bearer", }, { - type: 'select', - name: 'credentials', - label: 'Send Credentials', - defaultValue: 'body', + type: "select", + name: "credentials", + label: "Send Credentials", + defaultValue: "body", options: [ - { label: 'In Request Body', value: 'body' }, - { label: 'As Basic Authentication', value: 'basic' }, + { label: "In Request Body", value: "body" }, + { label: "As Basic Authentication", value: "basic" }, ], dynamic: (_ctx: Context, { values }: GetHttpAuthenticationConfigRequest) => ({ hidden: - values.grantType === 'client_credentials' && - values.clientCredentialsMethod === 'client_assertion', + values.grantType === "client_credentials" && + values.clientCredentialsMethod === "client_assertion", }), }, ], }, { - type: 'accordion', - label: 'Access Token Response', + type: "accordion", + label: "Access Token Response", inputs: [], async dynamic(ctx, { contextId, values }) { const tokenArgs: TokenStoreArgs = { contextId, - authorizationUrl: stringArg(values, 'authorizationUrl'), - accessTokenUrl: stringArg(values, 'accessTokenUrl'), - clientId: stringArg(values, 'clientId'), + authorizationUrl: stringArg(values, "authorizationUrl"), + accessTokenUrl: stringArg(values, "accessTokenUrl"), + clientId: stringArg(values, "clientId"), }; const token = await getToken(ctx, tokenArgs); if (token == null) { return { hidden: true }; } return { - label: 'Access Token Response', + label: "Access Token Response", inputs: [ { - type: 'editor', - name: 'response', + type: "editor", + name: "response", defaultValue: JSON.stringify(token.response, null, 2), hideLabel: true, readOnly: true, - language: 'json', + language: "json", }, ], }; @@ -500,101 +500,101 @@ export const plugin: PluginDefinition = { }, ], async onApply(ctx, { values, contextId }) { - const headerPrefix = stringArg(values, 'headerPrefix'); - const grantType = stringArg(values, 'grantType') as GrantType; - const credentialsInBody = values.credentials === 'body'; - const tokenName = values.tokenName === 'id_token' ? 'id_token' : 'access_token'; + const headerPrefix = stringArg(values, "headerPrefix"); + const grantType = stringArg(values, "grantType") as GrantType; + const credentialsInBody = values.credentials === "body"; + const tokenName = values.tokenName === "id_token" ? "id_token" : "access_token"; // Build external browser options if enabled const useExternalBrowser = !!values.useExternalBrowser; const externalBrowserOptions = useExternalBrowser ? { useExternalBrowser: true, - callbackType: (stringArg(values, 'callbackType') || 'localhost') as CallbackType, - callbackPort: intArg(values, 'callbackPort') ?? undefined, + callbackType: (stringArg(values, "callbackType") || "localhost") as CallbackType, + callbackPort: intArg(values, "callbackPort") ?? undefined, } : undefined; let token: AccessToken; - if (grantType === 'authorization_code') { - const authorizationUrl = stringArg(values, 'authorizationUrl'); - const accessTokenUrl = stringArg(values, 'accessTokenUrl'); + if (grantType === "authorization_code") { + const authorizationUrl = stringArg(values, "authorizationUrl"); + const accessTokenUrl = stringArg(values, "accessTokenUrl"); token = await getAuthorizationCode(ctx, contextId, { accessTokenUrl: - accessTokenUrl === '' || accessTokenUrl.match(/^https?:\/\//) + accessTokenUrl === "" || accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`, authorizationUrl: - authorizationUrl === '' || authorizationUrl.match(/^https?:\/\//) + authorizationUrl === "" || authorizationUrl.match(/^https?:\/\//) ? authorizationUrl : `https://${authorizationUrl}`, - clientId: stringArg(values, 'clientId'), - clientSecret: stringArg(values, 'clientSecret'), - redirectUri: stringArgOrNull(values, 'redirectUri'), - scope: stringArgOrNull(values, 'scope'), - audience: stringArgOrNull(values, 'audience'), - state: stringArgOrNull(values, 'state'), + clientId: stringArg(values, "clientId"), + clientSecret: stringArg(values, "clientSecret"), + redirectUri: stringArgOrNull(values, "redirectUri"), + scope: stringArgOrNull(values, "scope"), + audience: stringArgOrNull(values, "audience"), + state: stringArgOrNull(values, "state"), credentialsInBody, pkce: values.usePkce ? { - challengeMethod: stringArg(values, 'pkceChallengeMethod') || DEFAULT_PKCE_METHOD, - codeVerifier: stringArg(values, 'pkceCodeVerifier') || genPkceCodeVerifier(), + challengeMethod: stringArg(values, "pkceChallengeMethod") || DEFAULT_PKCE_METHOD, + codeVerifier: stringArg(values, "pkceCodeVerifier") || genPkceCodeVerifier(), } : null, tokenName: tokenName, externalBrowser: externalBrowserOptions, }); - } else if (grantType === 'implicit') { - const authorizationUrl = stringArg(values, 'authorizationUrl'); + } else if (grantType === "implicit") { + const authorizationUrl = stringArg(values, "authorizationUrl"); token = await getImplicit(ctx, contextId, { authorizationUrl: authorizationUrl.match(/^https?:\/\//) ? authorizationUrl : `https://${authorizationUrl}`, - clientId: stringArg(values, 'clientId'), - redirectUri: stringArgOrNull(values, 'redirectUri'), - responseType: stringArg(values, 'responseType'), - scope: stringArgOrNull(values, 'scope'), - audience: stringArgOrNull(values, 'audience'), - state: stringArgOrNull(values, 'state'), + clientId: stringArg(values, "clientId"), + redirectUri: stringArgOrNull(values, "redirectUri"), + responseType: stringArg(values, "responseType"), + scope: stringArgOrNull(values, "scope"), + audience: stringArgOrNull(values, "audience"), + state: stringArgOrNull(values, "state"), tokenName: tokenName, externalBrowser: externalBrowserOptions, }); - } else if (grantType === 'client_credentials') { - const accessTokenUrl = stringArg(values, 'accessTokenUrl'); + } else if (grantType === "client_credentials") { + const accessTokenUrl = stringArg(values, "accessTokenUrl"); token = await getClientCredentials(ctx, contextId, { accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`, - clientId: stringArg(values, 'clientId'), - clientAssertionAlgorithm: stringArg(values, 'clientAssertionAlgorithm') as Algorithm, - clientSecret: stringArg(values, 'clientSecret'), - clientCredentialsMethod: stringArg(values, 'clientCredentialsMethod'), - clientAssertionSecret: stringArg(values, 'clientAssertionSecret'), + clientId: stringArg(values, "clientId"), + clientAssertionAlgorithm: stringArg(values, "clientAssertionAlgorithm") as Algorithm, + clientSecret: stringArg(values, "clientSecret"), + clientCredentialsMethod: stringArg(values, "clientCredentialsMethod"), + clientAssertionSecret: stringArg(values, "clientAssertionSecret"), clientAssertionSecretBase64: !!values.clientAssertionSecretBase64, - scope: stringArgOrNull(values, 'scope'), - audience: stringArgOrNull(values, 'audience'), + scope: stringArgOrNull(values, "scope"), + audience: stringArgOrNull(values, "audience"), credentialsInBody, }); - } else if (grantType === 'password') { - const accessTokenUrl = stringArg(values, 'accessTokenUrl'); + } else if (grantType === "password") { + const accessTokenUrl = stringArg(values, "accessTokenUrl"); token = await getPassword(ctx, contextId, { accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`, - clientId: stringArg(values, 'clientId'), - clientSecret: stringArg(values, 'clientSecret'), - username: stringArg(values, 'username'), - password: stringArg(values, 'password'), - scope: stringArgOrNull(values, 'scope'), - audience: stringArgOrNull(values, 'audience'), + clientId: stringArg(values, "clientId"), + clientSecret: stringArg(values, "clientSecret"), + username: stringArg(values, "username"), + password: stringArg(values, "password"), + scope: stringArgOrNull(values, "scope"), + audience: stringArgOrNull(values, "audience"), credentialsInBody, }); } else { throw new Error(`Invalid grant type ${String(grantType)}`); } - const headerName = stringArg(values, 'headerName') || 'Authorization'; - const headerValue = `${headerPrefix} ${token.response[tokenName] ?? ''}`.trim(); + const headerName = stringArg(values, "headerName") || "Authorization"; + const headerValue = `${headerPrefix} ${token.response[tokenName] ?? ""}`.trim(); return { setHeaders: [{ name: headerName, value: headerValue }] }; }, }, @@ -605,19 +605,19 @@ function stringArgOrNull( name: string, ): string | null { const arg = values[name]; - if (arg == null || arg === '') return null; + if (arg == null || arg === "") return null; return `${arg}`; } function stringArg(values: Record, name: string): string { const arg = stringArgOrNull(values, name); - if (!arg) return ''; + if (!arg) return ""; return arg; } function intArg(values: Record, name: string): number | null { const arg = values[name]; - if (arg == null || arg === '') return null; + if (arg == null || arg === "") return null; const num = parseInt(`${arg}`, 10); return Number.isNaN(num) ? null : num; } diff --git a/plugins/auth-oauth2/src/store.ts b/plugins/auth-oauth2/src/store.ts index bde50dc2..44bf3fd2 100644 --- a/plugins/auth-oauth2/src/store.ts +++ b/plugins/auth-oauth2/src/store.ts @@ -1,14 +1,14 @@ -import { createHash } from 'node:crypto'; -import type { Context } from '@yaakapp/api'; +import { createHash } from "node:crypto"; +import type { Context } from "@yaakapp/api"; export async function storeToken( ctx: Context, args: TokenStoreArgs, response: AccessTokenRawResponse, - tokenName: 'access_token' | 'id_token' = 'access_token', + tokenName: "access_token" | "id_token" = "access_token", ) { if (!response[tokenName]) { - throw new Error(`${tokenName} not found in response ${Object.keys(response).join(', ')}`); + throw new Error(`${tokenName} not found in response ${Object.keys(response).join(", ")}`); } const expiresAt = response.expires_in ? Date.now() + response.expires_in * 1000 : null; @@ -34,7 +34,7 @@ export async function resetDataDirKey(ctx: Context, contextId: string) { } export async function getDataDirKey(ctx: Context, contextId: string) { - const key = (await ctx.store.get(dataDirStoreKey(contextId))) ?? 'default'; + const key = (await ctx.store.get(dataDirStoreKey(contextId))) ?? "default"; return `${contextId}::${key}`; } @@ -50,17 +50,17 @@ export interface TokenStoreArgs { * account for slight variations (like domains with and without a protocol scheme). */ function tokenStoreKey(args: TokenStoreArgs) { - const hash = createHash('md5'); + const hash = createHash("md5"); if (args.contextId) hash.update(args.contextId.trim()); if (args.clientId) hash.update(args.clientId.trim()); - if (args.accessTokenUrl) hash.update(args.accessTokenUrl.trim().replace(/^https?:\/\//, '')); - if (args.authorizationUrl) hash.update(args.authorizationUrl.trim().replace(/^https?:\/\//, '')); - const key = hash.digest('hex'); - return ['token', key].join('::'); + if (args.accessTokenUrl) hash.update(args.accessTokenUrl.trim().replace(/^https?:\/\//, "")); + if (args.authorizationUrl) hash.update(args.authorizationUrl.trim().replace(/^https?:\/\//, "")); + const key = hash.digest("hex"); + return ["token", key].join("::"); } function dataDirStoreKey(contextId: string) { - return ['data_dir', contextId].join('::'); + return ["data_dir", contextId].join("::"); } export interface AccessToken { diff --git a/plugins/auth-oauth2/src/util.ts b/plugins/auth-oauth2/src/util.ts index 986cab0f..dfafffaf 100644 --- a/plugins/auth-oauth2/src/util.ts +++ b/plugins/auth-oauth2/src/util.ts @@ -1,4 +1,4 @@ -import type { AccessToken } from './store'; +import type { AccessToken } from "./store"; export function isTokenExpired(token: AccessToken) { return token.expiresAt && Date.now() > token.expiresAt; @@ -8,24 +8,24 @@ export function extractCode(urlStr: string, redirectUri: string | null): string const url = new URL(urlStr); if (!urlMatchesRedirect(url, redirectUri)) { - console.log('[oauth2] URL does not match redirect origin/path; skipping.'); + console.log("[oauth2] URL does not match redirect origin/path; skipping."); return null; } // Prefer query param; fall back to fragment if query lacks it const query = url.searchParams; - const queryError = query.get('error'); - const queryDesc = query.get('error_description'); - const queryUri = query.get('error_uri'); + const queryError = query.get("error"); + const queryDesc = query.get("error_description"); + const queryUri = query.get("error_uri"); let hashParams: URLSearchParams | null = null; if (url.hash && url.hash.length > 1) { hashParams = new URLSearchParams(url.hash.slice(1)); } - const hashError = hashParams?.get('error'); - const hashDesc = hashParams?.get('error_description'); - const hashUri = hashParams?.get('error_uri'); + const hashError = hashParams?.get("error"); + const hashDesc = hashParams?.get("error_description"); + const hashUri = hashParams?.get("error_uri"); const error = queryError || hashError; if (error) { @@ -37,13 +37,13 @@ export function extractCode(urlStr: string, redirectUri: string | null): string throw new Error(message); } - const queryCode = query.get('code'); + const queryCode = query.get("code"); if (queryCode) return queryCode; - const hashCode = hashParams?.get('code'); + const hashCode = hashParams?.get("code"); if (hashCode) return hashCode; - console.log('[oauth2] Code not found'); + console.log("[oauth2] Code not found"); return null; } @@ -54,7 +54,7 @@ export function urlMatchesRedirect(url: URL, redirectUrl: string | null): boolea try { redirect = new URL(redirectUrl); } catch { - console.log('[oauth2] Invalid redirect URI; skipping.'); + console.log("[oauth2] Invalid redirect URI; skipping."); return false; } @@ -63,17 +63,17 @@ export function urlMatchesRedirect(url: URL, redirectUrl: string | null): boolea const sameHost = url.hostname.toLowerCase() === redirect.hostname.toLowerCase(); const normalizePort = (u: URL) => - (u.protocol === 'https:' && (!u.port || u.port === '443')) || - (u.protocol === 'http:' && (!u.port || u.port === '80')) - ? '' + (u.protocol === "https:" && (!u.port || u.port === "443")) || + (u.protocol === "http:" && (!u.port || u.port === "80")) + ? "" : u.port; const samePort = normalizePort(url) === normalizePort(redirect); const normPath = (p: string) => { - const withLeading = p.startsWith('/') ? p : `/${p}`; + const withLeading = p.startsWith("/") ? p : `/${p}`; // strip trailing slashes, keep root as "/" - return withLeading.replace(/\/+$/g, '') || '/'; + return withLeading.replace(/\/+$/g, "") || "/"; }; // Require redirect path to be a prefix of the navigated URL path diff --git a/plugins/auth-oauth2/tests/util.test.ts b/plugins/auth-oauth2/tests/util.test.ts index a3435394..a5f0d8ec 100644 --- a/plugins/auth-oauth2/tests/util.test.ts +++ b/plugins/auth-oauth2/tests/util.test.ts @@ -1,109 +1,109 @@ -import { describe, expect, test } from 'vite-plus/test'; -import { extractCode } from '../src/util'; +import { describe, expect, test } from "vite-plus/test"; +import { extractCode } from "../src/util"; -describe('extractCode', () => { - test('extracts code from query when same origin + path', () => { - const url = 'https://app.example.com/cb?code=abc123&state=xyz'; - const redirect = 'https://app.example.com/cb'; - expect(extractCode(url, redirect)).toBe('abc123'); +describe("extractCode", () => { + test("extracts code from query when same origin + path", () => { + const url = "https://app.example.com/cb?code=abc123&state=xyz"; + const redirect = "https://app.example.com/cb"; + expect(extractCode(url, redirect)).toBe("abc123"); }); - test('extracts code from query with weird path', () => { - const url = 'https://app.example.com/cbwithextra?code=abc123&state=xyz'; - const redirect = 'https://app.example.com/cb'; + test("extracts code from query with weird path", () => { + const url = "https://app.example.com/cbwithextra?code=abc123&state=xyz"; + const redirect = "https://app.example.com/cb"; expect(extractCode(url, redirect)).toBeNull(); }); - test('allows trailing slash differences', () => { - expect(extractCode('https://app.example.com/cb/?code=abc', 'https://app.example.com/cb')).toBe( - 'abc', + test("allows trailing slash differences", () => { + expect(extractCode("https://app.example.com/cb/?code=abc", "https://app.example.com/cb")).toBe( + "abc", ); - expect(extractCode('https://app.example.com/cb?code=abc', 'https://app.example.com/cb/')).toBe( - 'abc', + expect(extractCode("https://app.example.com/cb?code=abc", "https://app.example.com/cb/")).toBe( + "abc", ); }); - test('treats default ports as equal (https:443, http:80)', () => { + test("treats default ports as equal (https:443, http:80)", () => { expect( - extractCode('https://app.example.com/cb?code=abc', 'https://app.example.com:443/cb'), - ).toBe('abc'); - expect(extractCode('http://app.example.com/cb?code=abc', 'http://app.example.com:80/cb')).toBe( - 'abc', + extractCode("https://app.example.com/cb?code=abc", "https://app.example.com:443/cb"), + ).toBe("abc"); + expect(extractCode("http://app.example.com/cb?code=abc", "http://app.example.com:80/cb")).toBe( + "abc", ); }); - test('rejects different port', () => { + test("rejects different port", () => { expect( - extractCode('https://app.example.com/cb?code=abc', 'https://app.example.com:8443/cb'), + extractCode("https://app.example.com/cb?code=abc", "https://app.example.com:8443/cb"), ).toBeNull(); }); - test('rejects different hostname (including subdomain changes)', () => { + test("rejects different hostname (including subdomain changes)", () => { expect( - extractCode('https://evil.example.com/cb?code=abc', 'https://app.example.com/cb'), + extractCode("https://evil.example.com/cb?code=abc", "https://app.example.com/cb"), ).toBeNull(); }); - test('requires path to start with redirect path (ignoring query/hash)', () => { + test("requires path to start with redirect path (ignoring query/hash)", () => { // same origin but wrong path -> null expect( - extractCode('https://app.example.com/other?code=abc', 'https://app.example.com/cb'), + extractCode("https://app.example.com/other?code=abc", "https://app.example.com/cb"), ).toBeNull(); // deeper subpath under the redirect path -> allowed (prefix match) expect( - extractCode('https://app.example.com/cb/deep?code=abc', 'https://app.example.com/cb'), - ).toBe('abc'); + extractCode("https://app.example.com/cb/deep?code=abc", "https://app.example.com/cb"), + ).toBe("abc"); }); - test('works with custom schemes', () => { - expect(extractCode('myapp://cb?code=abc', 'myapp://cb')).toBe('abc'); + test("works with custom schemes", () => { + expect(extractCode("myapp://cb?code=abc", "myapp://cb")).toBe("abc"); }); - test('prefers query over fragment when both present', () => { - const url = 'https://app.example.com/cb?code=queryCode#code=hashCode'; - const redirect = 'https://app.example.com/cb'; - expect(extractCode(url, redirect)).toBe('queryCode'); + test("prefers query over fragment when both present", () => { + const url = "https://app.example.com/cb?code=queryCode#code=hashCode"; + const redirect = "https://app.example.com/cb"; + expect(extractCode(url, redirect)).toBe("queryCode"); }); - test('extracts code from fragment when query lacks code', () => { - const url = 'https://app.example.com/cb#code=fromHash&state=xyz'; - const redirect = 'https://app.example.com/cb'; - expect(extractCode(url, redirect)).toBe('fromHash'); + test("extracts code from fragment when query lacks code", () => { + const url = "https://app.example.com/cb#code=fromHash&state=xyz"; + const redirect = "https://app.example.com/cb"; + expect(extractCode(url, redirect)).toBe("fromHash"); }); - test('returns null if no code present (query or fragment)', () => { - const url = 'https://app.example.com/cb?state=only'; - const redirect = 'https://app.example.com/cb'; + test("returns null if no code present (query or fragment)", () => { + const url = "https://app.example.com/cb?state=only"; + const redirect = "https://app.example.com/cb"; expect(extractCode(url, redirect)).toBeNull(); }); - test('returns null when provider reports an error', () => { - const url = 'https://app.example.com/cb?error=access_denied&error_description=oopsy'; - const redirect = 'https://app.example.com/cb'; - expect(() => extractCode(url, redirect)).toThrow('Failed to authorize: access_denied'); + test("returns null when provider reports an error", () => { + const url = "https://app.example.com/cb?error=access_denied&error_description=oopsy"; + const redirect = "https://app.example.com/cb"; + expect(() => extractCode(url, redirect)).toThrow("Failed to authorize: access_denied"); }); - test('when redirectUri is null, extracts code from any URL', () => { - expect(extractCode('https://random.example.com/whatever?code=abc', null)).toBe('abc'); + test("when redirectUri is null, extracts code from any URL", () => { + expect(extractCode("https://random.example.com/whatever?code=abc", null)).toBe("abc"); }); - test('handles extra params gracefully', () => { - const url = 'https://app.example.com/cb?foo=1&bar=2&code=abc&baz=3'; - const redirect = 'https://app.example.com/cb'; - expect(extractCode(url, redirect)).toBe('abc'); + test("handles extra params gracefully", () => { + const url = "https://app.example.com/cb?foo=1&bar=2&code=abc&baz=3"; + const redirect = "https://app.example.com/cb"; + expect(extractCode(url, redirect)).toBe("abc"); }); - test('ignores fragment noise when code is in query', () => { - const url = 'https://app.example.com/cb?code=abc#some=thing'; - const redirect = 'https://app.example.com/cb'; - expect(extractCode(url, redirect)).toBe('abc'); + test("ignores fragment noise when code is in query", () => { + const url = "https://app.example.com/cb?code=abc#some=thing"; + const redirect = "https://app.example.com/cb"; + expect(extractCode(url, redirect)).toBe("abc"); }); // If you decide NOT to support fragment-based codes, flip these to expect null or mark as .skip - test('supports fragment-only code for response_mode=fragment providers', () => { - const url = 'https://app.example.com/cb#state=xyz&code=abc'; - const redirect = 'https://app.example.com/cb'; - expect(extractCode(url, redirect)).toBe('abc'); + test("supports fragment-only code for response_mode=fragment providers", () => { + const url = "https://app.example.com/cb#state=xyz&code=abc"; + const redirect = "https://app.example.com/cb"; + expect(extractCode(url, redirect)).toBe("abc"); }); }); diff --git a/plugins/filter-jsonpath/package.json b/plugins/filter-jsonpath/package.json index 8b044d6f..502396d3 100644 --- a/plugins/filter-jsonpath/package.json +++ b/plugins/filter-jsonpath/package.json @@ -1,14 +1,14 @@ { "name": "@yaak/filter-jsonpath", "displayName": "JSONPath Filter", + "version": "0.1.0", + "private": true, "description": "Filter JSON response data using JSONPath expressions", "repository": { "type": "git", "url": "https://github.com/mountain-loop/yaak.git", "directory": "plugins/filter-jsonpath" }, - "private": true, - "version": "0.1.0", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/filter-jsonpath/src/index.ts b/plugins/filter-jsonpath/src/index.ts index 6a9f815e..d13e2885 100644 --- a/plugins/filter-jsonpath/src/index.ts +++ b/plugins/filter-jsonpath/src/index.ts @@ -1,17 +1,20 @@ -import type { PluginDefinition } from '@yaakapp/api'; -import { JSONPath } from 'jsonpath-plus'; +import type { PluginDefinition } from "@yaakapp/api"; +import { JSONPath } from "jsonpath-plus"; export const plugin: PluginDefinition = { filter: { - name: 'JSONPath', - description: 'Filter JSONPath', + name: "JSONPath", + description: "Filter JSONPath", onFilter(_ctx, args) { const parsed = JSON.parse(args.payload); try { const filtered = JSONPath({ path: args.filter, json: parsed }); return { content: JSON.stringify(filtered, null, 2) }; } catch (err) { - return { content: '', error: `Invalid filter: ${err instanceof Error ? err.message : String(err)}` }; + return { + content: "", + error: `Invalid filter: ${err instanceof Error ? err.message : String(err)}`, + }; } }, }, diff --git a/plugins/filter-xpath/package.json b/plugins/filter-xpath/package.json index f74035cc..792bc017 100644 --- a/plugins/filter-xpath/package.json +++ b/plugins/filter-xpath/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/filter-xpath", "displayName": "XPath Filter", - "description": "Filter response XML data using XPath expressions", - "private": true, "version": "0.1.0", + "private": true, + "description": "Filter response XML data using XPath expressions", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/filter-xpath/src/index.ts b/plugins/filter-xpath/src/index.ts index 22e90ebd..09213db0 100644 --- a/plugins/filter-xpath/src/index.ts +++ b/plugins/filter-xpath/src/index.ts @@ -1,24 +1,27 @@ /* oxlint-disable no-base-to-string */ -import { DOMParser } from '@xmldom/xmldom'; -import type { PluginDefinition } from '@yaakapp/api'; -import xpath from 'xpath'; +import { DOMParser } from "@xmldom/xmldom"; +import type { PluginDefinition } from "@yaakapp/api"; +import xpath from "xpath"; export const plugin: PluginDefinition = { filter: { - name: 'XPath', - description: 'Filter XPath', + name: "XPath", + description: "Filter XPath", onFilter(_ctx, args) { // oxlint-disable-next-line no-explicit-any - const doc: any = new DOMParser().parseFromString(args.payload, 'text/xml'); + const doc: any = new DOMParser().parseFromString(args.payload, "text/xml"); try { const result = xpath.select(args.filter, doc, false); if (Array.isArray(result)) { - return { content: result.map((r) => String(r)).join('\n') }; + return { content: result.map((r) => String(r)).join("\n") }; } // Not sure what cases this happens in (?) return { content: String(result) }; } catch (err) { - return { content: '', error: `Invalid filter: ${err instanceof Error ? err.message : String(err)}` }; + return { + content: "", + error: `Invalid filter: ${err instanceof Error ? err.message : String(err)}`, + }; } }, }, diff --git a/plugins/importer-curl/package.json b/plugins/importer-curl/package.json index f91a6428..9ee76896 100644 --- a/plugins/importer-curl/package.json +++ b/plugins/importer-curl/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/importer-curl", "displayName": "cURL Importer", - "description": "Import requests from cURL commands", - "private": true, "version": "0.1.0", + "private": true, + "description": "Import requests from cURL commands", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/importer-curl/src/index.ts b/plugins/importer-curl/src/index.ts index 53c99074..d5aced69 100644 --- a/plugins/importer-curl/src/index.ts +++ b/plugins/importer-curl/src/index.ts @@ -6,38 +6,38 @@ import type { HttpUrlParameter, PluginDefinition, Workspace, -} from '@yaakapp/api'; -import { split } from 'shlex'; +} from "@yaakapp/api"; +import { split } from "shlex"; type AtLeast = Partial & Pick; interface ExportResources { - workspaces: AtLeast[]; - environments: AtLeast[]; - httpRequests: AtLeast[]; - folders: AtLeast[]; + workspaces: AtLeast[]; + environments: AtLeast[]; + httpRequests: AtLeast[]; + folders: AtLeast[]; } -const DATA_FLAGS = ['d', 'data', 'data-raw', 'data-urlencode', 'data-binary', 'data-ascii']; +const DATA_FLAGS = ["d", "data", "data-raw", "data-urlencode", "data-binary", "data-ascii"]; const SUPPORTED_FLAGS = [ - ['cookie', 'b'], - ['d', 'data'], // Add url encoded data - ['data-ascii'], - ['data-binary'], - ['data-raw'], - ['data-urlencode'], - ['digest'], // Apply auth as digest - ['form', 'F'], // Add multipart data - ['get', 'G'], // Put the post data in the URL - ['header', 'H'], - ['request', 'X'], // Request method - ['url'], // Specify the URL explicitly - ['url-query'], - ['user', 'u'], // Authentication + ["cookie", "b"], + ["d", "data"], // Add url encoded data + ["data-ascii"], + ["data-binary"], + ["data-raw"], + ["data-urlencode"], + ["digest"], // Apply auth as digest + ["form", "F"], // Add multipart data + ["get", "G"], // Put the post data in the URL + ["header", "H"], + ["request", "X"], // Request method + ["url"], // Specify the URL explicitly + ["url-query"], + ["user", "u"], // Authentication DATA_FLAGS, ].flat(); -const BOOLEAN_FLAGS = ['G', 'get', 'digest']; +const BOOLEAN_FLAGS = ["G", "get", "digest"]; type FlagValue = string | boolean; @@ -45,8 +45,8 @@ type FlagsByName = Record; export const plugin: PluginDefinition = { importer: { - name: 'cURL', - description: 'Import cURL commands', + name: "cURL", + description: "Import cURL commands", onImport(_ctx: Context, args: { text: string }) { // oxlint-disable-next-line no-explicit-any return convertCurl(args.text) as any; @@ -60,14 +60,14 @@ export const plugin: PluginDefinition = { */ function splitCommands(rawData: string): string[] { // Join line continuations (backslash-newline, and backslash-CRLF for Windows) - const joined = rawData.replace(/\\\r?\n/g, ' '); + const joined = rawData.replace(/\\\r?\n/g, " "); // Count consecutive backslashes immediately before position i. // An even count means the quote at i is NOT escaped; odd means it IS escaped. function isEscaped(i: number): boolean { let backslashes = 0; let j = i - 1; - while (j >= 0 && joined[j] === '\\') { + while (j >= 0 && joined[j] === "\\") { backslashes++; j--; } @@ -76,7 +76,7 @@ function splitCommands(rawData: string): string[] { // Split on semicolons and newlines to separate commands const commands: string[] = []; - let current = ''; + let current = ""; let inSingleQuote = false; let inDoubleQuote = false; let inDollarQuote = false; @@ -108,7 +108,7 @@ function splitCommands(rawData: string): string[] { current += ch; continue; } - if (!inSingleQuote && !inDoubleQuote && !inDollarQuote && ch === '$' && next === "'") { + if (!inSingleQuote && !inDoubleQuote && !inDollarQuote && ch === "$" && next === "'") { inDollarQuote = true; current += ch + next; i++; // Skip the opening quote @@ -126,13 +126,13 @@ function splitCommands(rawData: string): string[] { if ( !inQuote && !isEscaped(i) && - (ch === ';' || ch === '\n' || (ch === '\r' && next === '\n')) + (ch === ";" || ch === "\n" || (ch === "\r" && next === "\n")) ) { - if (ch === '\r') i++; // Skip the \n in \r\n + if (ch === "\r") i++; // Skip the \n in \r\n if (current.trim()) { commands.push(current.trim()); } - current = ''; + current = ""; continue; } @@ -156,21 +156,21 @@ export function convertCurl(rawData: string) { // Break up squished arguments like `-XPOST` into `-X POST` return tokens.flatMap((token) => { - if (token.startsWith('-') && !token.startsWith('--') && token.length > 2) { + if (token.startsWith("-") && !token.startsWith("--") && token.length > 2) { return [token.slice(0, 2), token.slice(2)]; } return token; }); }); - const workspace: ExportResources['workspaces'][0] = { - model: 'workspace', - id: generateId('workspace'), - name: 'Curl Import', + const workspace: ExportResources["workspaces"][0] = { + model: "workspace", + id: generateId("workspace"), + name: "Curl Import", }; - const requests: ExportResources['httpRequests'] = commands - .filter((command) => command[0] === 'curl') + const requests: ExportResources["httpRequests"] = commands + .filter((command) => command[0] === "curl") .map((v) => importCommand(v, workspace.id)); return { @@ -191,13 +191,13 @@ function importCommand(parseEntries: string[], workspaceId: string) { // Start at 1 so we can skip the ^curl part for (let i = 1; i < parseEntries.length; i++) { let parseEntry = parseEntries[i]; - if (typeof parseEntry === 'string') { + if (typeof parseEntry === "string") { parseEntry = parseEntry.trim(); } - if (typeof parseEntry === 'string' && parseEntry.match(/^-{1,2}[\w-]+/)) { - const isSingleDash = parseEntry[0] === '-' && parseEntry[1] !== '-'; - let name = parseEntry.replace(/^-{1,2}/, ''); + if (typeof parseEntry === "string" && parseEntry.match(/^-{1,2}[\w-]+/)) { + const isSingleDash = parseEntry[0] === "-" && parseEntry[1] !== "-"; + let name = parseEntry.replace(/^-{1,2}/, ""); if (!SUPPORTED_FLAGS.includes(name)) { continue; @@ -211,13 +211,13 @@ function importCommand(parseEntries: string[], workspaceId: string) { // - Double dash followed by a letter: --data-raw, --header // This prevents mistaking data that starts with dashes (like multipart boundaries ------) as flags const nextEntryIsFlag = - typeof nextEntry === 'string' && + typeof nextEntry === "string" && (nextEntry.match(/^-[a-zA-Z]/) || nextEntry.match(/^--[a-zA-Z]/)); if (isSingleDash && name.length > 1) { // Handle squished arguments like -XPOST value = name.slice(1); name = name.slice(0, 1); - } else if (typeof nextEntry === 'string' && hasValue && !nextEntryIsFlag) { + } else if (typeof nextEntry === "string" && hasValue && !nextEntryIsFlag) { // Next arg is not a flag, so assign it as the value value = nextEntry; i++; // Skip next one @@ -236,14 +236,14 @@ function importCommand(parseEntries: string[], workspaceId: string) { // Build the request // // ~~~~~~~~~~~~~~~~~ // - const urlArg = getPairValue(flagsByName, (singletons[0] as string) || '', ['url']); - const [baseUrl, search] = splitOnce(urlArg, '?'); + const urlArg = getPairValue(flagsByName, (singletons[0] as string) || "", ["url"]); + const [baseUrl, search] = splitOnce(urlArg, "?"); const urlParameters: HttpUrlParameter[] = - search?.split('&').map((p) => { - const v = splitOnce(p, '='); + search?.split("&").map((p) => { + const v = splitOnce(p, "="); return { - name: decodeURIComponent(v[0] ?? ''), - value: decodeURIComponent(v[1] ?? ''), + name: decodeURIComponent(v[0] ?? ""), + value: decodeURIComponent(v[1] ?? ""), enabled: true, }; }) ?? []; @@ -251,27 +251,27 @@ function importCommand(parseEntries: string[], workspaceId: string) { const url = baseUrl ?? urlArg; // Query params - for (const p of flagsByName['url-query'] ?? []) { - if (typeof p !== 'string') { + for (const p of flagsByName["url-query"] ?? []) { + if (typeof p !== "string") { continue; } - const [name, value] = p.split('='); + const [name, value] = p.split("="); urlParameters.push({ - name: name ?? '', - value: value ?? '', + name: name ?? "", + value: value ?? "", enabled: true, }); } // Authentication - const [username, password] = getPairValue(flagsByName, '', ['u', 'user']).split(/:(.*)$/); + const [username, password] = getPairValue(flagsByName, "", ["u", "user"]).split(/:(.*)$/); - const isDigest = getPairValue(flagsByName, false, ['digest']); - const authenticationType = username ? (isDigest ? 'digest' : 'basic') : null; + const isDigest = getPairValue(flagsByName, false, ["digest"]); + const authenticationType = username ? (isDigest ? "digest" : "basic") : null; const authentication = username ? { username: username.trim(), - password: (password ?? '').trim(), + password: (password ?? "").trim(), } : {}; @@ -284,13 +284,13 @@ function importCommand(parseEntries: string[], workspaceId: string) { // remove final colon from header name if present if (!value) { return { - name: (name ?? '').trim().replace(/;$/, ''), - value: '', + name: (name ?? "").trim().replace(/;$/, ""), + value: "", enabled: true, }; } return { - name: (name ?? '').trim(), + name: (name ?? "").trim(), value: value.trim(), enabled: true, }; @@ -302,14 +302,14 @@ function importCommand(parseEntries: string[], workspaceId: string) { ...((flagsByName.b as string[] | undefined) || []), ] .map((str) => { - const name = str.split('=', 1)[0]; - const value = str.replace(`${name}=`, ''); + const name = str.split("=", 1)[0]; + const value = str.replace(`${name}=`, ""); return `${name}=${value}`; }) - .join('; '); + .join("; "); // Convert cookie value to header - const existingCookieHeader = headers.find((header) => header.name.toLowerCase() === 'cookie'); + const existingCookieHeader = headers.find((header) => header.name.toLowerCase() === "cookie"); if (cookieHeaderValue && existingCookieHeader) { // Has existing cookie header, so let's update it @@ -317,15 +317,15 @@ function importCommand(parseEntries: string[], workspaceId: string) { } else if (cookieHeaderValue) { // No existing cookie header, so let's make a new one headers.push({ - name: 'Cookie', + name: "Cookie", value: cookieHeaderValue, enabled: true, }); } // Body (Text or Blob) - const contentTypeHeader = headers.find((header) => header.name.toLowerCase() === 'content-type'); - const mimeType = contentTypeHeader ? contentTypeHeader.value.split(';')[0]?.trim() : null; + const contentTypeHeader = headers.find((header) => header.name.toLowerCase() === "content-type"); + const mimeType = contentTypeHeader ? contentTypeHeader.value.split(";")[0]?.trim() : null; // Extract boundary from Content-Type header for multipart parsing const boundaryMatch = contentTypeHeader?.value.match(/boundary=([^\s;]+)/i); @@ -333,19 +333,19 @@ function importCommand(parseEntries: string[], workspaceId: string) { // Get raw data from --data-raw flags (before splitting by &) const rawDataValues = [ - ...((flagsByName['data-raw'] as string[] | undefined) || []), + ...((flagsByName["data-raw"] as string[] | undefined) || []), ...((flagsByName.d as string[] | undefined) || []), ...((flagsByName.data as string[] | undefined) || []), - ...((flagsByName['data-binary'] as string[] | undefined) || []), - ...((flagsByName['data-ascii'] as string[] | undefined) || []), + ...((flagsByName["data-binary"] as string[] | undefined) || []), + ...((flagsByName["data-ascii"] as string[] | undefined) || []), ]; // Check if this is multipart form data in --data-raw (Chrome DevTools format) let multipartFormDataFromRaw: | { name: string; value?: string; file?: string; enabled: boolean }[] | null = null; - if (mimeType === 'multipart/form-data' && boundary && rawDataValues.length > 0) { - const rawBody = rawDataValues.join(''); + if (mimeType === "multipart/form-data" && boundary && rawDataValues.length > 0) { + const rawBody = rawDataValues.join(""); multipartFormDataFromRaw = parseMultipartFormData(rawBody, boundary); } @@ -356,15 +356,15 @@ function importCommand(parseEntries: string[], workspaceId: string) { ...((flagsByName.form as string[] | undefined) || []), ...((flagsByName.F as string[] | undefined) || []), ].map((str) => { - const parts = str.split('='); - const name = parts[0] ?? ''; - const value = parts[1] ?? ''; + const parts = str.split("="); + const name = parts[0] ?? ""; + const value = parts[1] ?? ""; const item: { name: string; value?: string; file?: string; enabled: boolean } = { name, enabled: true, }; - if (value.indexOf('@') === 0) { + if (value.indexOf("@") === 0) { item.file = value.slice(1); } else { item.value = value; @@ -376,11 +376,11 @@ function importCommand(parseEntries: string[], workspaceId: string) { // Body let body = {}; let bodyType: string | null = null; - const bodyAsGET = getPairValue(flagsByName, false, ['G', 'get']); + const bodyAsGET = getPairValue(flagsByName, false, ["G", "get"]); if (multipartFormDataFromRaw) { // Handle multipart form data parsed from --data-raw (Chrome DevTools format) - bodyType = 'multipart/form-data'; + bodyType = "multipart/form-data"; body = { form: multipartFormDataFromRaw, }; @@ -388,57 +388,57 @@ function importCommand(parseEntries: string[], workspaceId: string) { urlParameters.push(...dataParameters); } else if ( dataParameters.length > 0 && - (mimeType == null || mimeType === 'application/x-www-form-urlencoded') + (mimeType == null || mimeType === "application/x-www-form-urlencoded") ) { - bodyType = mimeType ?? 'application/x-www-form-urlencoded'; + bodyType = mimeType ?? "application/x-www-form-urlencoded"; body = { form: dataParameters.map((parameter) => ({ ...parameter, - name: decodeURIComponent(parameter.name || ''), - value: decodeURIComponent(parameter.value || ''), + name: decodeURIComponent(parameter.name || ""), + value: decodeURIComponent(parameter.value || ""), })), }; headers.push({ - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', + name: "Content-Type", + value: "application/x-www-form-urlencoded", enabled: true, }); } else if (dataParameters.length > 0) { bodyType = - mimeType === 'application/json' || mimeType === 'text/xml' || mimeType === 'text/plain' + mimeType === "application/json" || mimeType === "text/xml" || mimeType === "text/plain" ? mimeType - : 'other'; + : "other"; body = { text: dataParameters .map(({ name, value }) => (name && value ? `${name}=${value}` : name || value)) - .join('&'), + .join("&"), }; } else if (formDataParams.length) { - bodyType = mimeType ?? 'multipart/form-data'; + bodyType = mimeType ?? "multipart/form-data"; body = { form: formDataParams, }; if (mimeType == null) { headers.push({ - name: 'Content-Type', - value: 'multipart/form-data', + name: "Content-Type", + value: "multipart/form-data", enabled: true, }); } } // Method - let method = getPairValue(flagsByName, '', ['X', 'request']).toUpperCase(); + let method = getPairValue(flagsByName, "", ["X", "request"]).toUpperCase(); - if (method === '' && body) { - method = 'text' in body || 'form' in body ? 'POST' : 'GET'; + if (method === "" && body) { + method = "text" in body || "form" in body ? "POST" : "GET"; } - const request: ExportResources['httpRequests'][0] = { - id: generateId('http_request'), - model: 'http_request', + const request: ExportResources["httpRequests"][0] = { + id: generateId("http_request"), + model: "http_request", workspaceId, - name: '', + name: "", urlParameters, url, method, @@ -473,22 +473,22 @@ function pairsToDataParameters(keyedPairs: FlagsByName): DataParameter[] { } for (const p of pairs) { - if (typeof p !== 'string') continue; - const params = p.split('&'); + if (typeof p !== "string") continue; + const params = p.split("&"); for (const param of params) { - const [name, value] = splitOnce(param, '='); - if (param.startsWith('@')) { + const [name, value] = splitOnce(param, "="); + if (param.startsWith("@")) { // Yaak doesn't support files in url-encoded data, so dataParameters.push({ - name: name ?? '', - value: '', + name: name ?? "", + value: "", filePath: param.slice(1), enabled: true, }); } else { dataParameters.push({ - name: name ?? '', - value: flagName === 'data-urlencode' ? encodeURIComponent(value ?? '') : (value ?? ''), + name: name ?? "", + value: flagName === "data-urlencode" ? encodeURIComponent(value ?? "") : (value ?? ""), enabled: true, }); } @@ -537,12 +537,12 @@ function parseMultipartFormData( for (const part of parts) { // Skip empty parts and the closing boundary marker - if (!part || part.trim() === '--' || part.trim() === '--\r\n') { + if (!part || part.trim() === "--" || part.trim() === "--\r\n") { continue; } // Each part has headers and content separated by \r\n\r\n - const headerContentSplit = part.indexOf('\r\n\r\n'); + const headerContentSplit = part.indexOf("\r\n\r\n"); if (headerContentSplit === -1) { continue; } @@ -551,7 +551,7 @@ function parseMultipartFormData( let content = part.slice(headerContentSplit + 4); // Skip \r\n\r\n // Remove trailing \r\n from content - if (content.endsWith('\r\n')) { + if (content.endsWith("\r\n")) { content = content.slice(0, -2); } @@ -564,7 +564,7 @@ function parseMultipartFormData( continue; } - const name = contentDispositionMatch[1] ?? ''; + const name = contentDispositionMatch[1] ?? ""; const filename = contentDispositionMatch[2]; const item: { name: string; value?: string; file?: string; enabled: boolean } = { diff --git a/plugins/importer-curl/tests/index.test.ts b/plugins/importer-curl/tests/index.test.ts index 3b6ae65a..c9e2085f 100644 --- a/plugins/importer-curl/tests/index.test.ts +++ b/plugins/importer-curl/tests/index.test.ts @@ -1,159 +1,182 @@ -import type { HttpRequest, Workspace } from '@yaakapp/api'; -import { describe, expect, test } from 'vite-plus/test'; -import { convertCurl } from '../src'; +import type { HttpRequest, Workspace } from "@yaakapp/api"; +import { describe, expect, test } from "vite-plus/test"; +import { convertCurl } from "../src"; -describe('importer-curl', () => { - test('Imports basic GET', () => { - expect(convertCurl('curl https://yaak.app')).toEqual({ +describe("importer-curl", () => { + test("Imports basic GET", () => { + expect(convertCurl("curl https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', + url: "https://yaak.app", }), ], }, }); }); - test('Explicit URL', () => { - expect(convertCurl('curl --url https://yaak.app')).toEqual({ + test("Explicit URL", () => { + expect(convertCurl("curl --url https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', + url: "https://yaak.app", }), ], }, }); }); - test('Missing URL', () => { - expect(convertCurl('curl -X POST')).toEqual({ + test("Missing URL", () => { + expect(convertCurl("curl -X POST")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - method: 'POST', + method: "POST", }), ], }, }); }); - test('URL between', () => { - expect(convertCurl('curl -v https://yaak.app -X POST')).toEqual({ + test("URL between", () => { + expect(convertCurl("curl -v https://yaak.app -X POST")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', + url: "https://yaak.app", + method: "POST", }), ], }, }); }); - test('Random flags', () => { - expect(convertCurl('curl --random -Z -Y -S --foo https://yaak.app')).toEqual({ + test("Random flags", () => { + expect(convertCurl("curl --random -Z -Y -S --foo https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', + url: "https://yaak.app", }), ], }, }); }); - test('Imports --request method', () => { - expect(convertCurl('curl --request POST https://yaak.app')).toEqual({ + test("Imports --request method", () => { + expect(convertCurl("curl --request POST https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', + url: "https://yaak.app", + method: "POST", }), ], }, }); }); - test('Imports -XPOST method', () => { - expect(convertCurl('curl -XPOST --request POST https://yaak.app')).toEqual({ + test("Imports -XPOST method", () => { + expect(convertCurl("curl -XPOST --request POST https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', + url: "https://yaak.app", + method: "POST", }), ], }, }); }); - test('Imports multiple requests', () => { + test("Imports multiple requests", () => { expect( convertCurl('curl \\\n https://yaak.app\necho "foo"\ncurl example.com;curl foo.com'), ).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ - baseRequest({ url: 'https://yaak.app' }), - baseRequest({ url: 'example.com' }), - baseRequest({ url: 'foo.com' }), + baseRequest({ url: "https://yaak.app" }), + baseRequest({ url: "example.com" }), + baseRequest({ url: "foo.com" }), ], }, }); }); - test('Imports with Windows CRLF line endings', () => { - expect( - convertCurl('curl \\\r\n -X POST \\\r\n https://yaak.app'), - ).toEqual({ + test("Imports with Windows CRLF line endings", () => { + expect(convertCurl("curl \\\r\n -X POST \\\r\n https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], - httpRequests: [ - baseRequest({ url: 'https://yaak.app', method: 'POST' }), - ], + httpRequests: [baseRequest({ url: "https://yaak.app", method: "POST" })], }, }); }); - test('Throws on malformed quotes', () => { - expect(() => - convertCurl('curl -X POST -F "a=aaa" -F b=bbb" https://yaak.app'), - ).toThrow(); + test("Throws on malformed quotes", () => { + expect(() => convertCurl('curl -X POST -F "a=aaa" -F b=bbb" https://yaak.app')).toThrow(); }); - test('Imports form data', () => { - expect( - convertCurl('curl -X POST -F "a=aaa" -F b=bbb -F f=@filepath https://yaak.app'), - ).toEqual({ + test("Imports form data", () => { + expect(convertCurl('curl -X POST -F "a=aaa" -F b=bbb -F f=@filepath https://yaak.app')).toEqual( + { + resources: { + workspaces: [baseWorkspace()], + httpRequests: [ + baseRequest({ + method: "POST", + url: "https://yaak.app", + headers: [ + { + name: "Content-Type", + value: "multipart/form-data", + enabled: true, + }, + ], + bodyType: "multipart/form-data", + body: { + form: [ + { enabled: true, name: "a", value: "aaa" }, + { enabled: true, name: "b", value: "bbb" }, + { enabled: true, name: "f", file: "filepath" }, + ], + }, + }), + ], + }, + }, + ); + }); + + test("Imports data params as form url-encoded", () => { + expect(convertCurl("curl -d a -d b -d c=ccc https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - method: 'POST', - url: 'https://yaak.app', + method: "POST", + url: "https://yaak.app", + bodyType: "application/x-www-form-urlencoded", headers: [ { - name: 'Content-Type', - value: 'multipart/form-data', + name: "Content-Type", + value: "application/x-www-form-urlencoded", enabled: true, }, ], - bodyType: 'multipart/form-data', body: { form: [ - { enabled: true, name: 'a', value: 'aaa' }, - { enabled: true, name: 'b', value: 'bbb' }, - { enabled: true, name: 'f', file: 'filepath' }, + { name: "a", value: "", enabled: true }, + { name: "b", value: "", enabled: true }, + { name: "c", value: "ccc", enabled: true }, ], }, }), @@ -162,56 +185,27 @@ describe('importer-curl', () => { }); }); - test('Imports data params as form url-encoded', () => { - expect(convertCurl('curl -d a -d b -d c=ccc https://yaak.app')).toEqual({ - resources: { - workspaces: [baseWorkspace()], - httpRequests: [ - baseRequest({ - method: 'POST', - url: 'https://yaak.app', - bodyType: 'application/x-www-form-urlencoded', - headers: [ - { - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', - enabled: true, - }, - ], - body: { - form: [ - { name: 'a', value: '', enabled: true }, - { name: 'b', value: '', enabled: true }, - { name: 'c', value: 'ccc', enabled: true }, - ], - }, - }), - ], - }, - }); - }); - - test('Imports combined data params as form url-encoded', () => { + test("Imports combined data params as form url-encoded", () => { expect(convertCurl(`curl -d 'a=aaa&b=bbb&c' https://yaak.app`)).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - method: 'POST', - url: 'https://yaak.app', - bodyType: 'application/x-www-form-urlencoded', + method: "POST", + url: "https://yaak.app", + bodyType: "application/x-www-form-urlencoded", headers: [ { - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', + name: "Content-Type", + value: "application/x-www-form-urlencoded", enabled: true, }, ], body: { form: [ - { name: 'a', value: 'aaa', enabled: true }, - { name: 'b', value: 'bbb', enabled: true }, - { name: 'c', value: '', enabled: true }, + { name: "a", value: "aaa", enabled: true }, + { name: "b", value: "bbb", enabled: true }, + { name: "c", value: "", enabled: true }, ], }, }), @@ -220,38 +214,38 @@ describe('importer-curl', () => { }); }); - test('Imports data params as text', () => { + test("Imports data params as text", () => { expect( - convertCurl('curl -H Content-Type:text/plain -d a -d b -d c=ccc https://yaak.app'), + convertCurl("curl -H Content-Type:text/plain -d a -d b -d c=ccc https://yaak.app"), ).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - method: 'POST', - url: 'https://yaak.app', - headers: [{ name: 'Content-Type', value: 'text/plain', enabled: true }], - bodyType: 'text/plain', - body: { text: 'a&b&c=ccc' }, + method: "POST", + url: "https://yaak.app", + headers: [{ name: "Content-Type", value: "text/plain", enabled: true }], + bodyType: "text/plain", + body: { text: "a&b&c=ccc" }, }), ], }, }); }); - test('Imports post data into URL', () => { - expect(convertCurl('curl -G https://api.stripe.com/v1/payment_links -d limit=3')).toEqual({ + test("Imports post data into URL", () => { + expect(convertCurl("curl -G https://api.stripe.com/v1/payment_links -d limit=3")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - method: 'GET', - url: 'https://api.stripe.com/v1/payment_links', + method: "GET", + url: "https://api.stripe.com/v1/payment_links", urlParameters: [ { enabled: true, - name: 'limit', - value: '3', + name: "limit", + value: "3", }, ], }), @@ -260,7 +254,7 @@ describe('importer-curl', () => { }); }); - test('Imports multi-line JSON', () => { + test("Imports multi-line JSON", () => { expect( convertCurl( `curl -H Content-Type:application/json -d $'{\n "foo":"bar"\n}' https://yaak.app`, @@ -270,10 +264,10 @@ describe('importer-curl', () => { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - method: 'POST', - url: 'https://yaak.app', - headers: [{ name: 'Content-Type', value: 'application/json', enabled: true }], - bodyType: 'application/json', + method: "POST", + url: "https://yaak.app", + headers: [{ name: "Content-Type", value: "application/json", enabled: true }], + bodyType: "application/json", body: { text: '{\n "foo":"bar"\n}' }, }), ], @@ -281,20 +275,20 @@ describe('importer-curl', () => { }); }); - test('Imports multiple headers', () => { + test("Imports multiple headers", () => { expect( - convertCurl('curl -H Foo:bar --header Name -H AAA:bbb -H :ccc https://yaak.app'), + convertCurl("curl -H Foo:bar --header Name -H AAA:bbb -H :ccc https://yaak.app"), ).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', + url: "https://yaak.app", headers: [ - { name: 'Name', value: '', enabled: true }, - { name: 'Foo', value: 'bar', enabled: true }, - { name: 'AAA', value: 'bbb', enabled: true }, - { name: '', value: 'ccc', enabled: true }, + { name: "Name", value: "", enabled: true }, + { name: "Foo", value: "bar", enabled: true }, + { name: "AAA", value: "bbb", enabled: true }, + { name: "", value: "ccc", enabled: true }, ], }), ], @@ -302,17 +296,17 @@ describe('importer-curl', () => { }); }); - test('Imports basic auth', () => { - expect(convertCurl('curl --user user:pass https://yaak.app')).toEqual({ + test("Imports basic auth", () => { + expect(convertCurl("curl --user user:pass https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - authenticationType: 'basic', + url: "https://yaak.app", + authenticationType: "basic", authentication: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, }), ], @@ -320,17 +314,17 @@ describe('importer-curl', () => { }); }); - test('Imports digest auth', () => { - expect(convertCurl('curl --digest --user user:pass https://yaak.app')).toEqual({ + test("Imports digest auth", () => { + expect(convertCurl("curl --digest --user user:pass https://yaak.app")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - authenticationType: 'digest', + url: "https://yaak.app", + authenticationType: "digest", authentication: { - username: 'user', - password: 'pass', + username: "user", + password: "pass", }, }), ], @@ -338,30 +332,30 @@ describe('importer-curl', () => { }); }); - test('Imports cookie as header', () => { + test("Imports cookie as header", () => { expect(convertCurl('curl --cookie "foo=bar" https://yaak.app')).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - headers: [{ name: 'Cookie', value: 'foo=bar', enabled: true }], + url: "https://yaak.app", + headers: [{ name: "Cookie", value: "foo=bar", enabled: true }], }), ], }, }); }); - test('Imports query params', () => { + test("Imports query params", () => { expect(convertCurl('curl "https://yaak.app" --url-query foo=bar --url-query baz=qux')).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', + url: "https://yaak.app", urlParameters: [ - { name: 'foo', value: 'bar', enabled: true }, - { name: 'baz', value: 'qux', enabled: true }, + { name: "foo", value: "bar", enabled: true }, + { name: "baz", value: "qux", enabled: true }, ], }), ], @@ -369,16 +363,16 @@ describe('importer-curl', () => { }); }); - test('Imports query params from the URL', () => { + test("Imports query params from the URL", () => { expect(convertCurl('curl "https://yaak.app?foo=bar&baz=a%20a"')).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', + url: "https://yaak.app", urlParameters: [ - { name: 'foo', value: 'bar', enabled: true }, - { name: 'baz', value: 'a a', enabled: true }, + { name: "foo", value: "bar", enabled: true }, + { name: "baz", value: "a a", enabled: true }, ], }), ], @@ -386,23 +380,23 @@ describe('importer-curl', () => { }); }); - test('Imports weird body', () => { + test("Imports weird body", () => { expect(convertCurl(`curl 'https://yaak.app' -X POST --data-raw 'foo=bar=baz'`)).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'application/x-www-form-urlencoded', + url: "https://yaak.app", + method: "POST", + bodyType: "application/x-www-form-urlencoded", body: { - form: [{ name: 'foo', value: 'bar=baz', enabled: true }], + form: [{ name: "foo", value: "bar=baz", enabled: true }], }, headers: [ { enabled: true, - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', + name: "Content-Type", + value: "application/x-www-form-urlencoded", }, ], }), @@ -411,7 +405,7 @@ describe('importer-curl', () => { }); }); - test('Imports data with Unicode escape sequences', () => { + test("Imports data with Unicode escape sequences", () => { expect( convertCurl( `curl 'https://yaak.app' -H 'Content-Type: application/json' --data-raw $'{"query":"SearchQueryInput\\u0021"}' -X POST`, @@ -421,10 +415,10 @@ describe('importer-curl', () => { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', - headers: [{ name: 'Content-Type', value: 'application/json', enabled: true }], - bodyType: 'application/json', + url: "https://yaak.app", + method: "POST", + headers: [{ name: "Content-Type", value: "application/json", enabled: true }], + bodyType: "application/json", body: { text: '{"query":"SearchQueryInput!"}' }, }), ], @@ -432,7 +426,7 @@ describe('importer-curl', () => { }); }); - test('Imports data with multiple escape sequences', () => { + test("Imports data with multiple escape sequences", () => { expect( convertCurl( `curl 'https://yaak.app' --data-raw $'Line1\\nLine2\\tTab\\u0021Exclamation' -X POST`, @@ -442,17 +436,17 @@ describe('importer-curl', () => { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'application/x-www-form-urlencoded', + url: "https://yaak.app", + method: "POST", + bodyType: "application/x-www-form-urlencoded", body: { - form: [{ name: 'Line1\nLine2\tTab!Exclamation', value: '', enabled: true }], + form: [{ name: "Line1\nLine2\tTab!Exclamation", value: "", enabled: true }], }, headers: [ { enabled: true, - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', + name: "Content-Type", + value: "application/x-www-form-urlencoded", }, ], }), @@ -461,7 +455,7 @@ describe('importer-curl', () => { }); }); - test('Imports multipart form data from --data-raw (Chrome DevTools format)', () => { + test("Imports multipart form data from --data-raw (Chrome DevTools format)", () => { // This is the format Chrome DevTools uses when copying a multipart form submission as cURL const curlCommand = `curl 'http://localhost:8080/system' \ -H 'Content-Type: multipart/form-data; boundary=----WebKitFormBoundaryHwsXKi4rKA6P5VBd' \ @@ -472,21 +466,21 @@ describe('importer-curl', () => { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'http://localhost:8080/system', - method: 'POST', + url: "http://localhost:8080/system", + method: "POST", headers: [ { - name: 'Content-Type', - value: 'multipart/form-data; boundary=----WebKitFormBoundaryHwsXKi4rKA6P5VBd', + name: "Content-Type", + value: "multipart/form-data; boundary=----WebKitFormBoundaryHwsXKi4rKA6P5VBd", enabled: true, }, ], - bodyType: 'multipart/form-data', + bodyType: "multipart/form-data", body: { form: [ - { name: 'username', value: 'jsgj', enabled: true }, - { name: 'password', value: '654321', enabled: true }, - { name: 'captcha', file: 'test.xlsx', enabled: true }, + { name: "username", value: "jsgj", enabled: true }, + { name: "password", value: "654321", enabled: true }, + { name: "captcha", file: "test.xlsx", enabled: true }, ], }, }), @@ -495,7 +489,7 @@ describe('importer-curl', () => { }); }); - test('Imports JSON body with newlines in $quotes', () => { + test("Imports JSON body with newlines in $quotes", () => { expect( convertCurl( `curl 'https://yaak.app' -H 'Content-Type: application/json' --data-raw $'{\\n "foo": "bar",\\n "baz": "qux"\\n}' -X POST`, @@ -505,10 +499,10 @@ describe('importer-curl', () => { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', - headers: [{ name: 'Content-Type', value: 'application/json', enabled: true }], - bodyType: 'application/json', + url: "https://yaak.app", + method: "POST", + headers: [{ name: "Content-Type", value: "application/json", enabled: true }], + bodyType: "application/json", body: { text: '{\n "foo": "bar",\n "baz": "qux"\n}' }, }), ], @@ -516,110 +510,94 @@ describe('importer-curl', () => { }); }); - test('Handles double-quoted string ending with even backslashes before semicolon', () => { + test("Handles double-quoted string ending with even backslashes before semicolon", () => { // "C:\\" has two backslashes which escape each other, so the closing " is real. // The ; after should split into a second command. - expect( - convertCurl( - 'curl -d "C:\\\\" https://yaak.app;curl https://example.com', - ), - ).toEqual({ + expect(convertCurl('curl -d "C:\\\\" https://yaak.app;curl https://example.com')).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'application/x-www-form-urlencoded', + url: "https://yaak.app", + method: "POST", + bodyType: "application/x-www-form-urlencoded", body: { - form: [{ name: 'C:\\', value: '', enabled: true }], + form: [{ name: "C:\\", value: "", enabled: true }], }, headers: [ { - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', + name: "Content-Type", + value: "application/x-www-form-urlencoded", enabled: true, }, ], }), - baseRequest({ url: 'https://example.com' }), + baseRequest({ url: "https://example.com" }), ], }, }); }); - test('Handles $quoted string ending with a literal backslash before semicolon', () => { + test("Handles $quoted string ending with a literal backslash before semicolon", () => { // $'C:\\\\' has two backslashes which become one literal backslash. // The closing ' must not be misinterpreted as escaped. // The ; after should split into a second command. - expect( - convertCurl( - "curl -d $'C:\\\\' https://yaak.app;curl https://example.com", - ), - ).toEqual({ + expect(convertCurl("curl -d $'C:\\\\' https://yaak.app;curl https://example.com")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - method: 'POST', - bodyType: 'application/x-www-form-urlencoded', + url: "https://yaak.app", + method: "POST", + bodyType: "application/x-www-form-urlencoded", body: { - form: [{ name: 'C:\\', value: '', enabled: true }], + form: [{ name: "C:\\", value: "", enabled: true }], }, headers: [ { - name: 'Content-Type', - value: 'application/x-www-form-urlencoded', + name: "Content-Type", + value: "application/x-www-form-urlencoded", enabled: true, }, ], }), - baseRequest({ url: 'https://example.com' }), + baseRequest({ url: "https://example.com" }), ], }, }); }); - test('Imports $quoted header with escaped single quotes', () => { - expect( - convertCurl( - `curl https://yaak.app -H $'X-Custom: it\\'s a test'`, - ), - ).toEqual({ + test("Imports $quoted header with escaped single quotes", () => { + expect(convertCurl(`curl https://yaak.app -H $'X-Custom: it\\'s a test'`)).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - headers: [{ name: 'X-Custom', value: "it's a test", enabled: true }], + url: "https://yaak.app", + headers: [{ name: "X-Custom", value: "it's a test", enabled: true }], }), ], }, }); }); - test('Does not split on escaped semicolon outside quotes', () => { + test("Does not split on escaped semicolon outside quotes", () => { // In shell, \; is a literal semicolon and should not split commands. // This should be treated as a single curl command with the URL "https://yaak.app?a=1;b=2" - expect( - convertCurl('curl https://yaak.app?a=1\\;b=2'), - ).toEqual({ + expect(convertCurl("curl https://yaak.app?a=1\\;b=2")).toEqual({ resources: { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'https://yaak.app', - urlParameters: [ - { name: 'a', value: '1;b=2', enabled: true }, - ], + url: "https://yaak.app", + urlParameters: [{ name: "a", value: "1;b=2", enabled: true }], }), ], }, }); }); - test('Imports multipart form data with text-only fields from --data-raw', () => { + test("Imports multipart form data with text-only fields from --data-raw", () => { const curlCommand = `curl 'http://example.com/api' \ -H 'Content-Type: multipart/form-data; boundary=----FormBoundary123' \ --data-raw $'------FormBoundary123\r\nContent-Disposition: form-data; name="field1"\r\n\r\nvalue1\r\n------FormBoundary123\r\nContent-Disposition: form-data; name="field2"\r\n\r\nvalue2\r\n------FormBoundary123--\r\n'`; @@ -629,20 +607,20 @@ describe('importer-curl', () => { workspaces: [baseWorkspace()], httpRequests: [ baseRequest({ - url: 'http://example.com/api', - method: 'POST', + url: "http://example.com/api", + method: "POST", headers: [ { - name: 'Content-Type', - value: 'multipart/form-data; boundary=----FormBoundary123', + name: "Content-Type", + value: "multipart/form-data; boundary=----FormBoundary123", enabled: true, }, ], - bodyType: 'multipart/form-data', + bodyType: "multipart/form-data", body: { form: [ - { name: 'field1', value: 'value1', enabled: true }, - { name: 'field2', value: 'value2', enabled: true }, + { name: "field1", value: "value1", enabled: true }, + { name: "field2", value: "value2", enabled: true }, ], }, }), @@ -658,17 +636,17 @@ function baseRequest(mergeWith: Partial) { idCount.http_request = (idCount.http_request ?? -1) + 1; return { id: `GENERATE_ID::HTTP_REQUEST_${idCount.http_request}`, - model: 'http_request', + model: "http_request", authentication: {}, authenticationType: null, body: {}, bodyType: null, folderId: null, headers: [], - method: 'GET', - name: '', + method: "GET", + name: "", sortPriority: 0, - url: '', + url: "", urlParameters: [], workspaceId: `GENERATE_ID::WORKSPACE_${idCount.workspace}`, ...mergeWith, @@ -679,8 +657,8 @@ function baseWorkspace(mergeWith: Partial = {}) { idCount.workspace = (idCount.workspace ?? -1) + 1; return { id: `GENERATE_ID::WORKSPACE_${idCount.workspace}`, - model: 'workspace', - name: 'Curl Import', + model: "workspace", + name: "Curl Import", ...mergeWith, }; } diff --git a/plugins/importer-insomnia/package.json b/plugins/importer-insomnia/package.json index 2bccf9c0..468b90ec 100644 --- a/plugins/importer-insomnia/package.json +++ b/plugins/importer-insomnia/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/importer-insomnia", "displayName": "Insomnia Importer", - "description": "Import data from Insomnia", - "private": true, "version": "0.1.0", + "private": true, + "description": "Import data from Insomnia", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/importer-insomnia/src/common.ts b/plugins/importer-insomnia/src/common.ts index 04f0aa75..56a93ca3 100644 --- a/plugins/importer-insomnia/src/common.ts +++ b/plugins/importer-insomnia/src/common.ts @@ -1,13 +1,13 @@ export function isJSObject(obj: unknown) { - return Object.prototype.toString.call(obj) === '[object Object]'; + return Object.prototype.toString.call(obj) === "[object Object]"; } export function isJSString(obj: unknown) { - return Object.prototype.toString.call(obj) === '[object String]'; + return Object.prototype.toString.call(obj) === "[object String]"; } export function convertId(id: string): string { - if (id.startsWith('GENERATE_ID::')) { + if (id.startsWith("GENERATE_ID::")) { return id; } return `GENERATE_ID::${id}`; @@ -17,7 +17,7 @@ export function deleteUndefinedAttrs(obj: T): T { if (Array.isArray(obj) && obj != null) { return obj.map(deleteUndefinedAttrs) as T; } - if (typeof obj === 'object' && obj != null) { + if (typeof obj === "object" && obj != null) { return Object.fromEntries( Object.entries(obj) .filter(([, v]) => v !== undefined) @@ -29,14 +29,14 @@ export function deleteUndefinedAttrs(obj: T): T { /** Recursively render all nested object properties */ export function convertTemplateSyntax(obj: T): T { - if (typeof obj === 'string') { + if (typeof obj === "string") { // oxlint-disable-next-line no-template-curly-in-string -- Yaak template syntax - return obj.replaceAll(/{{\s*(_\.)?([^}]+)\s*}}/g, '${[$2]}') as T; + return obj.replaceAll(/{{\s*(_\.)?([^}]+)\s*}}/g, "${[$2]}") as T; } if (Array.isArray(obj) && obj != null) { return obj.map(convertTemplateSyntax) as T; } - if (typeof obj === 'object' && obj != null) { + if (typeof obj === "object" && obj != null) { return Object.fromEntries( Object.entries(obj).map(([k, v]) => [k, convertTemplateSyntax(v)]), ) as T; diff --git a/plugins/importer-insomnia/src/index.ts b/plugins/importer-insomnia/src/index.ts index d357faf7..cd54fc77 100644 --- a/plugins/importer-insomnia/src/index.ts +++ b/plugins/importer-insomnia/src/index.ts @@ -1,13 +1,13 @@ -import type { Context, PluginDefinition } from '@yaakapp/api'; -import YAML from 'yaml'; -import { deleteUndefinedAttrs, isJSObject } from './common'; -import { convertInsomniaV4 } from './v4'; -import { convertInsomniaV5 } from './v5'; +import type { Context, PluginDefinition } from "@yaakapp/api"; +import YAML from "yaml"; +import { deleteUndefinedAttrs, isJSObject } from "./common"; +import { convertInsomniaV4 } from "./v4"; +import { convertInsomniaV5 } from "./v5"; export const plugin: PluginDefinition = { importer: { - name: 'Insomnia', - description: 'Import Insomnia workspaces', + name: "Insomnia", + description: "Import Insomnia workspaces", async onImport(_ctx: Context, args: { text: string }) { return convertInsomnia(args.text); }, diff --git a/plugins/importer-insomnia/src/v4.ts b/plugins/importer-insomnia/src/v4.ts index 9f895339..8b8f4c1e 100644 --- a/plugins/importer-insomnia/src/v4.ts +++ b/plugins/importer-insomnia/src/v4.ts @@ -1,6 +1,6 @@ /* oxlint-disable no-explicit-any */ -import type { PartialImportResources } from '@yaakapp/api'; -import { convertId, convertTemplateSyntax, isJSObject } from './common'; +import type { PartialImportResources } from "@yaakapp/api"; +import { convertId, convertTemplateSyntax, isJSObject } from "./common"; export function convertInsomniaV4(parsed: any) { if (!Array.isArray(parsed.resources)) return null; @@ -16,19 +16,19 @@ export function convertInsomniaV4(parsed: any) { // Import workspaces const workspacesToImport = parsed.resources.filter( - (r: any) => isJSObject(r) && r._type === 'workspace', + (r: any) => isJSObject(r) && r._type === "workspace", ); for (const w of workspacesToImport) { resources.workspaces.push({ id: convertId(w._id), - createdAt: w.created ? new Date(w.created).toISOString().replace('Z', '') : undefined, - updatedAt: w.updated ? new Date(w.updated).toISOString().replace('Z', '') : undefined, - model: 'workspace', + createdAt: w.created ? new Date(w.created).toISOString().replace("Z", "") : undefined, + updatedAt: w.updated ? new Date(w.updated).toISOString().replace("Z", "") : undefined, + model: "workspace", name: w.name, description: w.description || undefined, }); const environmentsToImport = parsed.resources.filter( - (r: any) => isJSObject(r) && r._type === 'environment', + (r: any) => isJSObject(r) && r._type === "environment", ); resources.environments.push( ...environmentsToImport.map((r: any) => importEnvironment(r, w._id)), @@ -39,12 +39,12 @@ export function convertInsomniaV4(parsed: any) { for (const child of children) { if (!isJSObject(child)) continue; - if (child._type === 'request_group') { + if (child._type === "request_group") { resources.folders.push(importFolder(child, w._id)); nextFolder(child._id); - } else if (child._type === 'request') { + } else if (child._type === "request") { resources.httpRequests.push(importHttpRequest(child, w._id)); - } else if (child._type === 'grpc_request') { + } else if (child._type === "grpc_request") { resources.grpcRequests.push(importGrpcRequest(child, w._id)); } } @@ -63,48 +63,48 @@ export function convertInsomniaV4(parsed: any) { return { resources: convertTemplateSyntax(resources) }; } -function importHttpRequest(r: any, workspaceId: string): PartialImportResources['httpRequests'][0] { +function importHttpRequest(r: any, workspaceId: string): PartialImportResources["httpRequests"][0] { let bodyType: string | null = null; let body = {}; - if (r.body.mimeType === 'application/octet-stream') { - bodyType = 'binary'; - body = { filePath: r.body.fileName ?? '' }; - } else if (r.body?.mimeType === 'application/x-www-form-urlencoded') { - bodyType = 'application/x-www-form-urlencoded'; + if (r.body.mimeType === "application/octet-stream") { + bodyType = "binary"; + body = { filePath: r.body.fileName ?? "" }; + } else if (r.body?.mimeType === "application/x-www-form-urlencoded") { + bodyType = "application/x-www-form-urlencoded"; body = { form: (r.body.params ?? []).map((p: any) => ({ enabled: !p.disabled, - name: p.name ?? '', - value: p.value ?? '', + name: p.name ?? "", + value: p.value ?? "", })), }; - } else if (r.body?.mimeType === 'multipart/form-data') { - bodyType = 'multipart/form-data'; + } else if (r.body?.mimeType === "multipart/form-data") { + bodyType = "multipart/form-data"; body = { form: (r.body.params ?? []).map((p: any) => ({ enabled: !p.disabled, - name: p.name ?? '', - value: p.value ?? '', + name: p.name ?? "", + value: p.value ?? "", file: p.fileName ?? null, })), }; - } else if (r.body?.mimeType === 'application/graphql') { - bodyType = 'graphql'; - body = { text: r.body.text ?? '' }; - } else if (r.body?.mimeType === 'application/json') { - bodyType = 'application/json'; - body = { text: r.body.text ?? '' }; + } else if (r.body?.mimeType === "application/graphql") { + bodyType = "graphql"; + body = { text: r.body.text ?? "" }; + } else if (r.body?.mimeType === "application/json") { + bodyType = "application/json"; + body = { text: r.body.text ?? "" }; } let authenticationType: string | null = null; let authentication = {}; - if (r.authentication.type === 'bearer') { - authenticationType = 'bearer'; + if (r.authentication.type === "bearer") { + authenticationType = "bearer"; authentication = { token: r.authentication.token, }; - } else if (r.authentication.type === 'basic') { - authenticationType = 'basic'; + } else if (r.authentication.type === "basic") { + authenticationType = "basic"; authentication = { username: r.authentication.username, password: r.authentication.password, @@ -113,19 +113,19 @@ function importHttpRequest(r: any, workspaceId: string): PartialImportResources[ return { id: convertId(r.meta?.id ?? r._id), - createdAt: r.created ? new Date(r.created).toISOString().replace('Z', '') : undefined, - updatedAt: r.modified ? new Date(r.modified).toISOString().replace('Z', '') : undefined, + createdAt: r.created ? new Date(r.created).toISOString().replace("Z", "") : undefined, + updatedAt: r.modified ? new Date(r.modified).toISOString().replace("Z", "") : undefined, workspaceId: convertId(workspaceId), folderId: r.parentId === workspaceId ? null : convertId(r.parentId), - model: 'http_request', + model: "http_request", sortPriority: r.metaSortKey, name: r.name, description: r.description || undefined, url: r.url, urlParameters: (r.parameters ?? []).map((p: any) => ({ enabled: !p.disabled, - name: p.name ?? '', - value: p.value ?? '', + name: p.name ?? "", + value: p.value ?? "", })), body, bodyType, @@ -135,51 +135,51 @@ function importHttpRequest(r: any, workspaceId: string): PartialImportResources[ headers: (r.headers ?? []) .map((h: any) => ({ enabled: !h.disabled, - name: h.name ?? '', - value: h.value ?? '', + name: h.name ?? "", + value: h.value ?? "", })) - .filter(({ name, value }: any) => name !== '' || value !== ''), + .filter(({ name, value }: any) => name !== "" || value !== ""), }; } -function importGrpcRequest(r: any, workspaceId: string): PartialImportResources['grpcRequests'][0] { - const parts = r.protoMethodName.split('/').filter((p: any) => p !== ''); +function importGrpcRequest(r: any, workspaceId: string): PartialImportResources["grpcRequests"][0] { + const parts = r.protoMethodName.split("/").filter((p: any) => p !== ""); const service = parts[0] ?? null; const method = parts[1] ?? null; return { id: convertId(r.meta?.id ?? r._id), - createdAt: r.created ? new Date(r.created).toISOString().replace('Z', '') : undefined, - updatedAt: r.modified ? new Date(r.modified).toISOString().replace('Z', '') : undefined, + createdAt: r.created ? new Date(r.created).toISOString().replace("Z", "") : undefined, + updatedAt: r.modified ? new Date(r.modified).toISOString().replace("Z", "") : undefined, workspaceId: convertId(workspaceId), folderId: r.parentId === workspaceId ? null : convertId(r.parentId), - model: 'grpc_request', + model: "grpc_request", sortPriority: r.metaSortKey, name: r.name, description: r.description || undefined, url: r.url, service, method, - message: r.body?.text ?? '', + message: r.body?.text ?? "", metadata: (r.metadata ?? []) .map((h: any) => ({ enabled: !h.disabled, - name: h.name ?? '', - value: h.value ?? '', + name: h.name ?? "", + value: h.value ?? "", })) - .filter(({ name, value }: any) => name !== '' || value !== ''), + .filter(({ name, value }: any) => name !== "" || value !== ""), }; } -function importFolder(f: any, workspaceId: string): PartialImportResources['folders'][0] { +function importFolder(f: any, workspaceId: string): PartialImportResources["folders"][0] { return { id: convertId(f._id), - createdAt: f.created ? new Date(f.created).toISOString().replace('Z', '') : undefined, - updatedAt: f.modified ? new Date(f.modified).toISOString().replace('Z', '') : undefined, + createdAt: f.created ? new Date(f.created).toISOString().replace("Z", "") : undefined, + updatedAt: f.modified ? new Date(f.modified).toISOString().replace("Z", "") : undefined, folderId: f.parentId === workspaceId ? null : convertId(f.parentId), workspaceId: convertId(workspaceId), description: f.description || undefined, - model: 'folder', + model: "folder", name: f.name, }; } @@ -188,17 +188,17 @@ function importEnvironment( e: any, workspaceId: string, isParentOg?: boolean, -): PartialImportResources['environments'][0] { +): PartialImportResources["environments"][0] { const isParent = isParentOg ?? e.parentId === workspaceId; return { id: convertId(e._id), - createdAt: e.created ? new Date(e.created).toISOString().replace('Z', '') : undefined, - updatedAt: e.modified ? new Date(e.modified).toISOString().replace('Z', '') : undefined, + createdAt: e.created ? new Date(e.created).toISOString().replace("Z", "") : undefined, + updatedAt: e.modified ? new Date(e.modified).toISOString().replace("Z", "") : undefined, workspaceId: convertId(workspaceId), sortPriority: e.metaSortKey, - parentModel: isParent ? 'workspace' : 'environment', + parentModel: isParent ? "workspace" : "environment", parentId: null, - model: 'environment', + model: "environment", name: e.name, variables: Object.entries(e.data).map(([name, value]) => ({ enabled: true, diff --git a/plugins/importer-insomnia/src/v5.ts b/plugins/importer-insomnia/src/v5.ts index 6a236df8..c43e64e8 100644 --- a/plugins/importer-insomnia/src/v5.ts +++ b/plugins/importer-insomnia/src/v5.ts @@ -1,14 +1,14 @@ /* oxlint-disable no-explicit-any */ -import type { PartialImportResources } from '@yaakapp/api'; -import { convertId, convertTemplateSyntax, isJSObject } from './common'; +import type { PartialImportResources } from "@yaakapp/api"; +import { convertId, convertTemplateSyntax, isJSObject } from "./common"; export function convertInsomniaV5(parsed: any) { // Assert parsed is object - if (parsed == null || typeof parsed !== 'object') { + if (parsed == null || typeof parsed !== "object") { return null; } - if (!('collection' in parsed) || !Array.isArray(parsed.collection)) { + if (!("collection" in parsed) || !Array.isArray(parsed.collection)) { return null; } @@ -22,12 +22,12 @@ export function convertInsomniaV5(parsed: any) { }; // Import workspaces - const meta = ('meta' in parsed ? parsed.meta : {}) as Record; + const meta = ("meta" in parsed ? parsed.meta : {}) as Record; resources.workspaces.push({ - id: convertId(meta.id ?? 'collection'), - createdAt: meta.created ? new Date(meta.created).toISOString().replace('Z', '') : undefined, - updatedAt: meta.modified ? new Date(meta.modified).toISOString().replace('Z', '') : undefined, - model: 'workspace', + id: convertId(meta.id ?? "collection"), + createdAt: meta.created ? new Date(meta.created).toISOString().replace("Z", "") : undefined, + updatedAt: meta.modified ? new Date(meta.modified).toISOString().replace("Z", "") : undefined, + model: "workspace", name: parsed.name, description: meta.description || undefined, ...importHeaders(parsed), @@ -76,7 +76,7 @@ function importHttpRequest( r: any, workspaceId: string, parentId: string, -): PartialImportResources['httpRequests'][0] { +): PartialImportResources["httpRequests"][0] { const id = r.meta?.id ?? r._id; const created = r.meta?.created ?? r.created; const updated = r.meta?.modified ?? r.updated; @@ -84,51 +84,51 @@ function importHttpRequest( let bodyType: string | null = null; let body = {}; - if (r.body?.mimeType === 'application/octet-stream') { - bodyType = 'binary'; - body = { filePath: r.body.fileName ?? '' }; - } else if (r.body?.mimeType === 'application/x-www-form-urlencoded') { - bodyType = 'application/x-www-form-urlencoded'; + if (r.body?.mimeType === "application/octet-stream") { + bodyType = "binary"; + body = { filePath: r.body.fileName ?? "" }; + } else if (r.body?.mimeType === "application/x-www-form-urlencoded") { + bodyType = "application/x-www-form-urlencoded"; body = { form: (r.body.params ?? []).map((p: any) => ({ enabled: !p.disabled, - name: p.name ?? '', - value: p.value ?? '', + name: p.name ?? "", + value: p.value ?? "", })), }; - } else if (r.body?.mimeType === 'multipart/form-data') { - bodyType = 'multipart/form-data'; + } else if (r.body?.mimeType === "multipart/form-data") { + bodyType = "multipart/form-data"; body = { form: (r.body.params ?? []).map((p: any) => ({ enabled: !p.disabled, - name: p.name ?? '', - value: p.value ?? '', + name: p.name ?? "", + value: p.value ?? "", file: p.fileName ?? null, })), }; - } else if (r.body?.mimeType === 'application/graphql') { - bodyType = 'graphql'; - body = { text: r.body.text ?? '' }; - } else if (r.body?.mimeType === 'application/json') { - bodyType = 'application/json'; - body = { text: r.body.text ?? '' }; + } else if (r.body?.mimeType === "application/graphql") { + bodyType = "graphql"; + body = { text: r.body.text ?? "" }; + } else if (r.body?.mimeType === "application/json") { + bodyType = "application/json"; + body = { text: r.body.text ?? "" }; } return { id: convertId(id), workspaceId: convertId(workspaceId), - createdAt: created ? new Date(created).toISOString().replace('Z', '') : undefined, - updatedAt: updated ? new Date(updated).toISOString().replace('Z', '') : undefined, + createdAt: created ? new Date(created).toISOString().replace("Z", "") : undefined, + updatedAt: updated ? new Date(updated).toISOString().replace("Z", "") : undefined, folderId: parentId === workspaceId ? null : convertId(parentId), sortPriority: sortKey, - model: 'http_request', + model: "http_request", name: r.name, description: r.meta?.description || undefined, url: r.url, urlParameters: (r.parameters ?? []).map((p: any) => ({ enabled: !p.disabled, - name: p.name ?? '', - value: p.value ?? '', + name: p.name ?? "", + value: p.value ?? "", })), body, bodyType, @@ -142,22 +142,22 @@ function importGrpcRequest( r: any, workspaceId: string, parentId: string, -): PartialImportResources['grpcRequests'][0] { +): PartialImportResources["grpcRequests"][0] { const id = r.meta?.id ?? r._id; const created = r.meta?.created ?? r.created; const updated = r.meta?.modified ?? r.updated; const sortKey = r.meta?.sortKey ?? r.sortKey; - const parts = r.protoMethodName.split('/').filter((p: any) => p !== ''); + const parts = r.protoMethodName.split("/").filter((p: any) => p !== ""); const service = parts[0] ?? null; const method = parts[1] ?? null; return { - model: 'grpc_request', + model: "grpc_request", id: convertId(id), workspaceId: convertId(workspaceId), - createdAt: created ? new Date(created).toISOString().replace('Z', '') : undefined, - updatedAt: updated ? new Date(updated).toISOString().replace('Z', '') : undefined, + createdAt: created ? new Date(created).toISOString().replace("Z", "") : undefined, + updatedAt: updated ? new Date(updated).toISOString().replace("Z", "") : undefined, folderId: parentId === workspaceId ? null : convertId(parentId), sortPriority: sortKey, name: r.name, @@ -165,14 +165,14 @@ function importGrpcRequest( url: r.url, service, method, - message: r.body?.text ?? '', + message: r.body?.text ?? "", metadata: (r.metadata ?? []) .map((h: any) => ({ enabled: !h.disabled, - name: h.name ?? '', - value: h.value ?? '', + name: h.name ?? "", + value: h.value ?? "", })) - .filter(({ name, value }: any) => name !== '' || value !== ''), + .filter(({ name, value }: any) => name !== "" || value !== ""), }; } @@ -180,24 +180,24 @@ function importWebsocketRequest( r: any, workspaceId: string, parentId: string, -): PartialImportResources['websocketRequests'][0] { +): PartialImportResources["websocketRequests"][0] { const id = r.meta?.id ?? r._id; const created = r.meta?.created ?? r.created; const updated = r.meta?.modified ?? r.updated; const sortKey = r.meta?.sortKey ?? r.sortKey; return { - model: 'websocket_request', + model: "websocket_request", id: convertId(id), workspaceId: convertId(workspaceId), - createdAt: created ? new Date(created).toISOString().replace('Z', '') : undefined, - updatedAt: updated ? new Date(updated).toISOString().replace('Z', '') : undefined, + createdAt: created ? new Date(created).toISOString().replace("Z", "") : undefined, + updatedAt: updated ? new Date(updated).toISOString().replace("Z", "") : undefined, folderId: parentId === workspaceId ? null : convertId(parentId), sortPriority: sortKey, name: r.name, description: r.description || undefined, url: r.url, - message: r.body?.text ?? '', + message: r.body?.text ?? "", ...importHeaders(r), ...importAuthentication(r), }; @@ -207,23 +207,23 @@ function importHeaders(obj: any) { const headers = (obj.headers ?? []) .map((h: any) => ({ enabled: !h.disabled, - name: h.name ?? '', - value: h.value ?? '', + name: h.name ?? "", + value: h.value ?? "", })) - .filter(({ name, value }: any) => name !== '' || value !== ''); + .filter(({ name, value }: any) => name !== "" || value !== ""); return { headers } as const; } function importAuthentication(obj: any) { let authenticationType: string | null = null; let authentication = {}; - if (obj.authentication?.type === 'bearer') { - authenticationType = 'bearer'; + if (obj.authentication?.type === "bearer") { + authenticationType = "bearer"; authentication = { token: obj.authentication.token, }; - } else if (obj.authentication?.type === 'basic') { - authenticationType = 'basic'; + } else if (obj.authentication?.type === "basic") { + authenticationType = "basic"; authentication = { username: obj.authentication.username, password: obj.authentication.password, @@ -238,26 +238,26 @@ function importFolder( workspaceId: string, parentId: string, ): { - folder: PartialImportResources['folders'][0]; - environment: PartialImportResources['environments'][0] | null; + folder: PartialImportResources["folders"][0]; + environment: PartialImportResources["environments"][0] | null; } { const id = f.meta?.id ?? f._id; const created = f.meta?.created ?? f.created; const updated = f.meta?.modified ?? f.updated; const sortKey = f.meta?.sortKey ?? f.sortKey; - let environment: PartialImportResources['environments'][0] | null = null; + let environment: PartialImportResources["environments"][0] | null = null; if (Object.keys(f.environment ?? {}).length > 0) { environment = { id: convertId(`${id}folder`), - createdAt: created ? new Date(created).toISOString().replace('Z', '') : undefined, - updatedAt: updated ? new Date(updated).toISOString().replace('Z', '') : undefined, + createdAt: created ? new Date(created).toISOString().replace("Z", "") : undefined, + updatedAt: updated ? new Date(updated).toISOString().replace("Z", "") : undefined, workspaceId: convertId(workspaceId), public: true, - parentModel: 'folder', + parentModel: "folder", parentId: convertId(id), - model: 'environment', - name: 'Folder Environment', + model: "environment", + name: "Folder Environment", variables: Object.entries(f.environment ?? {}).map(([name, value]) => ({ enabled: true, name, @@ -268,10 +268,10 @@ function importFolder( return { folder: { - model: 'folder', + model: "folder", id: convertId(id), - createdAt: created ? new Date(created).toISOString().replace('Z', '') : undefined, - updatedAt: updated ? new Date(updated).toISOString().replace('Z', '') : undefined, + createdAt: created ? new Date(created).toISOString().replace("Z", "") : undefined, + updatedAt: updated ? new Date(updated).toISOString().replace("Z", "") : undefined, folderId: parentId === workspaceId ? null : convertId(parentId), sortPriority: sortKey, workspaceId: convertId(workspaceId), @@ -288,7 +288,7 @@ function importEnvironment( e: any, workspaceId: string, isParent?: boolean, -): PartialImportResources['environments'][0] { +): PartialImportResources["environments"][0] { const id = e.meta?.id ?? e._id; const created = e.meta?.created ?? e.created; const updated = e.meta?.modified ?? e.updated; @@ -296,14 +296,14 @@ function importEnvironment( return { id: convertId(id), - createdAt: created ? new Date(created).toISOString().replace('Z', '') : undefined, - updatedAt: updated ? new Date(updated).toISOString().replace('Z', '') : undefined, + createdAt: created ? new Date(created).toISOString().replace("Z", "") : undefined, + updatedAt: updated ? new Date(updated).toISOString().replace("Z", "") : undefined, workspaceId: convertId(workspaceId), public: !e.isPrivate, sortPriority: sortKey, - parentModel: isParent ? 'workspace' : 'environment', + parentModel: isParent ? "workspace" : "environment", parentId: null, - model: 'environment', + model: "environment", name: e.name, variables: Object.entries(e.data ?? {}).map(([name, value]) => ({ enabled: true, diff --git a/plugins/importer-insomnia/tests/index.test.ts b/plugins/importer-insomnia/tests/index.test.ts index 6952c520..d77f2589 100644 --- a/plugins/importer-insomnia/tests/index.test.ts +++ b/plugins/importer-insomnia/tests/index.test.ts @@ -1,23 +1,23 @@ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { describe, expect, test } from 'vite-plus/test'; -import YAML from 'yaml'; -import { convertInsomnia } from '../src'; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { describe, expect, test } from "vite-plus/test"; +import YAML from "yaml"; +import { convertInsomnia } from "../src"; -describe('importer-yaak', () => { - const p = path.join(__dirname, 'fixtures'); +describe("importer-yaak", () => { + const p = path.join(__dirname, "fixtures"); const fixtures = fs.readdirSync(p); for (const fixture of fixtures) { - if (fixture.includes('.output')) { + if (fixture.includes(".output")) { continue; } test(`Imports ${fixture}`, () => { - const contents = fs.readFileSync(path.join(p, fixture), 'utf-8'); + const contents = fs.readFileSync(path.join(p, fixture), "utf-8"); const expected = fs.readFileSync( - path.join(p, fixture.replace(/.input\..*/, '.output.json')), - 'utf-8', + path.join(p, fixture.replace(/.input\..*/, ".output.json")), + "utf-8", ); const result = convertInsomnia(contents); // console.log(JSON.stringify(result, null, 2)) diff --git a/plugins/importer-openapi/package.json b/plugins/importer-openapi/package.json index 642b0593..a78d90b1 100644 --- a/plugins/importer-openapi/package.json +++ b/plugins/importer-openapi/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/importer-openapi", "displayName": "OpenAPI Importer", - "description": "Import API specifications from OpenAPI/Swagger format", - "private": true, "version": "0.1.0", + "private": true, + "description": "Import API specifications from OpenAPI/Swagger format", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/importer-openapi/src/index.ts b/plugins/importer-openapi/src/index.ts index 9e0e83e0..a5ed442e 100644 --- a/plugins/importer-openapi/src/index.ts +++ b/plugins/importer-openapi/src/index.ts @@ -1,12 +1,12 @@ -import { convertPostman } from '@yaak/importer-postman/src'; -import type { Context, PluginDefinition } from '@yaakapp/api'; -import type { ImportPluginResponse } from '@yaakapp/api/lib/plugins/ImporterPlugin'; -import { convert } from 'openapi-to-postmanv2'; +import { convertPostman } from "@yaak/importer-postman/src"; +import type { Context, PluginDefinition } from "@yaakapp/api"; +import type { ImportPluginResponse } from "@yaakapp/api/lib/plugins/ImporterPlugin"; +import { convert } from "openapi-to-postmanv2"; export const plugin: PluginDefinition = { importer: { - name: 'OpenAPI', - description: 'Import OpenAPI collections', + name: "OpenAPI", + description: "Import OpenAPI collections", onImport(_ctx: Context, args: { text: string }) { return convertOpenApi(args.text); }, @@ -19,7 +19,7 @@ export async function convertOpenApi(contents: string): Promise { // oxlint-disable-next-line no-explicit-any - convert({ type: 'string', data: contents }, {}, (err, result: any) => { + convert({ type: "string", data: contents }, {}, (err, result: any) => { if (err != null) reject(err); if (Array.isArray(result.output) && result.output.length > 0) { diff --git a/plugins/importer-openapi/tests/fixtures/petstore.yaml b/plugins/importer-openapi/tests/fixtures/petstore.yaml index f5271a59..21866547 100644 --- a/plugins/importer-openapi/tests/fixtures/petstore.yaml +++ b/plugins/importer-openapi/tests/fixtures/petstore.yaml @@ -13,23 +13,23 @@ info: - [The source API definition for the Pet Store](https://github.com/swagger-api/swagger-petstore/blob/master/src/main/resources/openapi.yaml) version: 1.0.20-SNAPSHOT title: Swagger Petstore - OpenAPI 3.0 - termsOfService: 'http://swagger.io/terms/' + termsOfService: "http://swagger.io/terms/" contact: email: apiteam@swagger.io license: name: Apache 2.0 - url: 'http://www.apache.org/licenses/LICENSE-2.0.html' + url: "http://www.apache.org/licenses/LICENSE-2.0.html" tags: - name: pet description: Everything about your Pets externalDocs: description: Find out more - url: 'http://swagger.io' + url: "http://swagger.io" - name: store description: Access to Petstore orders externalDocs: description: Find out more about our store - url: 'http://swagger.io' + url: "http://swagger.io" - name: user description: Operations about user paths: @@ -41,34 +41,34 @@ paths: description: Add a new pet to the store operationId: addPet responses: - '200': + "200": description: Successful operation content: application/xml: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/json: schema: - $ref: '#/components/schemas/Pet' - '405': + $ref: "#/components/schemas/Pet" + "405": description: Invalid input security: - petstore_auth: - - 'write:pets' - - 'read:pets' + - "write:pets" + - "read:pets" requestBody: description: Create a new pet in the store required: true content: application/json: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/xml: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/x-www-form-urlencoded: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" put: tags: - pet @@ -76,38 +76,38 @@ paths: description: Update an existing pet by Id operationId: updatePet responses: - '200': + "200": description: Successful operation content: application/xml: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/json: schema: - $ref: '#/components/schemas/Pet' - '400': + $ref: "#/components/schemas/Pet" + "400": description: Invalid ID supplied - '404': + "404": description: Pet not found - '405': + "405": description: Validation exception security: - petstore_auth: - - 'write:pets' - - 'read:pets' + - "write:pets" + - "read:pets" requestBody: description: Update an existent pet in the store required: true content: application/json: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/xml: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/x-www-form-urlencoded: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" /pet/findByStatus: get: tags: @@ -129,25 +129,25 @@ paths: - sold default: available responses: - '200': + "200": description: successful operation content: application/xml: schema: type: array items: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/json: schema: type: array items: - $ref: '#/components/schemas/Pet' - '400': + $ref: "#/components/schemas/Pet" + "400": description: Invalid status value security: - petstore_auth: - - 'write:pets' - - 'read:pets' + - "write:pets" + - "read:pets" /pet/findByTags: get: tags: @@ -168,26 +168,26 @@ paths: items: type: string responses: - '200': + "200": description: successful operation content: application/xml: schema: type: array items: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/json: schema: type: array items: - $ref: '#/components/schemas/Pet' - '400': + $ref: "#/components/schemas/Pet" + "400": description: Invalid tag value security: - petstore_auth: - - 'write:pets' - - 'read:pets' - '/pet/{petId}': + - "write:pets" + - "read:pets" + "/pet/{petId}": get: tags: - pet @@ -203,29 +203,29 @@ paths: type: integer format: int64 responses: - '200': + "200": description: successful operation content: application/xml: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/json: schema: - $ref: '#/components/schemas/Pet' - '400': + $ref: "#/components/schemas/Pet" + "400": description: Invalid ID supplied - '404': + "404": description: Pet not found security: - api_key: [] - petstore_auth: - - 'write:pets' - - 'read:pets' + - "write:pets" + - "read:pets" post: tags: - pet summary: Updates a pet in the store with form data - description: '' + description: "" operationId: updatePetWithForm parameters: - name: petId @@ -246,22 +246,22 @@ paths: schema: type: string responses: - '405': + "405": description: Invalid input security: - petstore_auth: - - 'write:pets' - - 'read:pets' + - "write:pets" + - "read:pets" delete: tags: - pet summary: Deletes a pet - description: '' + description: "" operationId: deletePet parameters: - name: api_key in: header - description: '' + description: "" required: false schema: type: string @@ -273,18 +273,18 @@ paths: type: integer format: int64 responses: - '400': + "400": description: Invalid pet value security: - petstore_auth: - - 'write:pets' - - 'read:pets' - '/pet/{petId}/uploadImage': + - "write:pets" + - "read:pets" + "/pet/{petId}/uploadImage": post: tags: - pet summary: uploads an image - description: '' + description: "" operationId: uploadFile parameters: - name: petId @@ -301,16 +301,16 @@ paths: schema: type: string responses: - '200': + "200": description: successful operation content: application/json: schema: - $ref: '#/components/schemas/ApiResponse' + $ref: "#/components/schemas/ApiResponse" security: - petstore_auth: - - 'write:pets' - - 'read:pets' + - "write:pets" + - "read:pets" requestBody: content: application/octet-stream: @@ -326,7 +326,7 @@ paths: operationId: getInventory x-swagger-router-controller: OrderController responses: - '200': + "200": description: successful operation content: application/json: @@ -346,26 +346,26 @@ paths: operationId: placeOrder x-swagger-router-controller: OrderController responses: - '200': + "200": description: successful operation content: application/json: schema: - $ref: '#/components/schemas/Order' - '405': + $ref: "#/components/schemas/Order" + "405": description: Invalid input requestBody: content: application/json: schema: - $ref: '#/components/schemas/Order' + $ref: "#/components/schemas/Order" application/xml: schema: - $ref: '#/components/schemas/Order' + $ref: "#/components/schemas/Order" application/x-www-form-urlencoded: schema: - $ref: '#/components/schemas/Order' - '/store/order/{orderId}': + $ref: "#/components/schemas/Order" + "/store/order/{orderId}": get: tags: - store @@ -384,18 +384,18 @@ paths: type: integer format: int64 responses: - '200': + "200": description: successful operation content: application/xml: schema: - $ref: '#/components/schemas/Order' + $ref: "#/components/schemas/Order" application/json: schema: - $ref: '#/components/schemas/Order' - '400': + $ref: "#/components/schemas/Order" + "400": description: Invalid ID supplied - '404': + "404": description: Order not found delete: tags: @@ -415,9 +415,9 @@ paths: type: integer format: int64 responses: - '400': + "400": description: Invalid ID supplied - '404': + "404": description: Order not found /user: post: @@ -432,40 +432,40 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/xml: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" requestBody: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/xml: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/x-www-form-urlencoded: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" description: Created user object /user/createWithList: post: tags: - user summary: Creates list of users with given input array - description: 'Creates list of users with given input array' + description: "Creates list of users with given input array" x-swagger-router-controller: UserController operationId: createUsersWithListInput responses: - '200': + "200": description: Successful operation content: application/xml: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" default: description: successful operation requestBody: @@ -474,13 +474,13 @@ paths: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" /user/login: get: tags: - user summary: Logs user into the system - description: '' + description: "" operationId: loginUser parameters: - name: username @@ -496,7 +496,7 @@ paths: schema: type: string responses: - '200': + "200": description: successful operation headers: X-Rate-Limit: @@ -516,46 +516,46 @@ paths: application/json: schema: type: string - '400': + "400": description: Invalid username/password supplied /user/logout: get: tags: - user summary: Logs out current logged in user session - description: '' + description: "" operationId: logoutUser parameters: [] responses: default: description: successful operation - '/user/{username}': + "/user/{username}": get: tags: - user summary: Get user by user name - description: '' + description: "" operationId: getUserByName parameters: - name: username in: path - description: 'The name that needs to be fetched. Use user1 for testing. ' + description: "The name that needs to be fetched. Use user1 for testing. " required: true schema: type: string responses: - '200': + "200": description: successful operation content: application/xml: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/json: schema: - $ref: '#/components/schemas/User' - '400': + $ref: "#/components/schemas/User" + "400": description: Invalid username supplied - '404': + "404": description: User not found put: tags: @@ -579,13 +579,13 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/xml: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" application/x-www-form-urlencoded: schema: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" delete: tags: - user @@ -600,13 +600,13 @@ paths: schema: type: string responses: - '400': + "400": description: Invalid username supplied - '404': + "404": description: User not found externalDocs: description: Find out more about Swagger - url: 'http://swagger.io' + url: "http://swagger.io" components: schemas: Order: @@ -652,7 +652,7 @@ components: address: type: array items: - $ref: '#/components/schemas/Address' + $ref: "#/components/schemas/Address" xml: wrapped: true name: addresses @@ -747,7 +747,7 @@ components: type: string example: doggie category: - $ref: '#/components/schemas/Category' + $ref: "#/components/schemas/Category" photoUrls: type: array xml: @@ -761,7 +761,7 @@ components: xml: wrapped: true items: - $ref: '#/components/schemas/Tag' + $ref: "#/components/schemas/Tag" xml: name: tag status: @@ -784,17 +784,17 @@ components: message: type: string xml: - name: '##default' + name: "##default" type: object requestBodies: Pet: content: application/json: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" application/xml: schema: - $ref: '#/components/schemas/Pet' + $ref: "#/components/schemas/Pet" description: Pet object that needs to be added to the store UserArray: content: @@ -802,17 +802,17 @@ components: schema: type: array items: - $ref: '#/components/schemas/User' + $ref: "#/components/schemas/User" description: List of user object securitySchemes: petstore_auth: type: oauth2 flows: implicit: - authorizationUrl: 'https://petstore.swagger.io/oauth/authorize' + authorizationUrl: "https://petstore.swagger.io/oauth/authorize" scopes: - 'write:pets': modify pets in your account - 'read:pets': read your pets + "write:pets": modify pets in your account + "read:pets": read your pets api_key: type: apiKey name: api_key diff --git a/plugins/importer-openapi/tests/index.test.ts b/plugins/importer-openapi/tests/index.test.ts index 565806c8..efb29c9a 100644 --- a/plugins/importer-openapi/tests/index.test.ts +++ b/plugins/importer-openapi/tests/index.test.ts @@ -1,22 +1,22 @@ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { describe, expect, test } from 'vite-plus/test'; -import { convertOpenApi } from '../src'; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { describe, expect, test } from "vite-plus/test"; +import { convertOpenApi } from "../src"; -describe('importer-openapi', () => { - const p = path.join(__dirname, 'fixtures'); +describe("importer-openapi", () => { + const p = path.join(__dirname, "fixtures"); const fixtures = fs.readdirSync(p); - test('Maps operation description to request description', async () => { + test("Maps operation description to request description", async () => { const imported = await convertOpenApi( JSON.stringify({ - openapi: '3.0.0', - info: { title: 'Description Test', version: '1.0.0' }, + openapi: "3.0.0", + info: { title: "Description Test", version: "1.0.0" }, paths: { - '/klanten': { + "/klanten": { get: { - description: 'Lijst van klanten', - responses: { '200': { description: 'ok' } }, + description: "Lijst van klanten", + responses: { "200": { description: "ok" } }, }, }, }, @@ -25,24 +25,24 @@ describe('importer-openapi', () => { expect(imported?.resources.httpRequests).toEqual([ expect.objectContaining({ - description: 'Lijst van klanten', + description: "Lijst van klanten", }), ]); }); - test('Skips invalid file', async () => { - const imported = await convertOpenApi('{}'); + test("Skips invalid file", async () => { + const imported = await convertOpenApi("{}"); expect(imported).toBeUndefined(); }); for (const fixture of fixtures) { test(`Imports ${fixture}`, async () => { - const contents = fs.readFileSync(path.join(p, fixture), 'utf-8'); + const contents = fs.readFileSync(path.join(p, fixture), "utf-8"); const imported = await convertOpenApi(contents); expect(imported?.resources.workspaces).toEqual([ expect.objectContaining({ - name: 'Swagger Petstore - OpenAPI 3.0', - description: expect.stringContaining('This is a sample Pet Store Server'), + name: "Swagger Petstore - OpenAPI 3.0", + description: expect.stringContaining("This is a sample Pet Store Server"), }), ]); expect(imported?.resources.httpRequests.length).toBe(19); diff --git a/plugins/importer-postman-environment/package.json b/plugins/importer-postman-environment/package.json index b60c3836..4b57e68b 100644 --- a/plugins/importer-postman-environment/package.json +++ b/plugins/importer-postman-environment/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/importer-postman-environment", "displayName": "Postman Environment Importer", - "description": "Import environments from Postman", - "private": true, "version": "0.1.0", + "private": true, + "description": "Import environments from Postman", "main": "./build/index.js", "scripts": { "build": "yaakcli build", diff --git a/plugins/importer-postman-environment/src/index.ts b/plugins/importer-postman-environment/src/index.ts index 8cb31764..54333943 100644 --- a/plugins/importer-postman-environment/src/index.ts +++ b/plugins/importer-postman-environment/src/index.ts @@ -5,20 +5,20 @@ import type { PartialImportResources, PluginDefinition, Workspace, -} from '@yaakapp/api'; -import type { ImportPluginResponse } from '@yaakapp/api/lib/plugins/ImporterPlugin'; +} from "@yaakapp/api"; +import type { ImportPluginResponse } from "@yaakapp/api/lib/plugins/ImporterPlugin"; type AtLeast = Partial & Pick; interface ExportResources { - workspaces: AtLeast[]; - environments: AtLeast[]; + workspaces: AtLeast[]; + environments: AtLeast[]; } export const plugin: PluginDefinition = { importer: { - name: 'Postman Environment', - description: 'Import postman environment exports', + name: "Postman Environment", + description: "Import postman environment exports", onImport(_ctx: Context, args: { text: string }) { return convertPostmanEnvironment(args.text); }, @@ -38,9 +38,9 @@ export function convertPostmanEnvironment(contents: string): ImportPluginRespons type?: string; }>(root.values); const scope = root._postman_variable_scope; - const hasEnvMarkers = typeof scope === 'string'; + const hasEnvMarkers = typeof scope === "string"; - if (values.length === 0 || (!hasEnvMarkers && typeof root.name !== 'string')) { + if (values.length === 0 || (!hasEnvMarkers && typeof root.name !== "string")) { // Not a Postman environment file, skip return; } @@ -53,18 +53,18 @@ export function convertPostmanEnvironment(contents: string): ImportPluginRespons const envVariables = values .map((v) => ({ enabled: v.enabled ?? true, - name: String(v.key ?? ''), + name: String(v.key ?? ""), value: String(v.value), description: v.description ? String(v.description) : null, })) .filter((v) => v.name.length > 0); - const environment: ExportResources['environments'][0] = { - model: 'environment', - id: generateId('environment'), - name: root.name ? String(root.name) : 'Environment', - workspaceId: 'CURRENT_WORKSPACE', - parentModel: 'environment', + const environment: ExportResources["environments"][0] = { + model: "environment", + id: generateId("environment"), + name: root.name ? String(root.name) : "Environment", + workspaceId: "CURRENT_WORKSPACE", + parentModel: "environment", parentId: null, variables: envVariables, }; @@ -86,26 +86,26 @@ function parseJSONToRecord(jsonStr: string): Record | null { } function toRecord(value: unknown): Record { - if (value && typeof value === 'object' && !Array.isArray(value)) { + if (value && typeof value === "object" && !Array.isArray(value)) { return value as Record; } return {} as Record; } function toArray(value: unknown): T[] { - if (Object.prototype.toString.call(value) === '[object Array]') return value as T[]; + if (Object.prototype.toString.call(value) === "[object Array]") return value as T[]; return [] as T[]; } /** Recursively render all nested object properties */ function convertTemplateSyntax(obj: T): T { - if (typeof obj === 'string') { + if (typeof obj === "string") { return obj.replace(/{{\s*(_\.)?([^}]*)\s*}}/g, (_m, _dot, expr) => `\${[${expr.trim()}]}`) as T; } if (Array.isArray(obj) && obj != null) { return obj.map(convertTemplateSyntax) as T; } - if (typeof obj === 'object' && obj != null) { + if (typeof obj === "object" && obj != null) { return Object.fromEntries( Object.entries(obj as Record).map(([k, v]) => [k, convertTemplateSyntax(v)]), ) as T; @@ -117,7 +117,7 @@ function deleteUndefinedAttrs(obj: T): T { if (Array.isArray(obj) && obj != null) { return obj.map(deleteUndefinedAttrs) as T; } - if (typeof obj === 'object' && obj != null) { + if (typeof obj === "object" && obj != null) { return Object.fromEntries( Object.entries(obj as Record) .filter(([, v]) => v !== undefined) diff --git a/plugins/importer-postman-environment/tests/index.test.ts b/plugins/importer-postman-environment/tests/index.test.ts index 88c8a16a..a550e50b 100644 --- a/plugins/importer-postman-environment/tests/index.test.ts +++ b/plugins/importer-postman-environment/tests/index.test.ts @@ -1,20 +1,20 @@ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { describe, expect, test } from 'vite-plus/test'; -import { convertPostmanEnvironment } from '../src'; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { describe, expect, test } from "vite-plus/test"; +import { convertPostmanEnvironment } from "../src"; -describe('importer-postman-environment', () => { - const p = path.join(__dirname, 'fixtures'); +describe("importer-postman-environment", () => { + const p = path.join(__dirname, "fixtures"); const fixtures = fs.readdirSync(p); for (const fixture of fixtures) { - if (fixture.includes('.output')) { + if (fixture.includes(".output")) { continue; } test(`Imports ${fixture}`, () => { - const contents = fs.readFileSync(path.join(p, fixture), 'utf-8'); - const expected = fs.readFileSync(path.join(p, fixture.replace('.input', '.output')), 'utf-8'); + const contents = fs.readFileSync(path.join(p, fixture), "utf-8"); + const expected = fs.readFileSync(path.join(p, fixture.replace(".input", ".output")), "utf-8"); const result = convertPostmanEnvironment(contents); expect(result).toEqual(JSON.parse(expected)); }); diff --git a/plugins/importer-postman/package.json b/plugins/importer-postman/package.json index 8dbb916f..6c46a171 100644 --- a/plugins/importer-postman/package.json +++ b/plugins/importer-postman/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/importer-postman", "displayName": "Postman Importer", - "description": "Import collections from Postman", - "private": true, "version": "0.1.0", + "private": true, + "description": "Import collections from Postman", "main": "./build/index.js", "scripts": { "build": "yaakcli build", diff --git a/plugins/importer-postman/src/index.ts b/plugins/importer-postman/src/index.ts index 68f4a2d8..a36142b6 100644 --- a/plugins/importer-postman/src/index.ts +++ b/plugins/importer-postman/src/index.ts @@ -9,26 +9,26 @@ import type { PartialImportResources, PluginDefinition, Workspace, -} from '@yaakapp/api'; -import type { ImportPluginResponse } from '@yaakapp/api/lib/plugins/ImporterPlugin'; +} from "@yaakapp/api"; +import type { ImportPluginResponse } from "@yaakapp/api/lib/plugins/ImporterPlugin"; -const POSTMAN_2_1_0_SCHEMA = 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json'; -const POSTMAN_2_0_0_SCHEMA = 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json'; +const POSTMAN_2_1_0_SCHEMA = "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"; +const POSTMAN_2_0_0_SCHEMA = "https://schema.getpostman.com/json/collection/v2.0.0/collection.json"; const VALID_SCHEMAS = [POSTMAN_2_0_0_SCHEMA, POSTMAN_2_1_0_SCHEMA]; type AtLeast = Partial & Pick; interface ExportResources { - workspaces: AtLeast[]; - environments: AtLeast[]; - httpRequests: AtLeast[]; - folders: AtLeast[]; + workspaces: AtLeast[]; + environments: AtLeast[]; + httpRequests: AtLeast[]; + folders: AtLeast[]; } export const plugin: PluginDefinition = { importer: { - name: 'Postman', - description: 'Import postman collections', + name: "Postman", + description: "Import postman collections", onImport(_ctx: Context, args: { text: string }) { return convertPostman(args.text); }, @@ -41,7 +41,7 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin const info = toRecord(root.info); const isValidSchema = VALID_SCHEMAS.includes( - typeof info.schema === 'string' ? info.schema : 'n/a', + typeof info.schema === "string" ? info.schema : "n/a", ); if (!isValidSchema || !Array.isArray(root.item)) { return; @@ -56,22 +56,22 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin folders: [], }; - const workspace: ExportResources['workspaces'][0] = { - model: 'workspace', - id: generateId('workspace'), - name: info.name ? String(info.name) : 'Postman Import', + const workspace: ExportResources["workspaces"][0] = { + model: "workspace", + id: generateId("workspace"), + name: info.name ? String(info.name) : "Postman Import", description: importDescription(info.description), ...globalAuth, }; exportResources.workspaces.push(workspace); // Create the base environment - const environment: ExportResources['environments'][0] = { - model: 'environment', - id: generateId('environment'), - name: 'Global Variables', + const environment: ExportResources["environments"][0] = { + model: "environment", + id: generateId("environment"), + name: "Global Variables", workspaceId: workspace.id, - parentModel: 'workspace', + parentModel: "workspace", parentId: null, variables: toArray<{ key: string; value: string }>(root.variable).map((v) => ({ @@ -83,12 +83,12 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin let sortPriorityIndex = 0; const importItem = (v: Record, folderId: string | null = null) => { - if (typeof v.name === 'string' && Array.isArray(v.item)) { - const folder: ExportResources['folders'][0] = { - model: 'folder', + if (typeof v.name === "string" && Array.isArray(v.item)) { + const folder: ExportResources["folders"][0] = { + model: "folder", sortPriority: sortPriorityIndex++, workspaceId: workspace.id, - id: generateId('folder'), + id: generateId("folder"), name: v.name, folderId, }; @@ -96,7 +96,7 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin for (const child of v.item) { importItem(child, folder.id); } - } else if (typeof v.name === 'string' && 'request' in v) { + } else if (typeof v.name === "string" && "request" in v) { const r = toRecord(v.request); const bodyPatch = importBody(r.body); const requestAuth = importAuth(r.auth); @@ -126,14 +126,14 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin const { url, urlParameters } = convertUrl(r.url); - const request: ExportResources['httpRequests'][0] = { - model: 'http_request', - id: generateId('http_request'), + const request: ExportResources["httpRequests"][0] = { + model: "http_request", + id: generateId("http_request"), workspaceId: workspace.id, folderId, name: v.name, description: importDescription(r.description), - method: typeof r.method === 'string' ? r.method : 'GET', + method: typeof r.method === "string" ? r.method : "GET", url, urlParameters, body: bodyPatch.body, @@ -144,7 +144,7 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin }; exportResources.httpRequests.push(request); } else { - console.log('Unknown item', v, folderId); + console.log("Unknown item", v, folderId); } }; @@ -159,53 +159,53 @@ export function convertPostman(contents: string): ImportPluginResponse | undefin return { resources }; } -function convertUrl(rawUrl: unknown): Pick { - if (typeof rawUrl === 'string') { +function convertUrl(rawUrl: unknown): Pick { + if (typeof rawUrl === "string") { return { url: rawUrl, urlParameters: [] }; } const url = toRecord(rawUrl); - let v = ''; + let v = ""; - if ('protocol' in url && typeof url.protocol === 'string') { + if ("protocol" in url && typeof url.protocol === "string") { v += `${url.protocol}://`; } - if ('host' in url) { - v += `${Array.isArray(url.host) ? url.host.join('.') : String(url.host)}`; + if ("host" in url) { + v += `${Array.isArray(url.host) ? url.host.join(".") : String(url.host)}`; } - if ('port' in url && typeof url.port === 'string') { + if ("port" in url && typeof url.port === "string") { v += `:${url.port}`; } - if ('path' in url && Array.isArray(url.path) && url.path.length > 0) { - v += `/${Array.isArray(url.path) ? url.path.join('/') : url.path}`; + if ("path" in url && Array.isArray(url.path) && url.path.length > 0) { + v += `/${Array.isArray(url.path) ? url.path.join("/") : url.path}`; } const params: HttpUrlParameter[] = []; - if ('query' in url && Array.isArray(url.query) && url.query.length > 0) { + if ("query" in url && Array.isArray(url.query) && url.query.length > 0) { for (const query of url.query) { params.push({ - name: query.key ?? '', - value: query.value ?? '', + name: query.key ?? "", + value: query.value ?? "", enabled: !query.disabled, }); } } - if ('variable' in url && Array.isArray(url.variable) && url.variable.length > 0) { + if ("variable" in url && Array.isArray(url.variable) && url.variable.length > 0) { for (const v of url.variable) { params.push({ - name: `:${v.key ?? ''}`, - value: v.value ?? '', + name: `:${v.key ?? ""}`, + value: v.value ?? "", enabled: !v.disabled, }); } } - if ('hash' in url && typeof url.hash === 'string') { + if ("hash" in url && typeof url.hash === "string") { v += `#${url.hash}`; } @@ -214,7 +214,7 @@ function convertUrl(rawUrl: unknown): Pick return { url: v, urlParameters: params }; } -function importAuth(rawAuth: unknown): Pick { +function importAuth(rawAuth: unknown): Pick { const auth = toRecord>(rawAuth); // Helper: Postman stores auth params as an array of { key, value, ... } @@ -223,7 +223,7 @@ function importAuth(rawAuth: unknown): Pick = {}; for (const i of v) { const ii = toRecord(i); - if (typeof ii.key === 'string') { + if (typeof ii.key === "string") { o[ii.key] = ii.value; } } @@ -232,39 +232,39 @@ function importAuth(rawAuth: unknown): Pick = {}; - if (grantType === 'authorization_code_with_pkce') { - grantType = 'authorization_code'; + if (grantType === "authorization_code_with_pkce") { + grantType = "authorization_code"; pkcePatch = - o.grant_type === 'authorization_code_with_pkce' + o.grant_type === "authorization_code_with_pkce" ? { usePkce: true, pkceChallengeMethod: o.challengeAlgorithm ?? undefined, pkceCodeVerifier: o.code_verifier != null ? String(o.code_verifier) : undefined, } : {}; - } else if (grantType === 'password_credentials') { - grantType = 'password'; + } else if (grantType === "password_credentials") { + grantType = "password"; } const accessTokenUrl = o.accessTokenUrl != null ? String(o.accessTokenUrl) : undefined; @@ -327,8 +327,8 @@ function importAuth(rawAuth: unknown): Pick = {}; - if (grantType === 'authorization_code') { + if (grantType === "authorization_code") { grantPatch = { clientSecret, authorizationUrl, @@ -345,16 +345,16 @@ function importAuth(rawAuth: unknown): Pick; - return { authenticationType: 'oauth2', authentication }; + return { authenticationType: "oauth2", authentication }; } return { authenticationType: null, authentication: {} }; } -function importBody(rawBody: unknown): Pick { +function importBody(rawBody: unknown): Pick { const body = toRecord(rawBody) as { mode: string; graphql: { query?: string; variables?: string }; @@ -386,21 +386,21 @@ function importBody(rawBody: unknown): Pick[0]>(body.urlencoded).map((f) => ({ enabled: !f.disabled, - name: f.key ?? '', - value: f.value ?? '', + name: f.key ?? "", + value: f.value ?? "", })), }, }; } - if (body.mode === 'formdata') { + if (body.mode === "formdata") { return { headers: [ { - name: 'Content-Type', - value: 'multipart/form-data', + name: "Content-Type", + value: "multipart/form-data", enabled: true, }, ], - bodyType: 'multipart/form-data', + bodyType: "multipart/form-data", body: { form: toArray[0]>(body.formdata).map((f) => f.src != null ? { enabled: !f.disabled, contentType: f.contentType ?? null, - name: f.key ?? '', - file: f.src ?? '', + name: f.key ?? "", + file: f.src ?? "", } : { enabled: !f.disabled, - name: f.key ?? '', - value: f.value ?? '', + name: f.key ?? "", + value: f.value ?? "", }, ), }, }; } - if (body.mode === 'raw') { + if (body.mode === "raw") { return { headers: [ { - name: 'Content-Type', - value: body.options?.raw?.language === 'json' ? 'application/json' : '', + name: "Content-Type", + value: body.options?.raw?.language === "json" ? "application/json" : "", enabled: true, }, ], - bodyType: body.options?.raw?.language === 'json' ? 'application/json' : 'other', + bodyType: body.options?.raw?.language === "json" ? "application/json" : "other", body: { - text: body.raw ?? '', + text: body.raw ?? "", }, }; } - if (body.mode === 'file') { + if (body.mode === "file") { return { headers: [], - bodyType: 'binary', + bodyType: "binary", body: { filePath: body.file?.src, }, @@ -491,14 +491,14 @@ function parseJSONToRecord(jsonStr: string): Record | null { } function toRecord(value: unknown): Record { - if (value && typeof value === 'object' && !Array.isArray(value)) { + if (value && typeof value === "object" && !Array.isArray(value)) { return value as Record; } return {}; } function toArray(value: unknown): T[] { - if (Object.prototype.toString.call(value) === '[object Array]') return value as T[]; + if (Object.prototype.toString.call(value) === "[object Array]") return value as T[]; return []; } @@ -507,13 +507,13 @@ function importDescription(rawDescription: unknown): string | undefined { return undefined; } - if (typeof rawDescription === 'string') { + if (typeof rawDescription === "string") { return rawDescription; } - if (typeof rawDescription === 'object' && !Array.isArray(rawDescription)) { + if (typeof rawDescription === "object" && !Array.isArray(rawDescription)) { const description = toRecord(rawDescription); - if ('content' in description && description.content != null) { + if ("content" in description && description.content != null) { return String(description.content); } return undefined; @@ -524,16 +524,16 @@ function importDescription(rawDescription: unknown): string | undefined { /** Recursively render all nested object properties */ function convertTemplateSyntax(obj: T): T { - if (typeof obj === 'string') { + if (typeof obj === "string") { return obj.replace( /{{\s*(_\.)?([^}]*)\s*}}/g, - (_m, _dot, expr) => `\${[${expr.trim().replace(/^vault:/, '')}]}`, + (_m, _dot, expr) => `\${[${expr.trim().replace(/^vault:/, "")}]}`, ) as T; } if (Array.isArray(obj) && obj != null) { return obj.map(convertTemplateSyntax) as T; } - if (typeof obj === 'object' && obj != null) { + if (typeof obj === "object" && obj != null) { return Object.fromEntries( Object.entries(obj).map(([k, v]) => [k, convertTemplateSyntax(v)]), ) as T; @@ -545,7 +545,7 @@ function deleteUndefinedAttrs(obj: T): T { if (Array.isArray(obj) && obj != null) { return obj.map(deleteUndefinedAttrs) as T; } - if (typeof obj === 'object' && obj != null) { + if (typeof obj === "object" && obj != null) { return Object.fromEntries( Object.entries(obj) .filter(([, v]) => v !== undefined) diff --git a/plugins/importer-postman/tests/index.test.ts b/plugins/importer-postman/tests/index.test.ts index 4bdcf841..399605d4 100644 --- a/plugins/importer-postman/tests/index.test.ts +++ b/plugins/importer-postman/tests/index.test.ts @@ -1,20 +1,20 @@ -import * as fs from 'node:fs'; -import * as path from 'node:path'; -import { describe, expect, test } from 'vite-plus/test'; -import { convertPostman } from '../src'; +import * as fs from "node:fs"; +import * as path from "node:path"; +import { describe, expect, test } from "vite-plus/test"; +import { convertPostman } from "../src"; -describe('importer-postman', () => { - const p = path.join(__dirname, 'fixtures'); +describe("importer-postman", () => { + const p = path.join(__dirname, "fixtures"); const fixtures = fs.readdirSync(p); for (const fixture of fixtures) { - if (fixture.includes('.output')) { + if (fixture.includes(".output")) { continue; } test(`Imports ${fixture}`, () => { - const contents = fs.readFileSync(path.join(p, fixture), 'utf-8'); - const expected = fs.readFileSync(path.join(p, fixture.replace('.input', '.output')), 'utf-8'); + const contents = fs.readFileSync(path.join(p, fixture), "utf-8"); + const expected = fs.readFileSync(path.join(p, fixture.replace(".input", ".output")), "utf-8"); const result = convertPostman(contents); // console.log(JSON.stringify(result, null, 2)) expect(JSON.stringify(result, null, 2)).toEqual( @@ -23,21 +23,21 @@ describe('importer-postman', () => { }); } - test('Imports object descriptions without [object Object]', () => { + test("Imports object descriptions without [object Object]", () => { const result = convertPostman( JSON.stringify({ info: { - name: 'Description Test', - schema: 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json', + name: "Description Test", + schema: "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", }, item: [ { - name: 'Request 1', + name: "Request 1", request: { - method: 'GET', + method: "GET", description: { - content: 'Lijst van klanten', - type: 'text/plain', + content: "Lijst van klanten", + type: "text/plain", }, }, }, @@ -47,13 +47,13 @@ describe('importer-postman', () => { expect(result?.resources.workspaces).toEqual([ expect.objectContaining({ - name: 'Description Test', + name: "Description Test", }), ]); expect(result?.resources.httpRequests).toEqual([ expect.objectContaining({ - name: 'Request 1', - description: 'Lijst van klanten', + name: "Request 1", + description: "Lijst van klanten", }), ]); }); diff --git a/plugins/importer-yaak/package.json b/plugins/importer-yaak/package.json index 3eb501d1..fe898f5f 100644 --- a/plugins/importer-yaak/package.json +++ b/plugins/importer-yaak/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/importer-yaak", "displayName": "Yaak Importer", - "description": "Import data from Yaak export files", - "private": true, "version": "0.1.0", + "private": true, + "description": "Import data from Yaak export files", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/importer-yaak/src/index.ts b/plugins/importer-yaak/src/index.ts index 93fca0dd..48ca1ad4 100644 --- a/plugins/importer-yaak/src/index.ts +++ b/plugins/importer-yaak/src/index.ts @@ -1,9 +1,9 @@ -import type { Environment, PluginDefinition } from '@yaakapp/api'; +import type { Environment, PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { importer: { - name: 'Yaak', - description: 'Yaak official format', + name: "Yaak", + description: "Yaak official format", onImport(_ctx, args) { return migrateImport(args.text); }, @@ -23,24 +23,24 @@ export function migrateImport(contents: string) { return undefined; } - const isYaakExport = 'yaakSchema' in parsed; + const isYaakExport = "yaakSchema" in parsed; if (!isYaakExport) { return; } // Migrate v1 to v2 -- changes requests to httpRequests - if ('requests' in parsed.resources) { + if ("requests" in parsed.resources) { parsed.resources.httpRequests = parsed.resources.requests; parsed.resources.requests = undefined; } // Migrate v2 to v3 for (const workspace of parsed.resources.workspaces ?? []) { - if ('variables' in workspace) { + if ("variables" in workspace) { // Create the base environment const baseEnvironment: Partial = { id: `GENERATE_ID::base_env_${workspace.id}`, - name: 'Global Variables', + name: "Global Variables", variables: workspace.variables, workspaceId: workspace.id, }; @@ -61,7 +61,7 @@ export function migrateImport(contents: string) { // Migrate v3 to v4 for (const environment of parsed.resources.environments ?? []) { - if ('environmentId' in environment) { + if ("environmentId" in environment) { environment.base = environment.environmentId == null; environment.environmentId = undefined; } @@ -69,12 +69,12 @@ export function migrateImport(contents: string) { // Migrate v4 to v5 for (const environment of parsed.resources.environments ?? []) { - if ('base' in environment && environment.base && environment.parentModel == null) { - environment.parentModel = 'workspace'; + if ("base" in environment && environment.base && environment.parentModel == null) { + environment.parentModel = "workspace"; environment.parentId = null; environment.base = undefined; - } else if ('base' in environment && !environment.base && environment.parentModel == null) { - environment.parentModel = 'environment'; + } else if ("base" in environment && !environment.base && environment.parentModel == null) { + environment.parentModel = "environment"; environment.parentId = null; environment.base = undefined; } @@ -84,5 +84,5 @@ export function migrateImport(contents: string) { } function isJSObject(obj: unknown) { - return Object.prototype.toString.call(obj) === '[object Object]'; + return Object.prototype.toString.call(obj) === "[object Object]"; } diff --git a/plugins/importer-yaak/tests/index.test.ts b/plugins/importer-yaak/tests/index.test.ts index a0626622..9b928f64 100644 --- a/plugins/importer-yaak/tests/index.test.ts +++ b/plugins/importer-yaak/tests/index.test.ts @@ -1,14 +1,14 @@ -import { describe, expect, test } from 'vite-plus/test'; -import { migrateImport } from '../src'; +import { describe, expect, test } from "vite-plus/test"; +import { migrateImport } from "../src"; -describe('importer-yaak', () => { - test('Skips invalid imports', () => { - expect(migrateImport('not JSON')).toBeUndefined(); - expect(migrateImport('[]')).toBeUndefined(); +describe("importer-yaak", () => { + test("Skips invalid imports", () => { + expect(migrateImport("not JSON")).toBeUndefined(); + expect(migrateImport("[]")).toBeUndefined(); expect(migrateImport(JSON.stringify({ resources: {} }))).toBeUndefined(); }); - test('converts schema 1 to 2', () => { + test("converts schema 1 to 2", () => { const imported = migrateImport( JSON.stringify({ yaakSchema: 1, @@ -26,23 +26,23 @@ describe('importer-yaak', () => { }), ); }); - test('converts schema 2 to 3', () => { + test("converts schema 2 to 3", () => { const imported = migrateImport( JSON.stringify({ yaakSchema: 2, resources: { environments: [ { - id: 'e_1', - workspaceId: 'w_1', - name: 'Production', - variables: [{ name: 'E1', value: 'E1!' }], + id: "e_1", + workspaceId: "w_1", + name: "Production", + variables: [{ name: "E1", value: "E1!" }], }, ], workspaces: [ { - id: 'w_1', - variables: [{ name: 'W1', value: 'W1!' }], + id: "w_1", + variables: [{ name: "W1", value: "W1!" }], }, ], }, @@ -54,23 +54,23 @@ describe('importer-yaak', () => { resources: { workspaces: [ { - id: 'w_1', + id: "w_1", }, ], environments: [ { - id: 'e_1', - workspaceId: 'w_1', - name: 'Production', - variables: [{ name: 'E1', value: 'E1!' }], - parentModel: 'environment', + id: "e_1", + workspaceId: "w_1", + name: "Production", + variables: [{ name: "E1", value: "E1!" }], + parentModel: "environment", parentId: null, }, { - id: 'GENERATE_ID::base_env_w_1', - workspaceId: 'w_1', - name: 'Global Variables', - variables: [{ name: 'W1', value: 'W1!' }], + id: "GENERATE_ID::base_env_w_1", + workspaceId: "w_1", + name: "Global Variables", + variables: [{ name: "W1", value: "W1!" }], }, ], }, @@ -78,35 +78,35 @@ describe('importer-yaak', () => { ); }); - test('converts schema 4 to 5', () => { + test("converts schema 4 to 5", () => { const imported = migrateImport( JSON.stringify({ yaakSchema: 2, resources: { environments: [ { - id: 'e_1', - workspaceId: 'w_1', + id: "e_1", + workspaceId: "w_1", base: false, - name: 'Production', - variables: [{ name: 'E1', value: 'E1!' }], + name: "Production", + variables: [{ name: "E1", value: "E1!" }], }, { - id: 'e_1', - workspaceId: 'w_1', + id: "e_1", + workspaceId: "w_1", base: true, - name: 'Global Variables', - variables: [{ name: 'G1', value: 'G1!' }], + name: "Global Variables", + variables: [{ name: "G1", value: "G1!" }], }, ], folders: [ { - id: 'f_1', + id: "f_1", }, ], workspaces: [ { - id: 'w_1', + id: "w_1", }, ], }, @@ -118,30 +118,30 @@ describe('importer-yaak', () => { resources: { workspaces: [ { - id: 'w_1', + id: "w_1", }, ], folders: [ { - id: 'f_1', + id: "f_1", }, ], environments: [ { - id: 'e_1', - workspaceId: 'w_1', - name: 'Production', - variables: [{ name: 'E1', value: 'E1!' }], - parentModel: 'environment', + id: "e_1", + workspaceId: "w_1", + name: "Production", + variables: [{ name: "E1", value: "E1!" }], + parentModel: "environment", parentId: null, }, { - id: 'e_1', - workspaceId: 'w_1', - name: 'Global Variables', - parentModel: 'workspace', + id: "e_1", + workspaceId: "w_1", + name: "Global Variables", + parentModel: "workspace", parentId: null, - variables: [{ name: 'G1', value: 'G1!' }], + variables: [{ name: "G1", value: "G1!" }], }, ], }, diff --git a/plugins/template-function-1password/package.json b/plugins/template-function-1password/package.json index 5eaf639f..b73552a6 100644 --- a/plugins/template-function-1password/package.json +++ b/plugins/template-function-1password/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-1password", "displayName": "1Password Template Functions", - "description": "Template function for accessing 1Password secrets", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template function for accessing 1Password secrets", "scripts": { "build": "run-s build:*", "build:1-build": "yaakcli build", diff --git a/plugins/template-function-1password/src/index.ts b/plugins/template-function-1password/src/index.ts index e8b0655c..bc2f2df2 100644 --- a/plugins/template-function-1password/src/index.ts +++ b/plugins/template-function-1password/src/index.ts @@ -1,8 +1,8 @@ -import crypto from 'node:crypto'; -import type { Client } from '@1password/sdk'; -import { createClient, DesktopAuth } from '@1password/sdk'; -import type { JsonPrimitive, PluginDefinition } from '@yaakapp/api'; -import type { CallTemplateFunctionArgs } from '@yaakapp-internal/plugins'; +import crypto from "node:crypto"; +import type { Client } from "@1password/sdk"; +import { createClient, DesktopAuth } from "@1password/sdk"; +import type { JsonPrimitive, PluginDefinition } from "@yaakapp/api"; +import type { CallTemplateFunctionArgs } from "@yaakapp-internal/plugins"; const _clients: Record = {}; @@ -23,32 +23,32 @@ async function op( let authMethod: string | DesktopAuth; let hash: string; switch (args.values.authMethod) { - case 'desktop': { + case "desktop": { const account = args.values.token; - if (typeof account !== 'string' || !account) return { error: 'Missing account name' }; + if (typeof account !== "string" || !account) return { error: "Missing account name" }; - hash = crypto.createHash('sha256').update(`desktop:${account}`).digest('hex'); + hash = crypto.createHash("sha256").update(`desktop:${account}`).digest("hex"); authMethod = new DesktopAuth(account); break; } - case 'token': { + case "token": { const token = args.values.token; - if (typeof token !== 'string' || !token) return { error: 'Missing service token' }; + if (typeof token !== "string" || !token) return { error: "Missing service token" }; - hash = crypto.createHash('sha256').update(`token:${token}`).digest('hex'); + hash = crypto.createHash("sha256").update(`token:${token}`).digest("hex"); authMethod = token; break; } default: - return { error: 'Invalid authentication method' }; + return { error: "Invalid authentication method" }; } if (!_clients[hash]) { try { _clients[hash] = await createClient({ auth: authMethod, - integrationName: 'Yaak 1Password Plugin', - integrationVersion: 'v1.0.0', + integrationName: "Yaak 1Password Plugin", + integrationVersion: "v1.0.0", }); } catch (e) { return { error: e }; @@ -66,18 +66,18 @@ async function getValue( fieldId?: JsonPrimitive, ): Promise> { const res = await op(args); - if ('error' in res) return { error: res.error }; + if ("error" in res) return { error: res.error }; const clientHash = res.clientHash; const client = res.client; - if (!vaultId || typeof vaultId !== 'string') { - return { error: 'No vault specified' }; + if (!vaultId || typeof vaultId !== "string") { + return { error: "No vault specified" }; } - if (!itemId || typeof itemId !== 'string') { - return { error: 'No item specified' }; + if (!itemId || typeof itemId !== "string") { + return { error: "No item specified" }; } - if (!fieldId || typeof fieldId !== 'string') { - return { error: 'No field specified' }; + if (!fieldId || typeof fieldId !== "string") { + return { error: "No field specified" }; } try { @@ -98,47 +98,47 @@ async function getValue( export const plugin: PluginDefinition = { templateFunctions: [ { - name: '1password.item', - description: 'Get a secret', - previewArgs: ['field'], + name: "1password.item", + description: "Get a secret", + previewArgs: ["field"], args: [ { - type: 'h_stack', + type: "h_stack", inputs: [ { - name: 'authMethod', - type: 'select', - label: 'Authentication Method', - defaultValue: 'token', + name: "authMethod", + type: "select", + label: "Authentication Method", + defaultValue: "token", options: [ { - label: 'Service Account', - value: 'token', + label: "Service Account", + value: "token", }, { - label: 'Desktop App', - value: 'desktop', + label: "Desktop App", + value: "desktop", }, ], }, { - name: 'token', - type: 'text', + name: "token", + type: "text", // oxlint-disable-next-line no-template-curly-in-string -- Yaak template syntax - defaultValue: '${[1PASSWORD_TOKEN]}', + defaultValue: "${[1PASSWORD_TOKEN]}", dynamic(_ctx, args) { switch (args.values.authMethod) { - case 'desktop': + case "desktop": return { - label: 'Account Name', + label: "Account Name", description: 'Account name can be taken from the sidebar of the 1Password App. Make sure you\'re on the BETA version of the 1Password app and have "Integrate with other apps" enabled in Settings > Developer.', }; - case 'token': + case "token": return { - label: 'Token', + label: "Token", description: - 'Token can be generated from the 1Password website by visiting Developer > Service Accounts', + "Token can be generated from the 1Password website by visiting Developer > Service Accounts", password: true, }; } @@ -149,13 +149,13 @@ export const plugin: PluginDefinition = { ], }, { - name: 'vault', - label: 'Vault', - type: 'select', + name: "vault", + label: "Vault", + type: "select", options: [], async dynamic(_ctx, args) { const res = await op(args); - if ('error' in res) return { hidden: true }; + if ("error" in res) return { hidden: true }; const clientHash = res.clientHash; const client = res.client; @@ -169,9 +169,9 @@ export const plugin: PluginDefinition = { return { options: vaults.map((vault) => { let title = vault.id; - if ('title' in vault) { + if ("title" in vault) { title = vault.title; - } else if ('name' in vault) { + } else if ("name" in vault) { // The SDK returns 'name' instead of 'title' but the bindings still use 'title' title = (vault as { name: string }).name; } @@ -185,18 +185,18 @@ export const plugin: PluginDefinition = { }, }, { - name: 'item', - label: 'Item', - type: 'select', + name: "item", + label: "Item", + type: "select", options: [], async dynamic(_ctx, args) { const res = await op(args); - if ('error' in res) return { hidden: true }; + if ("error" in res) return { hidden: true }; const clientHash = res.clientHash; const client = res.client; const vaultId = args.values.vault; - if (typeof vaultId !== 'string') return { hidden: true }; + if (typeof vaultId !== "string") return { hidden: true }; try { const cacheKey = `${clientHash}:items:${vaultId}`; @@ -216,19 +216,19 @@ export const plugin: PluginDefinition = { }, }, { - name: 'field', - label: 'Field', - type: 'select', + name: "field", + label: "Field", + type: "select", options: [], async dynamic(_ctx, args) { const res = await op(args); - if ('error' in res) return { hidden: true }; + if ("error" in res) return { hidden: true }; const clientHash = res.clientHash; const client = res.client; const vaultId = args.values.vault; const itemId = args.values.item; - if (typeof vaultId !== 'string' || typeof itemId !== 'string') { + if (typeof vaultId !== "string" || typeof itemId !== "string") { return { hidden: true }; } @@ -252,7 +252,7 @@ export const plugin: PluginDefinition = { const itemId = args.values.item; const fieldId = args.values.field; const res = await getValue(args, vaultId, itemId, fieldId); - if ('error' in res) { + if ("error" in res) { throw res.error; } diff --git a/plugins/template-function-cookie/package.json b/plugins/template-function-cookie/package.json index 55ff8303..1fd325ec 100644 --- a/plugins/template-function-cookie/package.json +++ b/plugins/template-function-cookie/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-cookie", "displayName": "Cookie Template Functions", - "description": "Template functions for working with cookies", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for working with cookies", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-cookie/src/index.ts b/plugins/template-function-cookie/src/index.ts index 34baa045..d2587edb 100644 --- a/plugins/template-function-cookie/src/index.ts +++ b/plugins/template-function-cookie/src/index.ts @@ -1,16 +1,16 @@ -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'cookie.value', - description: 'Read the value of a cookie in the jar, by name', - previewArgs: ['name'], + name: "cookie.value", + description: "Read the value of a cookie in the jar, by name", + previewArgs: ["name"], args: [ { - type: 'text', - name: 'name', - label: 'Cookie Name', + type: "text", + name: "name", + label: "Cookie Name", }, ], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { diff --git a/plugins/template-function-ctx/package.json b/plugins/template-function-ctx/package.json index 424d99f3..baf7f144 100644 --- a/plugins/template-function-ctx/package.json +++ b/plugins/template-function-ctx/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-ctx", "displayName": "Window Template Functions", - "description": "Template functions for accessing attributes of the current window", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for accessing attributes of the current window", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-ctx/src/index.ts b/plugins/template-function-ctx/src/index.ts index 833cbd17..13e78e40 100644 --- a/plugins/template-function-ctx/src/index.ts +++ b/plugins/template-function-ctx/src/index.ts @@ -1,26 +1,26 @@ -import type { PluginDefinition } from '@yaakapp/api'; +import type { PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'ctx.request', - description: 'Get the ID of the currently active request', + name: "ctx.request", + description: "Get the ID of the currently active request", args: [], async onRender(ctx) { return ctx.window.requestId(); }, }, { - name: 'ctx.environment', - description: 'Get the ID of the currently active environment', + name: "ctx.environment", + description: "Get the ID of the currently active environment", args: [], async onRender(ctx) { return ctx.window.environmentId(); }, }, { - name: 'ctx.workspace', - description: 'Get the ID of the currently active workspace', + name: "ctx.workspace", + description: "Get the ID of the currently active workspace", args: [], async onRender(ctx) { return ctx.window.workspaceId(); diff --git a/plugins/template-function-encode/package.json b/plugins/template-function-encode/package.json index 33bd3c2c..c11762ba 100644 --- a/plugins/template-function-encode/package.json +++ b/plugins/template-function-encode/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-encode", "displayName": "Encoding Template Functions", - "description": "Template functions for encoding and decoding data", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for encoding and decoding data", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-encode/src/index.ts b/plugins/template-function-encode/src/index.ts index 6a38669d..a1009651 100644 --- a/plugins/template-function-encode/src/index.ts +++ b/plugins/template-function-encode/src/index.ts @@ -1,60 +1,60 @@ -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'base64.encode', - description: 'Encode a value to base64', + name: "base64.encode", + description: "Encode a value to base64", args: [ { - label: 'Encoding', - type: 'select', - name: 'encoding', - defaultValue: 'base64', + label: "Encoding", + type: "select", + name: "encoding", + defaultValue: "base64", options: [ { - label: 'Base64', - value: 'base64', + label: "Base64", + value: "base64", }, { - label: 'Base64 URL-safe', - value: 'base64url', + label: "Base64 URL-safe", + value: "base64url", }, ], }, - { label: 'Plain Text', type: 'text', name: 'value', multiLine: true }, + { label: "Plain Text", type: "text", name: "value", multiLine: true }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - return Buffer.from(String(args.values.value ?? '')).toString( - args.values.encoding === 'base64url' ? 'base64url' : 'base64', + return Buffer.from(String(args.values.value ?? "")).toString( + args.values.encoding === "base64url" ? "base64url" : "base64", ); }, }, { - name: 'base64.decode', - description: 'Decode a value from base64', - args: [{ label: 'Encoded Value', type: 'text', name: 'value', multiLine: true }], + name: "base64.decode", + description: "Decode a value from base64", + args: [{ label: "Encoded Value", type: "text", name: "value", multiLine: true }], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - return Buffer.from(String(args.values.value ?? ''), 'base64').toString('utf-8'); + return Buffer.from(String(args.values.value ?? ""), "base64").toString("utf-8"); }, }, { - name: 'url.encode', - description: 'Encode a value for use in a URL (percent-encoding)', - args: [{ label: 'Plain Text', type: 'text', name: 'value', multiLine: true }], + name: "url.encode", + description: "Encode a value for use in a URL (percent-encoding)", + args: [{ label: "Plain Text", type: "text", name: "value", multiLine: true }], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - return encodeURIComponent(String(args.values.value ?? '')); + return encodeURIComponent(String(args.values.value ?? "")); }, }, { - name: 'url.decode', - description: 'Decode a percent-encoded URL value', - args: [{ label: 'Encoded Value', type: 'text', name: 'value', multiLine: true }], + name: "url.decode", + description: "Decode a percent-encoded URL value", + args: [{ label: "Encoded Value", type: "text", name: "value", multiLine: true }], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { try { - return decodeURIComponent(String(args.values.value ?? '')); + return decodeURIComponent(String(args.values.value ?? "")); } catch { - return ''; + return ""; } }, }, diff --git a/plugins/template-function-fs/package.json b/plugins/template-function-fs/package.json index 00108f5e..64437811 100644 --- a/plugins/template-function-fs/package.json +++ b/plugins/template-function-fs/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-fs", "displayName": "File System Template Functions", - "description": "Template functions for working with the file system", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for working with the file system", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-fs/src/index.ts b/plugins/template-function-fs/src/index.ts index 18799e93..8be95e87 100644 --- a/plugins/template-function-fs/src/index.ts +++ b/plugins/template-function-fs/src/index.ts @@ -1,45 +1,45 @@ -import fs from 'node:fs'; -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; +import fs from "node:fs"; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; -const UTF8 = 'utf8'; +const UTF8 = "utf8"; const options = [ - { label: 'ASCII', value: 'ascii' }, - { label: 'UTF-8', value: UTF8 }, - { label: 'UTF-16 LE', value: 'utf16le' }, - { label: 'Base64', value: 'base64' }, - { label: 'Base64 URL-safe', value: 'base64url' }, - { label: 'Latin-1', value: 'latin1' }, - { label: 'Hexadecimal', value: 'hex' }, + { label: "ASCII", value: "ascii" }, + { label: "UTF-8", value: UTF8 }, + { label: "UTF-16 LE", value: "utf16le" }, + { label: "Base64", value: "base64" }, + { label: "Base64 URL-safe", value: "base64url" }, + { label: "Latin-1", value: "latin1" }, + { label: "Hexadecimal", value: "hex" }, ]; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'fs.readFile', - description: 'Read the contents of a file as utf-8', - previewArgs: ['encoding'], + name: "fs.readFile", + description: "Read the contents of a file as utf-8", + previewArgs: ["encoding"], args: [ - { title: 'Select File', type: 'file', name: 'path', label: 'File' }, + { title: "Select File", type: "file", name: "path", label: "File" }, { - type: 'select', - name: 'encoding', - label: 'Encoding', + type: "select", + name: "encoding", + label: "Encoding", defaultValue: UTF8, description: "Specifies how the file's bytes are decoded into text when read", options, }, { - type: 'checkbox', - name: 'trim', - label: 'Trim Whitespace', - description: 'Remove leading and trailing whitespace from the file contents', + type: "checkbox", + name: "trim", + label: "Trim Whitespace", + description: "Remove leading and trailing whitespace from the file contents", }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { if (!args.values.path || !args.values.encoding) return null; try { - const v = await fs.promises.readFile(String(args.values.path ?? ''), { - encoding: String(args.values.encoding ?? 'utf-8') as BufferEncoding, + const v = await fs.promises.readFile(String(args.values.path ?? ""), { + encoding: String(args.values.encoding ?? "utf-8") as BufferEncoding, }); return args.values.trim ? v.trim() : v; } catch { diff --git a/plugins/template-function-hash/package.json b/plugins/template-function-hash/package.json index bfa96718..954e00b0 100755 --- a/plugins/template-function-hash/package.json +++ b/plugins/template-function-hash/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-hash", "displayName": "Hash Template Functions", - "description": "Template functions for generating hash values", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for generating hash values", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-hash/src/index.ts b/plugins/template-function-hash/src/index.ts index 2cb19002..0e7e958b 100755 --- a/plugins/template-function-hash/src/index.ts +++ b/plugins/template-function-hash/src/index.ts @@ -1,27 +1,27 @@ -import { createHash, createHmac } from 'node:crypto'; -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; +import { createHash, createHmac } from "node:crypto"; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; -const algorithms = ['md5', 'sha1', 'sha256', 'sha512'] as const; -const encodings = ['base64', 'hex'] as const; +const algorithms = ["md5", "sha1", "sha256", "sha512"] as const; +const encodings = ["base64", "hex"] as const; -type TemplateFunctionPlugin = NonNullable[number]; +type TemplateFunctionPlugin = NonNullable[number]; const hashFunctions: TemplateFunctionPlugin[] = algorithms.map((algorithm) => ({ name: `hash.${algorithm}`, - description: 'Hash a value to its hexadecimal representation', + description: "Hash a value to its hexadecimal representation", args: [ { - type: 'text', - name: 'input', - label: 'Input', - placeholder: 'input text', + type: "text", + name: "input", + label: "Input", + placeholder: "input text", multiLine: true, }, { - type: 'select', - name: 'encoding', - label: 'Encoding', - defaultValue: 'base64', + type: "select", + name: "encoding", + label: "Encoding", + defaultValue: "base64", options: encodings.map((encoding) => ({ label: capitalize(encoding), value: encoding, @@ -32,32 +32,32 @@ const hashFunctions: TemplateFunctionPlugin[] = algorithms.map((algorithm) => ({ const input = String(args.values.input); const encoding = String(args.values.encoding) as (typeof encodings)[number]; - return createHash(algorithm).update(input, 'utf-8').digest(encoding); + return createHash(algorithm).update(input, "utf-8").digest(encoding); }, })); const hmacFunctions: TemplateFunctionPlugin[] = algorithms.map((algorithm) => ({ name: `hmac.${algorithm}`, - description: 'Compute the HMAC of a value', + description: "Compute the HMAC of a value", args: [ { - type: 'text', - name: 'input', - label: 'Input', - placeholder: 'input text', + type: "text", + name: "input", + label: "Input", + placeholder: "input text", multiLine: true, }, { - type: 'text', - name: 'key', - label: 'Key', + type: "text", + name: "key", + label: "Key", password: true, }, { - type: 'select', - name: 'encoding', - label: 'Encoding', - defaultValue: 'base64', + type: "select", + name: "encoding", + label: "Encoding", + defaultValue: "base64", options: encodings.map((encoding) => ({ value: encoding, label: capitalize(encoding), diff --git a/plugins/template-function-json/package.json b/plugins/template-function-json/package.json index 3cbecbfa..7b8e8847 100755 --- a/plugins/template-function-json/package.json +++ b/plugins/template-function-json/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-json", "displayName": "JSON Template Functions", - "description": "Template functions for working with JSON data", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for working with JSON data", "main": "build/index.js", "types": "src/index.ts", "scripts": { diff --git a/plugins/template-function-json/src/index.ts b/plugins/template-function-json/src/index.ts index 0492dfc2..445ee502 100755 --- a/plugins/template-function-json/src/index.ts +++ b/plugins/template-function-json/src/index.ts @@ -1,45 +1,45 @@ -import type { XPathResult } from '@yaak/template-function-xml'; -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; -import { JSONPath } from 'jsonpath-plus'; +import type { XPathResult } from "@yaak/template-function-xml"; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; +import { JSONPath } from "jsonpath-plus"; -const RETURN_FIRST = 'first'; -const RETURN_ALL = 'all'; -const RETURN_JOIN = 'join'; +const RETURN_FIRST = "first"; +const RETURN_ALL = "all"; +const RETURN_JOIN = "join"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'json.jsonpath', - description: 'Filter JSON-formatted text using JSONPath syntax', - previewArgs: ['query'], + name: "json.jsonpath", + description: "Filter JSON-formatted text using JSONPath syntax", + previewArgs: ["query"], args: [ { - type: 'editor', - name: 'input', - label: 'Input', - language: 'json', + type: "editor", + name: "input", + label: "Input", + language: "json", placeholder: '{ "foo": "bar" }', }, { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'select', - name: 'result', - label: 'Return Format', + type: "select", + name: "result", + label: "Return Format", defaultValue: RETURN_FIRST, options: [ - { label: 'First result', value: RETURN_FIRST }, - { label: 'All results', value: RETURN_ALL }, - { label: 'Join with separator', value: RETURN_JOIN }, + { label: "First result", value: RETURN_FIRST }, + { label: "All results", value: RETURN_ALL }, + { label: "Join with separator", value: RETURN_JOIN }, ], }, { - name: 'join', - type: 'text', - label: 'Separator', + name: "join", + type: "text", + label: "Separator", optional: true, - defaultValue: ', ', + defaultValue: ", ", dynamic(_ctx, args) { return { hidden: args.values.result !== RETURN_JOIN }; }, @@ -47,15 +47,15 @@ export const plugin: PluginDefinition = { ], }, { - type: 'checkbox', - name: 'formatted', - label: 'Pretty Print', - description: 'Format the output as JSON', + type: "checkbox", + name: "formatted", + label: "Pretty Print", + description: "Format the output as JSON", dynamic(_ctx, args) { return { hidden: args.values.result === RETURN_JOIN }; }, }, - { type: 'text', name: 'query', label: 'Query', placeholder: '$..foo' }, + { type: "text", name: "query", label: "Query", placeholder: "$..foo" }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { try { @@ -72,36 +72,36 @@ export const plugin: PluginDefinition = { }, }, { - name: 'json.escape', - description: 'Escape a JSON string, useful when using the output in JSON values', + name: "json.escape", + description: "Escape a JSON string, useful when using the output in JSON values", args: [ { - type: 'text', - name: 'input', - label: 'Input', + type: "text", + name: "input", + label: "Input", multiLine: true, placeholder: 'Hello "World"', }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - const input = String(args.values.input ?? ''); - return input.replace(/\\/g, '\\\\').replace(/"/g, '\\"'); + const input = String(args.values.input ?? ""); + return input.replace(/\\/g, "\\\\").replace(/"/g, '\\"'); }, }, { - name: 'json.minify', - description: 'Remove unnecessary whitespace from a valid JSON string.', + name: "json.minify", + description: "Remove unnecessary whitespace from a valid JSON string.", args: [ { - type: 'editor', - language: 'json', - name: 'input', - label: 'Input', + type: "editor", + language: "json", + name: "input", + label: "Input", placeholder: '{ "foo": "bar" }', }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - const input = String(args.values.input ?? ''); + const input = String(args.values.input ?? ""); try { return JSON.stringify(JSON.parse(input)); } catch { @@ -112,7 +112,7 @@ export const plugin: PluginDefinition = { ], }; -export type JSONPathResult = 'first' | 'join' | 'all'; +export type JSONPathResult = "first" | "join" | "all"; export function filterJSONPath( body: string, @@ -125,15 +125,15 @@ export function filterJSONPath( let items = JSONPath({ path, json: parsed }); if (items == null) { - return ''; + return ""; } if (!Array.isArray(items)) { // Already good - } else if (result === 'first') { - items = items[0] ?? ''; - } else if (result === 'join') { - items = items.map((i) => objToStr(i, false)).join(join ?? ''); + } else if (result === "first") { + items = items[0] ?? ""; + } else if (result === "join") { + items = items.map((i) => objToStr(i, false)).join(join ?? ""); } return objToStr(items, formatted); @@ -141,8 +141,8 @@ export function filterJSONPath( function objToStr(o: unknown, formatted = false): string { if ( - Object.prototype.toString.call(o) === '[object Array]' || - Object.prototype.toString.call(o) === '[object Object]' + Object.prototype.toString.call(o) === "[object Array]" || + Object.prototype.toString.call(o) === "[object Object]" ) { return formatted ? JSON.stringify(o, null, 2) : JSON.stringify(o); } diff --git a/plugins/template-function-prompt/package.json b/plugins/template-function-prompt/package.json index a3b8d1f2..7829308c 100644 --- a/plugins/template-function-prompt/package.json +++ b/plugins/template-function-prompt/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-prompt", "displayName": "Prompt Template Functions", - "description": "Template functions for prompting for user input", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for prompting for user input", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-prompt/src/index.ts b/plugins/template-function-prompt/src/index.ts index 4247954f..b03ae3d0 100644 --- a/plugins/template-function-prompt/src/index.ts +++ b/plugins/template-function-prompt/src/index.ts @@ -1,9 +1,9 @@ -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; -import slugify from 'slugify'; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; +import slugify from "slugify"; -const STORE_NONE = 'none'; -const STORE_FOREVER = 'forever'; -const STORE_EXPIRE = 'expire'; +const STORE_NONE = "none"; +const STORE_FOREVER = "forever"; +const STORE_EXPIRE = "expire"; interface Saved { value: string; @@ -13,15 +13,15 @@ interface Saved { export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'prompt.text', - description: 'Prompt the user for input when sending a request', - previewType: 'click', - previewArgs: ['label'], + name: "prompt.text", + description: "Prompt the user for input when sending a request", + previewType: "click", + previewArgs: ["label"], args: [ { - type: 'text', - name: 'label', - label: 'Label', + type: "text", + name: "label", + label: "Label", optional: true, dynamic(_ctx, args) { if ( @@ -33,45 +33,45 @@ export const plugin: PluginDefinition = { }, }, { - type: 'select', - name: 'store', - label: 'Store Input', + type: "select", + name: "store", + label: "Store Input", defaultValue: STORE_NONE, options: [ - { label: 'Never', value: STORE_NONE }, - { label: 'Expire', value: STORE_EXPIRE }, - { label: 'Forever', value: STORE_FOREVER }, + { label: "Never", value: STORE_NONE }, + { label: "Expire", value: STORE_EXPIRE }, + { label: "Forever", value: STORE_FOREVER }, ], }, { - type: 'h_stack', + type: "h_stack", dynamic(_ctx, args) { return { hidden: args.values.store === STORE_NONE }; }, inputs: [ { - type: 'text', - name: 'namespace', - label: 'Namespace', + type: "text", + name: "namespace", + label: "Namespace", // oxlint-disable-next-line no-template-curly-in-string -- Yaak template syntax - defaultValue: '${[ctx.workspace()]}', + defaultValue: "${[ctx.workspace()]}", optional: true, }, { - type: 'text', - name: 'key', - label: 'Key (defaults to Label)', + type: "text", + name: "key", + label: "Key (defaults to Label)", optional: true, dynamic(_ctx, args) { - return { placeholder: String(args.values.label || '') }; + return { placeholder: String(args.values.label || "") }; }, }, { - type: 'text', - name: 'ttl', - label: 'TTL (seconds)', - placeholder: '0', - defaultValue: '0', + type: "text", + name: "ttl", + label: "TTL (seconds)", + placeholder: "0", + defaultValue: "0", optional: true, dynamic(_ctx, args) { return { hidden: args.values.store !== STORE_EXPIRE }; @@ -80,49 +80,49 @@ export const plugin: PluginDefinition = { ], }, { - type: 'banner', - color: 'info', + type: "banner", + color: "info", inputs: [], dynamic(_ctx, args) { let key: string; try { key = buildKey(args); } catch (err) { - return { color: 'danger', inputs: [{ type: 'markdown', content: String(err) }] }; + return { color: "danger", inputs: [{ type: "markdown", content: String(err) }] }; } return { hidden: args.values.store === STORE_NONE, inputs: [ { - type: 'markdown', - content: [`Value will be saved under: \`${key}\``].join('\n\n'), + type: "markdown", + content: [`Value will be saved under: \`${key}\``].join("\n\n"), }, ], }; }, }, { - type: 'accordion', - label: 'Advanced', + type: "accordion", + label: "Advanced", inputs: [ { - type: 'text', - name: 'title', - label: 'Prompt Title', + type: "text", + name: "title", + label: "Prompt Title", optional: true, - placeholder: 'Enter Value', + placeholder: "Enter Value", }, - { type: 'text', name: 'defaultValue', label: 'Default Value', optional: true }, - { type: 'text', name: 'placeholder', label: 'Input Placeholder', optional: true }, - { type: 'checkbox', name: 'password', label: 'Mask Value' }, + { type: "text", name: "defaultValue", label: "Default Value", optional: true }, + { type: "text", name: "placeholder", label: "Input Placeholder", optional: true }, + { type: "checkbox", name: "password", label: "Mask Value" }, ], }, ], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { - if (args.purpose !== 'send') return null; + if (args.purpose !== "send") return null; if (args.values.store !== STORE_NONE && !args.values.namespace) { - throw new Error('Namespace is required when storing values'); + throw new Error("Namespace is required when storing values"); } const existing = await maybeGetValue(ctx, args); @@ -131,17 +131,17 @@ export const plugin: PluginDefinition = { } const value = await ctx.prompt.text({ - id: `prompt-${args.values.label ?? 'none'}`, - label: String(args.values.label || 'Value'), - title: String(args.values.title ?? 'Enter Value'), - defaultValue: String(args.values.defaultValue ?? ''), - placeholder: String(args.values.placeholder ?? ''), + id: `prompt-${args.values.label ?? "none"}`, + label: String(args.values.label || "Value"), + title: String(args.values.title ?? "Enter Value"), + defaultValue: String(args.values.defaultValue ?? ""), + placeholder: String(args.values.placeholder ?? ""), password: Boolean(args.values.password), required: false, }); if (value == null) { - throw new Error('Prompt cancelled'); + throw new Error("Prompt cancelled"); } if (args.values.store !== STORE_NONE) { @@ -156,12 +156,12 @@ export const plugin: PluginDefinition = { function buildKey(args: CallTemplateFunctionArgs) { if (!args.values.key && !args.values.label) { - throw new Error('A label or key is required when storing values'); + throw new Error("A label or key is required when storing values"); } return [args.values.namespace, args.values.key || args.values.label] .filter((v) => !!v) .map((v) => slugify(String(v), { lower: true, trim: true })) - .join('.'); + .join("."); } async function maybeGetValue(ctx: Context, args: CallTemplateFunctionArgs) { diff --git a/plugins/template-function-random/package.json b/plugins/template-function-random/package.json index 7bb77092..6d0774eb 100644 --- a/plugins/template-function-random/package.json +++ b/plugins/template-function-random/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-random", "displayName": "Random Template Functions", - "description": "Template functions for generating random values", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for generating random values", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-random/src/index.ts b/plugins/template-function-random/src/index.ts index 8212b745..fc29039a 100644 --- a/plugins/template-function-random/src/index.ts +++ b/plugins/template-function-random/src/index.ts @@ -1,36 +1,36 @@ -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'random.range', - description: 'Generate a random number between two values', - previewArgs: ['min', 'max'], + name: "random.range", + description: "Generate a random number between two values", + previewArgs: ["min", "max"], args: [ { - type: 'text', - name: 'min', - label: 'Minimum', - defaultValue: '0', + type: "text", + name: "min", + label: "Minimum", + defaultValue: "0", }, { - type: 'text', - name: 'max', - label: 'Maximum', - defaultValue: '1', + type: "text", + name: "max", + label: "Maximum", + defaultValue: "1", }, { - type: 'text', - name: 'decimals', + type: "text", + name: "decimals", optional: true, - label: 'Decimal Places', + label: "Decimal Places", }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - const min = args.values.min ? Number.parseInt(String(args.values.min ?? '0'), 10) : 0; - const max = args.values.max ? Number.parseInt(String(args.values.max ?? '1'), 10) : 1; + const min = args.values.min ? Number.parseInt(String(args.values.min ?? "0"), 10) : 0; + const max = args.values.max ? Number.parseInt(String(args.values.max ?? "1"), 10) : 1; const decimals = args.values.decimals - ? Number.parseInt(String(args.values.decimals ?? '0'), 10) + ? Number.parseInt(String(args.values.decimals ?? "0"), 10) : null; let value = Math.random() * (max - min) + min; diff --git a/plugins/template-function-regex/package.json b/plugins/template-function-regex/package.json index b37b12f1..2534d4c9 100644 --- a/plugins/template-function-regex/package.json +++ b/plugins/template-function-regex/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-regex", "displayName": "Regex Template Functions", - "description": "Template functions for working with regular expressions", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for working with regular expressions", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/template-function-regex/src/index.ts b/plugins/template-function-regex/src/index.ts index 53a16b91..f004cb66 100644 --- a/plugins/template-function-regex/src/index.ts +++ b/plugins/template-function-regex/src/index.ts @@ -1,73 +1,73 @@ -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; -import type { TemplateFunctionArg } from '@yaakapp-internal/plugins'; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; +import type { TemplateFunctionArg } from "@yaakapp-internal/plugins"; const inputArg: TemplateFunctionArg = { - type: 'text', - name: 'input', - label: 'Input Text', + type: "text", + name: "input", + label: "Input Text", multiLine: true, }; const regexArg: TemplateFunctionArg = { - type: 'text', - name: 'regex', - label: 'Regular Expression', - placeholder: '\\w+', - defaultValue: '.*', + type: "text", + name: "regex", + label: "Regular Expression", + placeholder: "\\w+", + defaultValue: ".*", description: - 'A JavaScript regular expression. Use a capture group to reference parts of the match in the replacement.', + "A JavaScript regular expression. Use a capture group to reference parts of the match in the replacement.", }; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'regex.match', - description: 'Extract text using a regular expression', + name: "regex.match", + description: "Extract text using a regular expression", args: [inputArg, regexArg], previewArgs: [regexArg.name], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - const input = String(args.values.input ?? ''); - const regex = new RegExp(String(args.values.regex ?? '')); + const input = String(args.values.input ?? ""); + const regex = new RegExp(String(args.values.regex ?? "")); const match = input.match(regex); return match?.groups - ? (Object.values(match.groups)[0] ?? '') - : (match?.[1] ?? match?.[0] ?? ''); + ? (Object.values(match.groups)[0] ?? "") + : (match?.[1] ?? match?.[0] ?? ""); }, }, { - name: 'regex.replace', - description: 'Replace text using a regular expression', + name: "regex.replace", + description: "Replace text using a regular expression", previewArgs: [regexArg.name], args: [ inputArg, regexArg, { - type: 'text', - name: 'replacement', - label: 'Replacement Text', - placeholder: 'hello $1', + type: "text", + name: "replacement", + label: "Replacement Text", + placeholder: "hello $1", description: - 'The replacement text. Use $1, $2, ... to reference capture groups or $& to reference the entire match.', + "The replacement text. Use $1, $2, ... to reference capture groups or $& to reference the entire match.", }, { - type: 'text', - name: 'flags', - label: 'Flags', - placeholder: 'g', - defaultValue: 'g', + type: "text", + name: "flags", + label: "Flags", + placeholder: "g", + defaultValue: "g", optional: true, description: - 'Regular expression flags (g for global, i for case-insensitive, m for multiline, etc.)', + "Regular expression flags (g for global, i for case-insensitive, m for multiline, etc.)", }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { - const input = String(args.values.input ?? ''); - const replacement = String(args.values.replacement ?? ''); - const flags = String(args.values.flags || ''); + const input = String(args.values.input ?? ""); + const replacement = String(args.values.replacement ?? ""); + const flags = String(args.values.flags || ""); const regex = String(args.values.regex); - if (!regex) return ''; + if (!regex) return ""; return input.replace(new RegExp(String(args.values.regex), flags), replacement); }, diff --git a/plugins/template-function-regex/tests/regex.test.ts b/plugins/template-function-regex/tests/regex.test.ts index 5d6af14d..6c8c575a 100644 --- a/plugins/template-function-regex/tests/regex.test.ts +++ b/plugins/template-function-regex/tests/regex.test.ts @@ -1,196 +1,196 @@ -import type { Context } from '@yaakapp/api'; -import { describe, expect, it } from 'vite-plus/test'; -import { plugin } from '../src'; +import type { Context } from "@yaakapp/api"; +import { describe, expect, it } from "vite-plus/test"; +import { plugin } from "../src"; -describe('regex.match', () => { - const matchFunction = plugin.templateFunctions?.find((f) => f.name === 'regex.match'); +describe("regex.match", () => { + const matchFunction = plugin.templateFunctions?.find((f) => f.name === "regex.match"); - it('should exist', () => { + it("should exist", () => { expect(matchFunction).toBeDefined(); }); - it('should extract first capture group', async () => { + it("should extract first capture group", async () => { const result = await matchFunction?.onRender({} as Context, { values: { - regex: 'Hello (\\w+)', - input: 'Hello World', + regex: "Hello (\\w+)", + input: "Hello World", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('World'); + expect(result).toBe("World"); }); - it('should extract named capture group', async () => { + it("should extract named capture group", async () => { const result = await matchFunction?.onRender({} as Context, { values: { - regex: 'Hello (?\\w+)', - input: 'Hello World', + regex: "Hello (?\\w+)", + input: "Hello World", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('World'); + expect(result).toBe("World"); }); - it('should return full match when no capture groups', async () => { + it("should return full match when no capture groups", async () => { const result = await matchFunction?.onRender({} as Context, { values: { - regex: 'Hello \\w+', - input: 'Hello World', + regex: "Hello \\w+", + input: "Hello World", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('Hello World'); + expect(result).toBe("Hello World"); }); - it('should return empty string when no match', async () => { + it("should return empty string when no match", async () => { const result = await matchFunction?.onRender({} as Context, { values: { - regex: 'Goodbye', - input: 'Hello World', + regex: "Goodbye", + input: "Hello World", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe(''); + expect(result).toBe(""); }); - it('should return empty string when regex is empty', async () => { + it("should return empty string when regex is empty", async () => { const result = await matchFunction?.onRender({} as Context, { values: { - regex: '', - input: 'Hello World', + regex: "", + input: "Hello World", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe(''); + expect(result).toBe(""); }); - it('should return empty string when input is empty', async () => { + it("should return empty string when input is empty", async () => { const result = await matchFunction?.onRender({} as Context, { values: { - regex: 'Hello', - input: '', + regex: "Hello", + input: "", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe(''); + expect(result).toBe(""); }); }); -describe('regex.replace', () => { - const replaceFunction = plugin.templateFunctions?.find((f) => f.name === 'regex.replace'); +describe("regex.replace", () => { + const replaceFunction = plugin.templateFunctions?.find((f) => f.name === "regex.replace"); - it('should exist', () => { + it("should exist", () => { expect(replaceFunction).toBeDefined(); }); - it('should replace one occurrence by default', async () => { + it("should replace one occurrence by default", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: 'o', - input: 'Hello World', - replacement: 'a', + regex: "o", + input: "Hello World", + replacement: "a", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('Hella World'); + expect(result).toBe("Hella World"); }); - it('should replace with capture groups', async () => { + it("should replace with capture groups", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: '(\\w+) (\\w+)', - input: 'Hello World', - replacement: '$2 $1', + regex: "(\\w+) (\\w+)", + input: "Hello World", + replacement: "$2 $1", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('World Hello'); + expect(result).toBe("World Hello"); }); - it('should replace with full match reference', async () => { + it("should replace with full match reference", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: 'World', - input: 'Hello World', - replacement: '[$&]', + regex: "World", + input: "Hello World", + replacement: "[$&]", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('Hello [World]'); + expect(result).toBe("Hello [World]"); }); - it('should respect flags parameter', async () => { + it("should respect flags parameter", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: 'hello', - input: 'Hello World', - replacement: 'Hi', - flags: 'i', + regex: "hello", + input: "Hello World", + replacement: "Hi", + flags: "i", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('Hi World'); + expect(result).toBe("Hi World"); }); - it('should handle empty replacement', async () => { + it("should handle empty replacement", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: 'World', - input: 'Hello World', - replacement: '', + regex: "World", + input: "Hello World", + replacement: "", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('Hello '); + expect(result).toBe("Hello "); }); - it('should return original input when no match', async () => { + it("should return original input when no match", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: 'Goodbye', - input: 'Hello World', - replacement: 'Hi', + regex: "Goodbye", + input: "Hello World", + replacement: "Hi", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe('Hello World'); + expect(result).toBe("Hello World"); }); - it('should return empty string when regex is empty', async () => { + it("should return empty string when regex is empty", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: '', - input: 'Hello World', - replacement: 'Hi', + regex: "", + input: "Hello World", + replacement: "Hi", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe(''); + expect(result).toBe(""); }); - it('should return empty string when input is empty', async () => { + it("should return empty string when input is empty", async () => { const result = await replaceFunction?.onRender({} as Context, { values: { - regex: 'Hello', - input: '', - replacement: 'Hi', + regex: "Hello", + input: "", + replacement: "Hi", }, - purpose: 'send', + purpose: "send", }); - expect(result).toBe(''); + expect(result).toBe(""); }); - it('should throw on invalid regex', async () => { + it("should throw on invalid regex", async () => { const fn = replaceFunction?.onRender({} as Context, { values: { - regex: '[', - input: 'Hello World', - replacement: 'Hi', + regex: "[", + input: "Hello World", + replacement: "Hi", }, - purpose: 'send', + purpose: "send", }); await expect(fn).rejects.toThrow( - 'Invalid regular expression: /[/: Unterminated character class', + "Invalid regular expression: /[/: Unterminated character class", ); }); }); diff --git a/plugins/template-function-request/package.json b/plugins/template-function-request/package.json index c205bc69..1f5e0d34 100755 --- a/plugins/template-function-request/package.json +++ b/plugins/template-function-request/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-request", "displayName": "Request Template Functions", - "description": "Template functions for extracting value from requests", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for extracting value from requests", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-request/src/index.ts b/plugins/template-function-request/src/index.ts index fe379c3f..89d6630e 100755 --- a/plugins/template-function-request/src/index.ts +++ b/plugins/template-function-request/src/index.ts @@ -1,64 +1,64 @@ -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; -import type { AnyModel, HttpUrlParameter } from '@yaakapp-internal/models'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import type { JSONPathResult } from '../../template-function-json'; -import { filterJSONPath } from '../../template-function-json'; -import type { XPathResult } from '../../template-function-xml'; -import { filterXPath } from '../../template-function-xml'; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; +import type { AnyModel, HttpUrlParameter } from "@yaakapp-internal/models"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import type { JSONPathResult } from "../../template-function-json"; +import { filterJSONPath } from "../../template-function-json"; +import type { XPathResult } from "../../template-function-xml"; +import { filterXPath } from "../../template-function-xml"; -const RETURN_FIRST = 'first'; -const RETURN_ALL = 'all'; -const RETURN_JOIN = 'join'; +const RETURN_FIRST = "first"; +const RETURN_ALL = "all"; +const RETURN_JOIN = "join"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'request.body.raw', - aliases: ['request.body'], + name: "request.body.raw", + aliases: ["request.body"], args: [ { - name: 'requestId', - label: 'Http Request', - type: 'http_request', + name: "requestId", + label: "Http Request", + type: "http_request", }, ], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { - const requestId = String(args.values.requestId ?? 'n/a'); + const requestId = String(args.values.requestId ?? "n/a"); const httpRequest = await ctx.httpRequest.getById({ id: requestId }); if (httpRequest == null) return null; return String( await ctx.templates.render({ - data: httpRequest.body?.text ?? '', + data: httpRequest.body?.text ?? "", purpose: args.purpose, }), ); }, }, { - name: 'request.body.path', - previewArgs: ['path'], + name: "request.body.path", + previewArgs: ["path"], args: [ - { name: 'requestId', label: 'Http Request', type: 'http_request' }, + { name: "requestId", label: "Http Request", type: "http_request" }, { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'select', - name: 'result', - label: 'Return Format', + type: "select", + name: "result", + label: "Return Format", defaultValue: RETURN_FIRST, options: [ - { label: 'First result', value: RETURN_FIRST }, - { label: 'All results', value: RETURN_ALL }, - { label: 'Join with separator', value: RETURN_JOIN }, + { label: "First result", value: RETURN_FIRST }, + { label: "All results", value: RETURN_ALL }, + { label: "Join with separator", value: RETURN_JOIN }, ], }, { - name: 'join', - type: 'text', - label: 'Separator', + name: "join", + type: "text", + label: "Separator", optional: true, - defaultValue: ', ', + defaultValue: ", ", dynamic(_ctx, args) { return { hidden: args.values.result !== RETURN_JOIN }; }, @@ -66,52 +66,52 @@ export const plugin: PluginDefinition = { ], }, { - type: 'text', - name: 'path', - label: 'JSONPath or XPath', - placeholder: '$.books[0].id or /books[0]/id', + type: "text", + name: "path", + label: "JSONPath or XPath", + placeholder: "$.books[0].id or /books[0]/id", dynamic: async (ctx, args) => { - const requestId = String(args.values.requestId ?? 'n/a'); + const requestId = String(args.values.requestId ?? "n/a"); const httpRequest = await ctx.httpRequest.getById({ id: requestId }); if (httpRequest == null) return null; const contentType = httpRequest.headers - .find((h) => h.name.toLowerCase() === 'content-type') - ?.value.toLowerCase() ?? ''; - if (contentType.includes('xml') || contentType?.includes('html')) { + .find((h) => h.name.toLowerCase() === "content-type") + ?.value.toLowerCase() ?? ""; + if (contentType.includes("xml") || contentType?.includes("html")) { return { - label: 'XPath', - placeholder: '/books[0]/id', - description: 'Enter an XPath expression used to filter the results', + label: "XPath", + placeholder: "/books[0]/id", + description: "Enter an XPath expression used to filter the results", }; } return { - label: 'JSONPath', - placeholder: '$.books[0].id', - description: 'Enter a JSONPath expression used to filter the results', + label: "JSONPath", + placeholder: "$.books[0].id", + description: "Enter a JSONPath expression used to filter the results", }; }, }, ], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { - const requestId = String(args.values.requestId ?? 'n/a'); + const requestId = String(args.values.requestId ?? "n/a"); const httpRequest = await ctx.httpRequest.getById({ id: requestId }); if (httpRequest == null) return null; - const body = httpRequest.body?.text ?? ''; + const body = httpRequest.body?.text ?? ""; try { const result: JSONPathResult = args.values.result === RETURN_ALL - ? 'all' + ? "all" : args.values.result === RETURN_JOIN - ? 'join' - : 'first'; + ? "join" + : "first"; return filterJSONPath( body, - String(args.values.path || ''), + String(args.values.path || ""), result, args.values.join == null ? null : String(args.values.join), ); @@ -122,13 +122,13 @@ export const plugin: PluginDefinition = { try { const result: XPathResult = args.values.result === RETURN_ALL - ? 'all' + ? "all" : args.values.result === RETURN_JOIN - ? 'join' - : 'first'; + ? "join" + : "first"; return filterXPath( body, - String(args.values.path || ''), + String(args.values.path || ""), result, args.values.join == null ? null : String(args.values.join), ); @@ -140,21 +140,21 @@ export const plugin: PluginDefinition = { }, }, { - name: 'request.header', - description: 'Read the value of a request header, by name', - previewArgs: ['header'], + name: "request.header", + description: "Read the value of a request header, by name", + previewArgs: ["header"], args: [ { - name: 'requestId', - label: 'Http Request', - type: 'http_request', + name: "requestId", + label: "Http Request", + type: "http_request", }, { - name: 'header', - label: 'Header Name', - type: 'text', + name: "header", + label: "Header Name", + type: "text", async dynamic(ctx, args) { - if (typeof args.values.requestId !== 'string') return null; + if (typeof args.values.requestId !== "string") return null; const request = await ctx.httpRequest.getById({ id: args.values.requestId }); if (request == null) return null; @@ -164,15 +164,15 @@ export const plugin: PluginDefinition = { placeholder: validHeaders[0]?.name, completionOptions: validHeaders.map((h) => ({ label: h.name, - type: 'constant', + type: "constant", })), }; }, }, ], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { - const headerName = String(args.values.header ?? ''); - const requestId = String(args.values.requestId ?? 'n/a'); + const headerName = String(args.values.header ?? ""); + const requestId = String(args.values.requestId ?? "n/a"); const httpRequest = await ctx.httpRequest.getById({ id: requestId }); if (httpRequest == null) return null; const header = httpRequest.headers.find( @@ -180,29 +180,29 @@ export const plugin: PluginDefinition = { ); return String( await ctx.templates.render({ - data: header?.value ?? '', + data: header?.value ?? "", purpose: args.purpose, }), ); }, }, { - name: 'request.param', + name: "request.param", args: [ { - name: 'requestId', - label: 'Http Request', - type: 'http_request', + name: "requestId", + label: "Http Request", + type: "http_request", }, { - name: 'param', - label: 'Param Name', - type: 'text', + name: "param", + label: "Param Name", + type: "text", }, ], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { - const paramName = String(args.values.param ?? ''); - const requestId = String(args.values.requestId ?? 'n/a'); + const paramName = String(args.values.param ?? ""); + const requestId = String(args.values.requestId ?? "n/a"); const httpRequest = await ctx.httpRequest.getById({ id: requestId }); if (httpRequest == null) return null; @@ -211,7 +211,7 @@ export const plugin: PluginDefinition = { purpose: args.purpose, }); - const querystring = renderedUrl.split('?')[1] ?? ''; + const querystring = renderedUrl.split("?")[1] ?? ""; const paramsFromUrl: HttpUrlParameter[] = new URLSearchParams(querystring) .entries() .map(([name, value]): HttpUrlParameter => ({ name, value })) @@ -222,23 +222,23 @@ export const plugin: PluginDefinition = { const foundParam = allEnabledParams.find((p) => p.name === paramName); const renderedValue = await ctx.templates.render({ - data: foundParam?.value ?? '', + data: foundParam?.value ?? "", purpose: args.purpose, }); return renderedValue; }, }, { - name: 'request.name', + name: "request.name", args: [ { - name: 'requestId', - label: 'Http Request', - type: 'http_request', + name: "requestId", + label: "Http Request", + type: "http_request", }, ], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { - const requestId = String(args.values.requestId ?? 'n/a'); + const requestId = String(args.values.requestId ?? "n/a"); const httpRequest = await ctx.httpRequest.getById({ id: requestId }); if (httpRequest == null) return null; @@ -250,37 +250,37 @@ export const plugin: PluginDefinition = { // TODO: Use a common function for this, but it fails to build on windows during CI if I try importing it here export function resolvedModelName(r: AnyModel | null): string { - if (r == null) return ''; + if (r == null) return ""; - if (!('url' in r) || r.model === 'plugin') { - return 'name' in r ? r.name : ''; + if (!("url" in r) || r.model === "plugin") { + return "name" in r ? r.name : ""; } // Return name if it has one - if ('name' in r && r.name) { + if ("name" in r && r.name) { return r.name; } // Replace variable syntax with variable name - const withoutVariables = r.url.replace(/\$\{\[\s*([^\]\s]+)\s*]}/g, '$1'); - if (withoutVariables.trim() === '') { - return r.model === 'http_request' - ? r.bodyType && r.bodyType === 'graphql' - ? 'GraphQL Request' - : 'HTTP Request' - : r.model === 'websocket_request' - ? 'WebSocket Request' - : 'gRPC Request'; + const withoutVariables = r.url.replace(/\$\{\[\s*([^\]\s]+)\s*]}/g, "$1"); + if (withoutVariables.trim() === "") { + return r.model === "http_request" + ? r.bodyType && r.bodyType === "graphql" + ? "GraphQL Request" + : "HTTP Request" + : r.model === "websocket_request" + ? "WebSocket Request" + : "gRPC Request"; } // GRPC gets nice short names - if (r.model === 'grpc_request' && r.service != null && r.method != null) { - const shortService = r.service.split('.').pop(); + if (r.model === "grpc_request" && r.service != null && r.method != null) { + const shortService = r.service.split(".").pop(); return `${shortService}/${r.method}`; } // Strip unnecessary protocol - const withoutProto = withoutVariables.replace(/^(http|https|ws|wss):\/\//, ''); + const withoutProto = withoutVariables.replace(/^(http|https|ws|wss):\/\//, ""); return withoutProto; } diff --git a/plugins/template-function-response/package.json b/plugins/template-function-response/package.json index b29948a7..f8153baf 100644 --- a/plugins/template-function-response/package.json +++ b/plugins/template-function-response/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-response", "displayName": "Response Template Functions", - "description": "Template functions for request chaining", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for request chaining", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-response/src/index.ts b/plugins/template-function-response/src/index.ts index a086f9c9..9de85ffb 100644 --- a/plugins/template-function-response/src/index.ts +++ b/plugins/template-function-response/src/index.ts @@ -1,4 +1,4 @@ -import { readFileSync } from 'node:fs'; +import { readFileSync } from "node:fs"; import type { CallTemplateFunctionArgs, Context, @@ -7,41 +7,41 @@ import type { HttpResponse, PluginDefinition, RenderPurpose, -} from '@yaakapp/api'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import type { JSONPathResult } from '../../template-function-json'; -import { filterJSONPath } from '../../template-function-json'; -import type { XPathResult } from '../../template-function-xml'; -import { filterXPath } from '../../template-function-xml'; +} from "@yaakapp/api"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import type { JSONPathResult } from "../../template-function-json"; +import { filterJSONPath } from "../../template-function-json"; +import type { XPathResult } from "../../template-function-xml"; +import { filterXPath } from "../../template-function-xml"; -const BEHAVIOR_TTL = 'ttl'; -const BEHAVIOR_ALWAYS = 'always'; -const BEHAVIOR_SMART = 'smart'; +const BEHAVIOR_TTL = "ttl"; +const BEHAVIOR_ALWAYS = "always"; +const BEHAVIOR_SMART = "smart"; -const RETURN_FIRST = 'first'; -const RETURN_ALL = 'all'; -const RETURN_JOIN = 'join'; +const RETURN_FIRST = "first"; +const RETURN_ALL = "all"; +const RETURN_JOIN = "join"; const behaviorArgs: DynamicTemplateFunctionArg = { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'select', - name: 'behavior', - label: 'Sending Behavior', + type: "select", + name: "behavior", + label: "Sending Behavior", defaultValue: BEHAVIOR_SMART, options: [ - { label: 'When no responses', value: BEHAVIOR_SMART }, - { label: 'Always', value: BEHAVIOR_ALWAYS }, - { label: 'When expired', value: BEHAVIOR_TTL }, + { label: "When no responses", value: BEHAVIOR_SMART }, + { label: "Always", value: BEHAVIOR_ALWAYS }, + { label: "When expired", value: BEHAVIOR_TTL }, ], }, { - type: 'text', - name: 'ttl', - label: 'TTL (seconds)', - placeholder: '0', - defaultValue: '0', + type: "text", + name: "ttl", + label: "TTL (seconds)", + placeholder: "0", + defaultValue: "0", description: 'Resend the request when the latest response is older than this many seconds, or if there are no responses yet. "0" means never expires', dynamic(_ctx, args) { @@ -52,42 +52,42 @@ const behaviorArgs: DynamicTemplateFunctionArg = { }; const requestArg: FormInput = { - type: 'http_request', - name: 'request', - label: 'Request', - defaultValue: '', // Make it not select the active one by default + type: "http_request", + name: "request", + label: "Request", + defaultValue: "", // Make it not select the active one by default }; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'response.header', - description: 'Read the value of a response header, by name', - previewArgs: ['header'], + name: "response.header", + description: "Read the value of a response header, by name", + previewArgs: ["header"], args: [ requestArg, behaviorArgs, { - type: 'text', - name: 'header', - label: 'Header Name', + type: "text", + name: "header", + label: "Header Name", async dynamic(ctx, args) { // Dynamic form config also runs during send-time rendering. // Keep this preview-only to avoid side-effect request sends. - if (args.purpose !== 'preview') return null; + if (args.purpose !== "preview") return null; const response = await getResponse(ctx, { - requestId: String(args.values.request || ''), + requestId: String(args.values.request || ""), purpose: args.purpose, behavior: args.values.behavior ? String(args.values.behavior) : null, - ttl: String(args.values.ttl || ''), + ttl: String(args.values.ttl || ""), }); return { placeholder: response?.headers[0]?.name, completionOptions: response?.headers.map((h) => ({ label: h.name, - type: 'constant', + type: "constant", })), }; }, @@ -97,47 +97,47 @@ export const plugin: PluginDefinition = { if (!args.values.request || !args.values.header) return null; const response = await getResponse(ctx, { - requestId: String(args.values.request || ''), + requestId: String(args.values.request || ""), purpose: args.purpose, behavior: args.values.behavior ? String(args.values.behavior) : null, - ttl: String(args.values.ttl || ''), + ttl: String(args.values.ttl || ""), }); if (response == null) return null; const header = response.headers.find( - (h) => h.name.toLowerCase() === String(args.values.header ?? '').toLowerCase(), + (h) => h.name.toLowerCase() === String(args.values.header ?? "").toLowerCase(), ); return header?.value ?? null; }, }, { - name: 'response.body.path', - description: 'Access a field of the response body using JsonPath or XPath', - aliases: ['response'], - previewArgs: ['path'], + name: "response.body.path", + description: "Access a field of the response body using JsonPath or XPath", + aliases: ["response"], + previewArgs: ["path"], args: [ requestArg, behaviorArgs, { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'select', - name: 'result', - label: 'Return Format', + type: "select", + name: "result", + label: "Return Format", defaultValue: RETURN_FIRST, options: [ - { label: 'First result', value: RETURN_FIRST }, - { label: 'All results', value: RETURN_ALL }, - { label: 'Join with separator', value: RETURN_JOIN }, + { label: "First result", value: RETURN_FIRST }, + { label: "All results", value: RETURN_ALL }, + { label: "Join with separator", value: RETURN_JOIN }, ], }, { - name: 'join', - type: 'text', - label: 'Separator', + name: "join", + type: "text", + label: "Separator", optional: true, - defaultValue: ', ', + defaultValue: ", ", dynamic(_ctx, args) { return { hidden: args.values.result !== RETURN_JOIN }; }, @@ -145,20 +145,20 @@ export const plugin: PluginDefinition = { ], }, { - type: 'text', - name: 'path', - label: 'JSONPath or XPath', - placeholder: '$.books[0].id or /books[0]/id', + type: "text", + name: "path", + label: "JSONPath or XPath", + placeholder: "$.books[0].id or /books[0]/id", dynamic: async (ctx, args) => { // Dynamic form config also runs during send-time rendering. // Keep this preview-only to avoid side-effect request sends. - if (args.purpose !== 'preview') return null; + if (args.purpose !== "preview") return null; const resp = await getResponse(ctx, { - requestId: String(args.values.request || ''), - purpose: 'preview', + requestId: String(args.values.request || ""), + purpose: "preview", behavior: args.values.behavior ? String(args.values.behavior) : null, - ttl: String(args.values.ttl || ''), + ttl: String(args.values.ttl || ""), }); if (resp == null) { @@ -167,20 +167,20 @@ export const plugin: PluginDefinition = { const contentType = resp?.headers - .find((h) => h.name.toLowerCase() === 'content-type') - ?.value.toLowerCase() ?? ''; - if (contentType.includes('xml') || contentType?.includes('html')) { + .find((h) => h.name.toLowerCase() === "content-type") + ?.value.toLowerCase() ?? ""; + if (contentType.includes("xml") || contentType?.includes("html")) { return { - label: 'XPath', - placeholder: '/books[0]/id', - description: 'Enter an XPath expression used to filter the results', + label: "XPath", + placeholder: "/books[0]/id", + description: "Enter an XPath expression used to filter the results", }; } return { - label: 'JSONPath', - placeholder: '$.books[0].id', - description: 'Enter a JSONPath expression used to filter the results', + label: "JSONPath", + placeholder: "$.books[0].id", + description: "Enter a JSONPath expression used to filter the results", }; }, }, @@ -189,10 +189,10 @@ export const plugin: PluginDefinition = { if (!args.values.request || !args.values.path) return null; const response = await getResponse(ctx, { - requestId: String(args.values.request || ''), + requestId: String(args.values.request || ""), purpose: args.purpose, behavior: args.values.behavior ? String(args.values.behavior) : null, - ttl: String(args.values.ttl || ''), + ttl: String(args.values.ttl || ""), }); if (response == null) return null; @@ -200,10 +200,10 @@ export const plugin: PluginDefinition = { return null; } - const BOM = '\ufeff'; + const BOM = "\ufeff"; let body: string; try { - body = readFileSync(response.bodyPath, 'utf-8').replace(BOM, ''); + body = readFileSync(response.bodyPath, "utf-8").replace(BOM, ""); } catch { return null; } @@ -211,13 +211,13 @@ export const plugin: PluginDefinition = { try { const result: JSONPathResult = args.values.result === RETURN_ALL - ? 'all' + ? "all" : args.values.result === RETURN_JOIN - ? 'join' - : 'first'; + ? "join" + : "first"; return filterJSONPath( body, - String(args.values.path || ''), + String(args.values.path || ""), result, args.values.join == null ? null : String(args.values.join), ); @@ -228,13 +228,13 @@ export const plugin: PluginDefinition = { try { const result: XPathResult = args.values.result === RETURN_ALL - ? 'all' + ? "all" : args.values.result === RETURN_JOIN - ? 'join' - : 'first'; + ? "join" + : "first"; return filterXPath( body, - String(args.values.path || ''), + String(args.values.path || ""), result, args.values.join == null ? null : String(args.values.join), ); @@ -246,18 +246,18 @@ export const plugin: PluginDefinition = { }, }, { - name: 'response.body.raw', - description: 'Access the entire response body, as text', - aliases: ['response'], + name: "response.body.raw", + description: "Access the entire response body, as text", + aliases: ["response"], args: [requestArg, behaviorArgs], async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise { if (!args.values.request) return null; const response = await getResponse(ctx, { - requestId: String(args.values.request || ''), + requestId: String(args.values.request || ""), purpose: args.purpose, behavior: args.values.behavior ? String(args.values.behavior) : null, - ttl: String(args.values.ttl || ''), + ttl: String(args.values.ttl || ""), }); if (response == null) return null; @@ -267,7 +267,7 @@ export const plugin: PluginDefinition = { let body: string; try { - body = readFileSync(response.bodyPath, 'utf-8'); + body = readFileSync(response.bodyPath, "utf-8"); } catch { return null; } @@ -294,14 +294,14 @@ async function getResponse( ): Promise { if (!requestId) return null; - const httpRequest = await ctx.httpRequest.getById({ id: requestId ?? 'n/a' }); + const httpRequest = await ctx.httpRequest.getById({ id: requestId ?? "n/a" }); if (httpRequest == null) { return null; } const responses = await ctx.httpResponse.find({ requestId: httpRequest.id, limit: 1 }); - if (behavior === 'never' && responses.length === 0) { + if (behavior === "never" && responses.length === 0) { return null; } @@ -309,12 +309,12 @@ async function getResponse( // Previews happen a ton, and we don't want to send too many times on "always," so treat // it as "smart" during preview. - const finalBehavior = behavior === 'always' && purpose === 'preview' ? 'smart' : behavior; + const finalBehavior = behavior === "always" && purpose === "preview" ? "smart" : behavior; // Send if no responses and "smart," or "always" if ( - (finalBehavior === 'smart' && response == null) || - finalBehavior === 'always' || + (finalBehavior === "smart" && response == null) || + finalBehavior === "always" || (finalBehavior === BEHAVIOR_TTL && shouldSendExpired(response, ttl)) ) { // Explicitly render the request before send (instead of relying on send() to render) so that we can @@ -328,7 +328,7 @@ async function getResponse( function shouldSendExpired(response: HttpResponse | null, ttl: string | null): boolean { if (response == null) return true; - const ttlSeconds = Number.parseInt(ttl || '0', 10) || 0; + const ttlSeconds = Number.parseInt(ttl || "0", 10) || 0; if (ttlSeconds === 0) return false; const nowMillis = Date.now(); const respMillis = new Date(`${response.createdAt}Z`).getTime(); diff --git a/plugins/template-function-timestamp/package.json b/plugins/template-function-timestamp/package.json index 570719aa..288009ef 100755 --- a/plugins/template-function-timestamp/package.json +++ b/plugins/template-function-timestamp/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-timestamp", "displayName": "Timestamp Template Functions", - "description": "Template functions for dealing with timestamps", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for dealing with timestamps", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev", diff --git a/plugins/template-function-timestamp/src/index.ts b/plugins/template-function-timestamp/src/index.ts index 556b9d00..9e486abf 100755 --- a/plugins/template-function-timestamp/src/index.ts +++ b/plugins/template-function-timestamp/src/index.ts @@ -1,7 +1,7 @@ -import type { PluginDefinition } from '@yaakapp/api'; -import type { TemplateFunctionArg } from '@yaakapp-internal/plugins'; +import type { PluginDefinition } from "@yaakapp/api"; +import type { TemplateFunctionArg } from "@yaakapp-internal/plugins"; -import type { ContextFn } from 'date-fns'; +import type { ContextFn } from "date-fns"; import { addDays, addHours, @@ -18,75 +18,75 @@ import { subMonths, subSeconds, subYears, -} from 'date-fns'; +} from "date-fns"; const dateArg: TemplateFunctionArg = { - type: 'text', - name: 'date', - label: 'Timestamp', + type: "text", + name: "date", + label: "Timestamp", optional: true, description: - 'Can be a timestamp in milliseconds, ISO string, or anything parseable by JS `new Date()`', + "Can be a timestamp in milliseconds, ISO string, or anything parseable by JS `new Date()`", placeholder: new Date().toISOString(), }; const expressionArg: TemplateFunctionArg = { - type: 'text', - name: 'expression', - label: 'Expression', + type: "text", + name: "expression", + label: "Expression", description: "Modification expression (eg. '-5d +2h 3m'). Available units: y, M, d, h, m, s", optional: true, - placeholder: '-5d +2h 3m', + placeholder: "-5d +2h 3m", }; const formatArg: TemplateFunctionArg = { - name: 'format', - label: 'Format String', + name: "format", + label: "Format String", description: "Format string to describe the output (eg. 'yyyy-MM-dd at HH:mm:ss')", optional: true, - placeholder: 'yyyy-MM-dd HH:mm:ss', - type: 'text', + placeholder: "yyyy-MM-dd HH:mm:ss", + type: "text", }; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'timestamp.unix', - description: 'Get the timestamp in seconds', + name: "timestamp.unix", + description: "Get the timestamp in seconds", args: [dateArg], onRender: async (_ctx, args) => { - const d = parseDateString(String(args.values.date ?? '')); + const d = parseDateString(String(args.values.date ?? "")); return String(Math.floor(d.getTime() / 1000)); }, }, { - name: 'timestamp.unixMillis', - description: 'Get the timestamp in milliseconds', + name: "timestamp.unixMillis", + description: "Get the timestamp in milliseconds", args: [dateArg], onRender: async (_ctx, args) => { - const d = parseDateString(String(args.values.date ?? '')); + const d = parseDateString(String(args.values.date ?? "")); return String(d.getTime()); }, }, { - name: 'timestamp.iso8601', - description: 'Get the date in ISO8601 format', + name: "timestamp.iso8601", + description: "Get the date in ISO8601 format", args: [dateArg], onRender: async (_ctx, args) => { - const d = parseDateString(String(args.values.date ?? '')); + const d = parseDateString(String(args.values.date ?? "")); return d.toISOString(); }, }, { - name: 'timestamp.format', - description: 'Format a date using a dayjs-compatible format string', + name: "timestamp.format", + description: "Format a date using a dayjs-compatible format string", args: [dateArg, formatArg], previewArgs: [formatArg.name], onRender: async (_ctx, args) => formatDatetime(args.values), }, { - name: 'timestamp.offset', - description: 'Get the offset of a date based on an expression', + name: "timestamp.offset", + description: "Get the offset of a date based on an expression", args: [dateArg, expressionArg], previewArgs: [expressionArg.name], onRender: async (_ctx, args) => calculateDatetime(args.values), @@ -96,18 +96,18 @@ export const plugin: PluginDefinition = { function applyDateOp(d: Date, sign: string, amount: number, unit: string): Date { switch (unit) { - case 'y': - return sign === '-' ? subYears(d, amount) : addYears(d, amount); - case 'M': - return sign === '-' ? subMonths(d, amount) : addMonths(d, amount); - case 'd': - return sign === '-' ? subDays(d, amount) : addDays(d, amount); - case 'h': - return sign === '-' ? subHours(d, amount) : addHours(d, amount); - case 'm': - return sign === '-' ? subMinutes(d, amount) : addMinutes(d, amount); - case 's': - return sign === '-' ? subSeconds(d, amount) : addSeconds(d, amount); + case "y": + return sign === "-" ? subYears(d, amount) : addYears(d, amount); + case "M": + return sign === "-" ? subMonths(d, amount) : addMonths(d, amount); + case "d": + return sign === "-" ? subDays(d, amount) : addDays(d, amount); + case "h": + return sign === "-" ? subHours(d, amount) : addHours(d, amount); + case "m": + return sign === "-" ? subMinutes(d, amount) : addMinutes(d, amount); + case "s": + return sign === "-" ? subSeconds(d, amount) : addSeconds(d, amount); default: throw new Error(`Invalid data calculation unit: ${unit}`); } @@ -120,7 +120,7 @@ function parseOp(op: string): { sign: string; amount: number; unit: string } | n } const [, sign, amount, unit] = match; if (!unit) return null; - return { sign: sign ?? '+', amount: Number(amount ?? 0), unit }; + return { sign: sign ?? "+", amount: Number(amount ?? 0), unit }; } function parseDateString(date: string): Date { @@ -143,11 +143,11 @@ function parseDateString(date: string): Date { export function calculateDatetime(args: { date?: string; expression?: string }): string { const { date, expression } = args; - let jsDate = parseDateString(date ?? ''); + let jsDate = parseDateString(date ?? ""); if (expression) { const ops = String(expression) - .split(' ') + .split(" ") .map((s) => s.trim()) .filter(Boolean); for (const op of ops) { @@ -167,6 +167,6 @@ export function formatDatetime(args: { in?: ContextFn; }): string { const { date, format } = args; - const d = parseDateString(date ?? ''); - return formatDate(d, String(format || 'yyyy-MM-dd HH:mm:ss'), { in: args.in }); + const d = parseDateString(date ?? ""); + return formatDate(d, String(format || "yyyy-MM-dd HH:mm:ss"), { in: args.in }); } diff --git a/plugins/template-function-timestamp/tests/formatDatetime.test.ts b/plugins/template-function-timestamp/tests/formatDatetime.test.ts index b4cf5639..0617a27a 100644 --- a/plugins/template-function-timestamp/tests/formatDatetime.test.ts +++ b/plugins/template-function-timestamp/tests/formatDatetime.test.ts @@ -1,72 +1,72 @@ -import { tz } from '@date-fns/tz'; -import { describe, expect, it } from 'vite-plus/test'; -import { calculateDatetime, formatDatetime } from '../src'; +import { tz } from "@date-fns/tz"; +import { describe, expect, it } from "vite-plus/test"; +import { calculateDatetime, formatDatetime } from "../src"; -describe('formatDatetime', () => { - it('returns formatted current date', () => { +describe("formatDatetime", () => { + it("returns formatted current date", () => { const result = formatDatetime({}); expect(result).toMatch(/\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}/); }); - it('returns formatted specific date', () => { - const result = formatDatetime({ date: '2025-07-13T12:34:56' }); - expect(result).toBe('2025-07-13 12:34:56'); + it("returns formatted specific date", () => { + const result = formatDatetime({ date: "2025-07-13T12:34:56" }); + expect(result).toBe("2025-07-13 12:34:56"); }); - it('returns formatted specific timestamp', () => { - const result = formatDatetime({ date: '1752435296000', in: tz('America/Vancouver') }); - expect(result).toBe('2025-07-13 12:34:56'); + it("returns formatted specific timestamp", () => { + const result = formatDatetime({ date: "1752435296000", in: tz("America/Vancouver") }); + expect(result).toBe("2025-07-13 12:34:56"); }); - it('returns formatted specific timestamp with decimals', () => { - const result = formatDatetime({ date: '1752435296000.19', in: tz('America/Vancouver') }); - expect(result).toBe('2025-07-13 12:34:56'); + it("returns formatted specific timestamp with decimals", () => { + const result = formatDatetime({ date: "1752435296000.19", in: tz("America/Vancouver") }); + expect(result).toBe("2025-07-13 12:34:56"); }); - it('returns formatted date with custom output', () => { - const result = formatDatetime({ date: '2025-07-13T12:34:56', format: 'dd/MM/yyyy' }); - expect(result).toBe('13/07/2025'); + it("returns formatted date with custom output", () => { + const result = formatDatetime({ date: "2025-07-13T12:34:56", format: "dd/MM/yyyy" }); + expect(result).toBe("13/07/2025"); }); - it('handles invalid date gracefully', () => { - expect(() => formatDatetime({ date: 'invalid-date' })).toThrow('Invalid date: invalid-date'); + it("handles invalid date gracefully", () => { + expect(() => formatDatetime({ date: "invalid-date" })).toThrow("Invalid date: invalid-date"); }); }); -describe('calculateDatetime', () => { - it('returns ISO string for current date', () => { +describe("calculateDatetime", () => { + it("returns ISO string for current date", () => { const result = calculateDatetime({}); expect(result).toMatch(/\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/); }); - it('returns ISO string for specific date', () => { - const result = calculateDatetime({ date: '2025-07-13T12:34:56Z' }); - expect(result).toBe('2025-07-13T12:34:56.000Z'); + it("returns ISO string for specific date", () => { + const result = calculateDatetime({ date: "2025-07-13T12:34:56Z" }); + expect(result).toBe("2025-07-13T12:34:56.000Z"); }); - it('applies calc operations', () => { - const result = calculateDatetime({ date: '2025-07-13T12:00:00Z', expression: '+1d 2h' }); - expect(result).toBe('2025-07-14T14:00:00.000Z'); + it("applies calc operations", () => { + const result = calculateDatetime({ date: "2025-07-13T12:00:00Z", expression: "+1d 2h" }); + expect(result).toBe("2025-07-14T14:00:00.000Z"); }); - it('applies negative calc operations', () => { - const result = calculateDatetime({ date: '2025-07-13T12:00:00Z', expression: '-1d -2h 1m' }); - expect(result).toBe('2025-07-12T10:01:00.000Z'); + it("applies negative calc operations", () => { + const result = calculateDatetime({ date: "2025-07-13T12:00:00Z", expression: "-1d -2h 1m" }); + expect(result).toBe("2025-07-12T10:01:00.000Z"); }); - it('throws error for invalid unit', () => { - expect(() => calculateDatetime({ date: '2025-07-13T12:00:00Z', expression: '+1x' })).toThrow( - 'Invalid date expression: +1x', + it("throws error for invalid unit", () => { + expect(() => calculateDatetime({ date: "2025-07-13T12:00:00Z", expression: "+1x" })).toThrow( + "Invalid date expression: +1x", ); }); - it('throws error for invalid unit weird', () => { - expect(() => calculateDatetime({ date: '2025-07-13T12:00:00Z', expression: '+1&#^%' })).toThrow( - 'Invalid date expression: +1&#^%', + it("throws error for invalid unit weird", () => { + expect(() => calculateDatetime({ date: "2025-07-13T12:00:00Z", expression: "+1&#^%" })).toThrow( + "Invalid date expression: +1&#^%", ); }); - it('throws error for bad expression', () => { + it("throws error for bad expression", () => { expect(() => - calculateDatetime({ date: '2025-07-13T12:00:00Z', expression: 'bad expr' }), - ).toThrow('Invalid date expression: bad'); + calculateDatetime({ date: "2025-07-13T12:00:00Z", expression: "bad expr" }), + ).toThrow("Invalid date expression: bad"); }); }); diff --git a/plugins/template-function-uuid/package.json b/plugins/template-function-uuid/package.json index 1a1656d5..203f556f 100644 --- a/plugins/template-function-uuid/package.json +++ b/plugins/template-function-uuid/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-uuid", "displayName": "UUID Template Functions", - "description": "Template functions for generating UUIDs", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for generating UUIDs", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/template-function-uuid/src/index.ts b/plugins/template-function-uuid/src/index.ts index 3d79f80b..39e103a2 100644 --- a/plugins/template-function-uuid/src/index.ts +++ b/plugins/template-function-uuid/src/index.ts @@ -1,27 +1,27 @@ -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; -import { v1, v3, v4, v5, v6, v7 } from 'uuid'; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; +import { v1, v3, v4, v5, v6, v7 } from "uuid"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'uuid.v1', - description: 'Generate a UUID V1', + name: "uuid.v1", + description: "Generate a UUID V1", args: [], async onRender(): Promise { return v1(); }, }, { - name: 'uuid.v3', - description: 'Generate a UUID V3', + name: "uuid.v3", + description: "Generate a UUID V3", args: [ - { type: 'text', name: 'name', label: 'Name' }, + { type: "text", name: "name", label: "Name" }, { - type: 'text', - name: 'namespace', - label: 'Namespace UUID', - description: 'A valid UUID to use as the namespace', - placeholder: '24ced880-3bf4-11f0-8329-cd053d577f0e', + type: "text", + name: "namespace", + label: "Namespace UUID", + description: "A valid UUID to use as the namespace", + placeholder: "24ced880-3bf4-11f0-8329-cd053d577f0e", }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { @@ -29,35 +29,35 @@ export const plugin: PluginDefinition = { }, }, { - name: 'uuid.v4', - description: 'Generate a UUID V4', + name: "uuid.v4", + description: "Generate a UUID V4", args: [], async onRender(): Promise { return v4(); }, }, { - name: 'uuid.v5', - description: 'Generate a UUID V5', + name: "uuid.v5", + description: "Generate a UUID V5", args: [ - { type: 'text', name: 'name', label: 'Name' }, - { type: 'text', name: 'namespace', label: 'Namespace' }, + { type: "text", name: "name", label: "Name" }, + { type: "text", name: "namespace", label: "Namespace" }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { return v5(String(args.values.name), String(args.values.namespace)); }, }, { - name: 'uuid.v6', - description: 'Generate a UUID V6', + name: "uuid.v6", + description: "Generate a UUID V6", args: [ { - type: 'text', - name: 'timestamp', - label: 'Timestamp', + type: "text", + name: "timestamp", + label: "Timestamp", optional: true, - description: 'Can be any format that can be parsed by JavaScript new Date(...)', - placeholder: '2025-05-28T11:15:00Z', + description: "Can be any format that can be parsed by JavaScript new Date(...)", + placeholder: "2025-05-28T11:15:00Z", }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { @@ -65,8 +65,8 @@ export const plugin: PluginDefinition = { }, }, { - name: 'uuid.v7', - description: 'Generate a UUID V7', + name: "uuid.v7", + description: "Generate a UUID V7", args: [], async onRender(): Promise { return v7(); diff --git a/plugins/template-function-xml/package.json b/plugins/template-function-xml/package.json index b819a793..b381b4ab 100755 --- a/plugins/template-function-xml/package.json +++ b/plugins/template-function-xml/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/template-function-xml", "displayName": "XML Template Functions", - "description": "Template functions for working with XML data", - "private": true, "version": "0.1.0", + "private": true, + "description": "Template functions for working with XML data", "main": "build/index.js", "types": "src/index.ts", "scripts": { diff --git a/plugins/template-function-xml/src/index.ts b/plugins/template-function-xml/src/index.ts index 01279588..1d3ec5c9 100755 --- a/plugins/template-function-xml/src/index.ts +++ b/plugins/template-function-xml/src/index.ts @@ -1,53 +1,53 @@ /* oxlint-disable no-base-to-string */ -import { DOMParser } from '@xmldom/xmldom'; -import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api'; -import xpath from 'xpath'; +import { DOMParser } from "@xmldom/xmldom"; +import type { CallTemplateFunctionArgs, Context, PluginDefinition } from "@yaakapp/api"; +import xpath from "xpath"; -const RETURN_FIRST = 'first'; -const RETURN_ALL = 'all'; -const RETURN_JOIN = 'join'; +const RETURN_FIRST = "first"; +const RETURN_ALL = "all"; +const RETURN_JOIN = "join"; export const plugin: PluginDefinition = { templateFunctions: [ { - name: 'xml.xpath', - description: 'Filter XML-formatted text using XPath syntax', - previewArgs: ['query'], + name: "xml.xpath", + description: "Filter XML-formatted text using XPath syntax", + previewArgs: ["query"], args: [ { - type: 'text', - name: 'input', - label: 'Input', + type: "text", + name: "input", + label: "Input", multiLine: true, - placeholder: '', + placeholder: "", }, { - type: 'h_stack', + type: "h_stack", inputs: [ { - type: 'select', - name: 'result', - label: 'Return Format', + type: "select", + name: "result", + label: "Return Format", defaultValue: RETURN_FIRST, options: [ - { label: 'First result', value: RETURN_FIRST }, - { label: 'All results', value: RETURN_ALL }, - { label: 'Join with separator', value: RETURN_JOIN }, + { label: "First result", value: RETURN_FIRST }, + { label: "All results", value: RETURN_ALL }, + { label: "Join with separator", value: RETURN_JOIN }, ], }, { - name: 'join', - type: 'text', - label: 'Separator', + name: "join", + type: "text", + label: "Separator", optional: true, - defaultValue: ', ', + defaultValue: ", ", dynamic(_ctx, args) { return { hidden: args.values.result !== RETURN_JOIN }; }, }, ], }, - { type: 'text', name: 'query', label: 'Query', placeholder: '//foo' }, + { type: "text", name: "query", label: "Query", placeholder: "//foo" }, ], async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise { try { @@ -62,7 +62,7 @@ export const plugin: PluginDefinition = { ], }; -export type XPathResult = 'first' | 'join' | 'all'; +export type XPathResult = "first" | "join" | "all"; export function filterXPath( body: string, path: string, @@ -70,17 +70,17 @@ export function filterXPath( join: string | null, ): string { // oxlint-disable-next-line no-explicit-any - const doc: any = new DOMParser().parseFromString(body, 'text/xml'); + const doc: any = new DOMParser().parseFromString(body, "text/xml"); const items = xpath.select(path, doc, false); if (!Array.isArray(items)) { return String(items); } - if (!Array.isArray(items) || result === 'first') { - return items[0] != null ? String(items[0].firstChild ?? '') : ''; + if (!Array.isArray(items) || result === "first") { + return items[0] != null ? String(items[0].firstChild ?? "") : ""; } - if (result === 'join') { - return items.map((item) => String(item.firstChild ?? '')).join(join ?? ''); + if (result === "join") { + return items.map((item) => String(item.firstChild ?? "")).join(join ?? ""); } // Not sure what cases this happens in (?) return String(items); diff --git a/plugins/themes-yaak/package.json b/plugins/themes-yaak/package.json index c1ef7fbc..ddbb6868 100644 --- a/plugins/themes-yaak/package.json +++ b/plugins/themes-yaak/package.json @@ -1,9 +1,9 @@ { "name": "@yaak/themes-yaak", "displayName": "Yaak Themes", - "description": "Default themes for Yaak", - "private": true, "version": "0.1.0", + "private": true, + "description": "Default themes for Yaak", "scripts": { "build": "yaakcli build", "dev": "yaakcli dev" diff --git a/plugins/themes-yaak/src/index.ts b/plugins/themes-yaak/src/index.ts index 11a1fa7a..26879aab 100644 --- a/plugins/themes-yaak/src/index.ts +++ b/plugins/themes-yaak/src/index.ts @@ -1,28 +1,28 @@ -import type { PluginDefinition } from '@yaakapp/api'; -import { andromeda } from './themes/andromeda'; -import { atomOneDark } from './themes/atom-one-dark'; -import { ayuDark, ayuLight, ayuMirage } from './themes/ayu'; -import { blulocoDark, blulocoLight } from './themes/bluloco'; +import type { PluginDefinition } from "@yaakapp/api"; +import { andromeda } from "./themes/andromeda"; +import { atomOneDark } from "./themes/atom-one-dark"; +import { ayuDark, ayuLight, ayuMirage } from "./themes/ayu"; +import { blulocoDark, blulocoLight } from "./themes/bluloco"; import { catppuccinFrappe, catppuccinLatte, catppuccinMacchiato, catppuccinMocha, -} from './themes/catppuccin'; -import { cobalt2 } from './themes/cobalt2'; -import { dracula } from './themes/dracula'; -import { everforestDark, everforestLight } from './themes/everforest'; -import { fleetDark, fleetDarkPurple, fleetLight } from './themes/fleet'; -import { githubDark, githubLight } from './themes/github'; -import { githubDarkDimmed } from './themes/github-dimmed'; -import { gruvbox } from './themes/gruvbox'; +} from "./themes/catppuccin"; +import { cobalt2 } from "./themes/cobalt2"; +import { dracula } from "./themes/dracula"; +import { everforestDark, everforestLight } from "./themes/everforest"; +import { fleetDark, fleetDarkPurple, fleetLight } from "./themes/fleet"; +import { githubDark, githubLight } from "./themes/github"; +import { githubDarkDimmed } from "./themes/github-dimmed"; +import { gruvbox } from "./themes/gruvbox"; // Yaak themes -import { highContrast, highContrastDark } from './themes/high-contrast'; -import { horizon } from './themes/horizon'; -import { hotdogStand } from './themes/hotdog-stand'; -import { materialDarker } from './themes/material-darker'; -import { materialOcean } from './themes/material-ocean'; -import { materialPalenight } from './themes/material-palenight'; +import { highContrast, highContrastDark } from "./themes/high-contrast"; +import { horizon } from "./themes/horizon"; +import { hotdogStand } from "./themes/hotdog-stand"; +import { materialDarker } from "./themes/material-darker"; +import { materialOcean } from "./themes/material-ocean"; +import { materialPalenight } from "./themes/material-palenight"; import { monokaiPro, monokaiProClassic, @@ -30,24 +30,24 @@ import { monokaiProOctagon, monokaiProRistretto, monokaiProSpectrum, -} from './themes/monokai-pro'; -import { moonlight } from './themes/moonlight'; -import { lightOwl, nightOwl } from './themes/night-owl'; -import { noctisAzureus } from './themes/noctis'; -import { nord, nordLight, nordLightBrighter } from './themes/nord'; +} from "./themes/monokai-pro"; +import { moonlight } from "./themes/moonlight"; +import { lightOwl, nightOwl } from "./themes/night-owl"; +import { noctisAzureus } from "./themes/noctis"; +import { nord, nordLight, nordLightBrighter } from "./themes/nord"; // VSCode themes -import { oneDarkPro } from './themes/one-dark-pro'; -import { pandaSyntax } from './themes/panda'; -import { relaxing } from './themes/relaxing'; -import { rosePine, rosePineDawn, rosePineMoon } from './themes/rose-pine'; -import { shadesOfPurple, shadesOfPurpleSuperDark } from './themes/shades-of-purple'; -import { slackAubergine } from './themes/slack'; -import { solarizedDark, solarizedLight } from './themes/solarized'; -import { synthwave84 } from './themes/synthwave-84'; -import { tokyoNight, tokyoNightDay, tokyoNightStorm } from './themes/tokyo-night'; -import { triangle } from './themes/triangle'; -import { vitesseDark, vitesseLight } from './themes/vitesse'; -import { winterIsComing } from './themes/winter-is-coming'; +import { oneDarkPro } from "./themes/one-dark-pro"; +import { pandaSyntax } from "./themes/panda"; +import { relaxing } from "./themes/relaxing"; +import { rosePine, rosePineDawn, rosePineMoon } from "./themes/rose-pine"; +import { shadesOfPurple, shadesOfPurpleSuperDark } from "./themes/shades-of-purple"; +import { slackAubergine } from "./themes/slack"; +import { solarizedDark, solarizedLight } from "./themes/solarized"; +import { synthwave84 } from "./themes/synthwave-84"; +import { tokyoNight, tokyoNightDay, tokyoNightStorm } from "./themes/tokyo-night"; +import { triangle } from "./themes/triangle"; +import { vitesseDark, vitesseLight } from "./themes/vitesse"; +import { winterIsComing } from "./themes/winter-is-coming"; export const plugin: PluginDefinition = { themes: [ diff --git a/plugins/themes-yaak/src/themes/andromeda.ts b/plugins/themes-yaak/src/themes/andromeda.ts index 5e0c4041..b762d5a5 100644 --- a/plugins/themes-yaak/src/themes/andromeda.ts +++ b/plugins/themes-yaak/src/themes/andromeda.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const andromeda: Theme = { - id: 'andromeda', - label: 'Andromeda', + id: "andromeda", + label: "Andromeda", dark: true, base: { - surface: 'hsl(251, 25%, 15%)', - surfaceHighlight: 'hsl(251, 22%, 20%)', - text: 'hsl(220, 10%, 85%)', - textSubtle: 'hsl(220, 8%, 60%)', - textSubtlest: 'hsl(220, 6%, 45%)', - primary: 'hsl(293, 75%, 68%)', - secondary: 'hsl(220, 8%, 60%)', - info: 'hsl(180, 60%, 60%)', - success: 'hsl(85, 60%, 55%)', - notice: 'hsl(38, 100%, 65%)', - warning: 'hsl(25, 95%, 60%)', - danger: 'hsl(358, 80%, 60%)', + surface: "hsl(251, 25%, 15%)", + surfaceHighlight: "hsl(251, 22%, 20%)", + text: "hsl(220, 10%, 85%)", + textSubtle: "hsl(220, 8%, 60%)", + textSubtlest: "hsl(220, 6%, 45%)", + primary: "hsl(293, 75%, 68%)", + secondary: "hsl(220, 8%, 60%)", + info: "hsl(180, 60%, 60%)", + success: "hsl(85, 60%, 55%)", + notice: "hsl(38, 100%, 65%)", + warning: "hsl(25, 95%, 60%)", + danger: "hsl(358, 80%, 60%)", }, components: { dialog: { - surface: 'hsl(251, 25%, 12%)', + surface: "hsl(251, 25%, 12%)", }, sidebar: { - surface: 'hsl(251, 23%, 13%)', - border: 'hsl(251, 20%, 18%)', + surface: "hsl(251, 23%, 13%)", + border: "hsl(251, 20%, 18%)", }, appHeader: { - surface: 'hsl(251, 25%, 11%)', - border: 'hsl(251, 20%, 16%)', + surface: "hsl(251, 25%, 11%)", + border: "hsl(251, 20%, 16%)", }, responsePane: { - surface: 'hsl(251, 23%, 13%)', - border: 'hsl(251, 20%, 18%)', + surface: "hsl(251, 23%, 13%)", + border: "hsl(251, 20%, 18%)", }, button: { - primary: 'hsl(293, 75%, 61%)', - secondary: 'hsl(220, 8%, 53%)', - info: 'hsl(180, 60%, 53%)', - success: 'hsl(85, 60%, 48%)', - notice: 'hsl(38, 100%, 58%)', - warning: 'hsl(25, 95%, 53%)', - danger: 'hsl(358, 80%, 53%)', + primary: "hsl(293, 75%, 61%)", + secondary: "hsl(220, 8%, 53%)", + info: "hsl(180, 60%, 53%)", + success: "hsl(85, 60%, 48%)", + notice: "hsl(38, 100%, 58%)", + warning: "hsl(25, 95%, 53%)", + danger: "hsl(358, 80%, 53%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/atom-one-dark.ts b/plugins/themes-yaak/src/themes/atom-one-dark.ts index 226226b9..d4490248 100644 --- a/plugins/themes-yaak/src/themes/atom-one-dark.ts +++ b/plugins/themes-yaak/src/themes/atom-one-dark.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const atomOneDark: Theme = { - id: 'atom-one-dark', - label: 'Atom One Dark', + id: "atom-one-dark", + label: "Atom One Dark", dark: true, base: { - surface: 'hsl(220, 13%, 18%)', - surfaceHighlight: 'hsl(219, 13%, 22%)', - text: 'hsl(219, 14%, 71%)', - textSubtle: 'hsl(220, 9%, 55%)', - textSubtlest: 'hsl(220, 8%, 45%)', - primary: 'hsl(286, 60%, 67%)', - secondary: 'hsl(220, 9%, 55%)', - info: 'hsl(207, 82%, 66%)', - success: 'hsl(95, 38%, 62%)', - notice: 'hsl(39, 67%, 69%)', - warning: 'hsl(29, 54%, 61%)', - danger: 'hsl(355, 65%, 65%)', + surface: "hsl(220, 13%, 18%)", + surfaceHighlight: "hsl(219, 13%, 22%)", + text: "hsl(219, 14%, 71%)", + textSubtle: "hsl(220, 9%, 55%)", + textSubtlest: "hsl(220, 8%, 45%)", + primary: "hsl(286, 60%, 67%)", + secondary: "hsl(220, 9%, 55%)", + info: "hsl(207, 82%, 66%)", + success: "hsl(95, 38%, 62%)", + notice: "hsl(39, 67%, 69%)", + warning: "hsl(29, 54%, 61%)", + danger: "hsl(355, 65%, 65%)", }, components: { dialog: { - surface: 'hsl(220, 13%, 14%)', + surface: "hsl(220, 13%, 14%)", }, sidebar: { - surface: 'hsl(220, 13%, 16%)', - border: 'hsl(220, 13%, 20%)', + surface: "hsl(220, 13%, 16%)", + border: "hsl(220, 13%, 20%)", }, appHeader: { - surface: 'hsl(220, 13%, 12%)', - border: 'hsl(220, 13%, 18%)', + surface: "hsl(220, 13%, 12%)", + border: "hsl(220, 13%, 18%)", }, responsePane: { - surface: 'hsl(220, 13%, 16%)', - border: 'hsl(220, 13%, 20%)', + surface: "hsl(220, 13%, 16%)", + border: "hsl(220, 13%, 20%)", }, button: { - primary: 'hsl(286, 60%, 60%)', - secondary: 'hsl(220, 9%, 48%)', - info: 'hsl(207, 82%, 59%)', - success: 'hsl(95, 38%, 55%)', - notice: 'hsl(39, 67%, 62%)', - warning: 'hsl(29, 54%, 54%)', - danger: 'hsl(355, 65%, 58%)', + primary: "hsl(286, 60%, 60%)", + secondary: "hsl(220, 9%, 48%)", + info: "hsl(207, 82%, 59%)", + success: "hsl(95, 38%, 55%)", + notice: "hsl(39, 67%, 62%)", + warning: "hsl(29, 54%, 54%)", + danger: "hsl(355, 65%, 58%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/ayu.ts b/plugins/themes-yaak/src/themes/ayu.ts index 4ab980d6..35f19150 100644 --- a/plugins/themes-yaak/src/themes/ayu.ts +++ b/plugins/themes-yaak/src/themes/ayu.ts @@ -1,122 +1,122 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const ayuDark: Theme = { - id: 'ayu-dark', - label: 'Ayu Dark', + id: "ayu-dark", + label: "Ayu Dark", dark: true, base: { - surface: 'hsl(220, 25%, 10%)', - surfaceHighlight: 'hsl(220, 20%, 15%)', - text: 'hsl(210, 22%, 78%)', - textSubtle: 'hsl(40, 13%, 50%)', - textSubtlest: 'hsl(220, 10%, 40%)', - primary: 'hsl(38, 100%, 56%)', - secondary: 'hsl(210, 15%, 55%)', - info: 'hsl(200, 80%, 60%)', - success: 'hsl(100, 75%, 60%)', - notice: 'hsl(38, 100%, 56%)', - warning: 'hsl(25, 100%, 60%)', - danger: 'hsl(345, 80%, 60%)', + surface: "hsl(220, 25%, 10%)", + surfaceHighlight: "hsl(220, 20%, 15%)", + text: "hsl(210, 22%, 78%)", + textSubtle: "hsl(40, 13%, 50%)", + textSubtlest: "hsl(220, 10%, 40%)", + primary: "hsl(38, 100%, 56%)", + secondary: "hsl(210, 15%, 55%)", + info: "hsl(200, 80%, 60%)", + success: "hsl(100, 75%, 60%)", + notice: "hsl(38, 100%, 56%)", + warning: "hsl(25, 100%, 60%)", + danger: "hsl(345, 80%, 60%)", }, components: { dialog: { - surface: 'hsl(220, 25%, 8%)', + surface: "hsl(220, 25%, 8%)", }, sidebar: { - surface: 'hsl(220, 22%, 12%)', - border: 'hsl(220, 20%, 16%)', + surface: "hsl(220, 22%, 12%)", + border: "hsl(220, 20%, 16%)", }, appHeader: { - surface: 'hsl(220, 25%, 7%)', - border: 'hsl(220, 20%, 13%)', + surface: "hsl(220, 25%, 7%)", + border: "hsl(220, 20%, 13%)", }, responsePane: { - surface: 'hsl(220, 22%, 12%)', - border: 'hsl(220, 20%, 16%)', + surface: "hsl(220, 22%, 12%)", + border: "hsl(220, 20%, 16%)", }, button: { - primary: 'hsl(38, 100%, 50%)', - secondary: 'hsl(210, 15%, 48%)', - info: 'hsl(200, 80%, 53%)', - success: 'hsl(100, 75%, 53%)', - notice: 'hsl(38, 100%, 50%)', - warning: 'hsl(25, 100%, 53%)', - danger: 'hsl(345, 80%, 53%)', + primary: "hsl(38, 100%, 50%)", + secondary: "hsl(210, 15%, 48%)", + info: "hsl(200, 80%, 53%)", + success: "hsl(100, 75%, 53%)", + notice: "hsl(38, 100%, 50%)", + warning: "hsl(25, 100%, 53%)", + danger: "hsl(345, 80%, 53%)", }, }, }; export const ayuMirage: Theme = { - id: 'ayu-mirage', - label: 'Ayu Mirage', + id: "ayu-mirage", + label: "Ayu Mirage", dark: true, base: { - surface: 'hsl(226, 23%, 17%)', - surfaceHighlight: 'hsl(226, 20%, 22%)', - text: 'hsl(212, 15%, 81%)', - textSubtle: 'hsl(212, 12%, 55%)', - textSubtlest: 'hsl(212, 10%, 45%)', - primary: 'hsl(38, 100%, 67%)', - secondary: 'hsl(212, 12%, 55%)', - info: 'hsl(200, 80%, 70%)', - success: 'hsl(100, 50%, 68%)', - notice: 'hsl(38, 100%, 67%)', - warning: 'hsl(25, 100%, 70%)', - danger: 'hsl(345, 80%, 70%)', + surface: "hsl(226, 23%, 17%)", + surfaceHighlight: "hsl(226, 20%, 22%)", + text: "hsl(212, 15%, 81%)", + textSubtle: "hsl(212, 12%, 55%)", + textSubtlest: "hsl(212, 10%, 45%)", + primary: "hsl(38, 100%, 67%)", + secondary: "hsl(212, 12%, 55%)", + info: "hsl(200, 80%, 70%)", + success: "hsl(100, 50%, 68%)", + notice: "hsl(38, 100%, 67%)", + warning: "hsl(25, 100%, 70%)", + danger: "hsl(345, 80%, 70%)", }, components: { dialog: { - surface: 'hsl(226, 23%, 14%)', + surface: "hsl(226, 23%, 14%)", }, sidebar: { - surface: 'hsl(226, 22%, 15%)', - border: 'hsl(226, 20%, 20%)', + surface: "hsl(226, 22%, 15%)", + border: "hsl(226, 20%, 20%)", }, appHeader: { - surface: 'hsl(226, 23%, 12%)', - border: 'hsl(226, 20%, 17%)', + surface: "hsl(226, 23%, 12%)", + border: "hsl(226, 20%, 17%)", }, responsePane: { - surface: 'hsl(226, 22%, 15%)', - border: 'hsl(226, 20%, 20%)', + surface: "hsl(226, 22%, 15%)", + border: "hsl(226, 20%, 20%)", }, button: { - primary: 'hsl(38, 100%, 60%)', - info: 'hsl(200, 80%, 63%)', - success: 'hsl(100, 50%, 61%)', - notice: 'hsl(38, 100%, 60%)', - warning: 'hsl(25, 100%, 63%)', - danger: 'hsl(345, 80%, 63%)', + primary: "hsl(38, 100%, 60%)", + info: "hsl(200, 80%, 63%)", + success: "hsl(100, 50%, 61%)", + notice: "hsl(38, 100%, 60%)", + warning: "hsl(25, 100%, 63%)", + danger: "hsl(345, 80%, 63%)", }, }, }; export const ayuLight: Theme = { - id: 'ayu-light', - label: 'Ayu Light', + id: "ayu-light", + label: "Ayu Light", dark: false, base: { - surface: 'hsl(40, 22%, 97%)', - surfaceHighlight: 'hsl(40, 20%, 93%)', - text: 'hsl(214, 10%, 35%)', - textSubtle: 'hsl(214, 8%, 50%)', - textSubtlest: 'hsl(214, 6%, 60%)', - primary: 'hsl(35, 100%, 45%)', - secondary: 'hsl(214, 8%, 50%)', - info: 'hsl(200, 75%, 45%)', - success: 'hsl(100, 60%, 40%)', - notice: 'hsl(35, 100%, 45%)', - warning: 'hsl(22, 100%, 50%)', - danger: 'hsl(345, 70%, 55%)', + surface: "hsl(40, 22%, 97%)", + surfaceHighlight: "hsl(40, 20%, 93%)", + text: "hsl(214, 10%, 35%)", + textSubtle: "hsl(214, 8%, 50%)", + textSubtlest: "hsl(214, 6%, 60%)", + primary: "hsl(35, 100%, 45%)", + secondary: "hsl(214, 8%, 50%)", + info: "hsl(200, 75%, 45%)", + success: "hsl(100, 60%, 40%)", + notice: "hsl(35, 100%, 45%)", + warning: "hsl(22, 100%, 50%)", + danger: "hsl(345, 70%, 55%)", }, components: { sidebar: { - surface: 'hsl(40, 20%, 95%)', - border: 'hsl(40, 15%, 90%)', + surface: "hsl(40, 20%, 95%)", + border: "hsl(40, 15%, 90%)", }, appHeader: { - surface: 'hsl(40, 20%, 93%)', - border: 'hsl(40, 15%, 88%)', + surface: "hsl(40, 20%, 93%)", + border: "hsl(40, 15%, 88%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/bluloco.ts b/plugins/themes-yaak/src/themes/bluloco.ts index 846eff0a..62f39b3c 100644 --- a/plugins/themes-yaak/src/themes/bluloco.ts +++ b/plugins/themes-yaak/src/themes/bluloco.ts @@ -1,77 +1,77 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const blulocoDark: Theme = { - id: 'bluloco-dark', - label: 'Bluloco Dark', + id: "bluloco-dark", + label: "Bluloco Dark", dark: true, base: { - surface: 'hsl(230, 20%, 14%)', - surfaceHighlight: 'hsl(230, 17%, 19%)', - text: 'hsl(220, 15%, 80%)', - textSubtle: 'hsl(220, 10%, 55%)', - textSubtlest: 'hsl(220, 8%, 42%)', - primary: 'hsl(218, 85%, 65%)', - secondary: 'hsl(220, 10%, 55%)', - info: 'hsl(218, 85%, 65%)', - success: 'hsl(95, 55%, 55%)', - notice: 'hsl(37, 90%, 60%)', - warning: 'hsl(22, 85%, 55%)', - danger: 'hsl(355, 75%, 60%)', + surface: "hsl(230, 20%, 14%)", + surfaceHighlight: "hsl(230, 17%, 19%)", + text: "hsl(220, 15%, 80%)", + textSubtle: "hsl(220, 10%, 55%)", + textSubtlest: "hsl(220, 8%, 42%)", + primary: "hsl(218, 85%, 65%)", + secondary: "hsl(220, 10%, 55%)", + info: "hsl(218, 85%, 65%)", + success: "hsl(95, 55%, 55%)", + notice: "hsl(37, 90%, 60%)", + warning: "hsl(22, 85%, 55%)", + danger: "hsl(355, 75%, 60%)", }, components: { dialog: { - surface: 'hsl(230, 20%, 11%)', + surface: "hsl(230, 20%, 11%)", }, sidebar: { - surface: 'hsl(230, 18%, 12%)', - border: 'hsl(230, 16%, 17%)', + surface: "hsl(230, 18%, 12%)", + border: "hsl(230, 16%, 17%)", }, appHeader: { - surface: 'hsl(230, 20%, 10%)', - border: 'hsl(230, 16%, 15%)', + surface: "hsl(230, 20%, 10%)", + border: "hsl(230, 16%, 15%)", }, responsePane: { - surface: 'hsl(230, 18%, 12%)', - border: 'hsl(230, 16%, 17%)', + surface: "hsl(230, 18%, 12%)", + border: "hsl(230, 16%, 17%)", }, button: { - primary: 'hsl(218, 85%, 58%)', - secondary: 'hsl(220, 10%, 48%)', - info: 'hsl(218, 85%, 58%)', - success: 'hsl(95, 55%, 48%)', - notice: 'hsl(37, 90%, 53%)', - warning: 'hsl(22, 85%, 48%)', - danger: 'hsl(355, 75%, 53%)', + primary: "hsl(218, 85%, 58%)", + secondary: "hsl(220, 10%, 48%)", + info: "hsl(218, 85%, 58%)", + success: "hsl(95, 55%, 48%)", + notice: "hsl(37, 90%, 53%)", + warning: "hsl(22, 85%, 48%)", + danger: "hsl(355, 75%, 53%)", }, }, }; export const blulocoLight: Theme = { - id: 'bluloco-light', - label: 'Bluloco Light', + id: "bluloco-light", + label: "Bluloco Light", dark: false, base: { - surface: 'hsl(0, 0%, 98%)', - surfaceHighlight: 'hsl(220, 15%, 94%)', - text: 'hsl(228, 18%, 30%)', - textSubtle: 'hsl(228, 10%, 48%)', - textSubtlest: 'hsl(228, 8%, 58%)', - primary: 'hsl(218, 80%, 48%)', - secondary: 'hsl(228, 10%, 48%)', - info: 'hsl(218, 80%, 48%)', - success: 'hsl(138, 55%, 40%)', - notice: 'hsl(35, 85%, 45%)', - warning: 'hsl(22, 80%, 48%)', - danger: 'hsl(355, 70%, 48%)', + surface: "hsl(0, 0%, 98%)", + surfaceHighlight: "hsl(220, 15%, 94%)", + text: "hsl(228, 18%, 30%)", + textSubtle: "hsl(228, 10%, 48%)", + textSubtlest: "hsl(228, 8%, 58%)", + primary: "hsl(218, 80%, 48%)", + secondary: "hsl(228, 10%, 48%)", + info: "hsl(218, 80%, 48%)", + success: "hsl(138, 55%, 40%)", + notice: "hsl(35, 85%, 45%)", + warning: "hsl(22, 80%, 48%)", + danger: "hsl(355, 70%, 48%)", }, components: { sidebar: { - surface: 'hsl(220, 15%, 96%)', - border: 'hsl(220, 12%, 90%)', + surface: "hsl(220, 15%, 96%)", + border: "hsl(220, 12%, 90%)", }, appHeader: { - surface: 'hsl(220, 15%, 94%)', - border: 'hsl(220, 12%, 88%)', + surface: "hsl(220, 15%, 94%)", + border: "hsl(220, 12%, 88%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/catppuccin.ts b/plugins/themes-yaak/src/themes/catppuccin.ts index 062c400a..d5e31e05 100644 --- a/plugins/themes-yaak/src/themes/catppuccin.ts +++ b/plugins/themes-yaak/src/themes/catppuccin.ts @@ -1,165 +1,165 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const catppuccinFrappe: Theme = { - id: 'catppuccin-frappe', - label: 'Catppuccin Frappé', + id: "catppuccin-frappe", + label: "Catppuccin Frappé", dark: true, base: { - surface: 'hsl(231,19%,20%)', - text: 'hsl(227,70%,87%)', - textSubtle: 'hsl(228,29%,73%)', - textSubtlest: 'hsl(227,17%,58%)', - primary: 'hsl(277,59%,76%)', - secondary: 'hsl(228,39%,80%)', - info: 'hsl(222,74%,74%)', - success: 'hsl(96,44%,68%)', - notice: 'hsl(40,62%,73%)', - warning: 'hsl(20,79%,70%)', - danger: 'hsl(359,68%,71%)', + surface: "hsl(231,19%,20%)", + text: "hsl(227,70%,87%)", + textSubtle: "hsl(228,29%,73%)", + textSubtlest: "hsl(227,17%,58%)", + primary: "hsl(277,59%,76%)", + secondary: "hsl(228,39%,80%)", + info: "hsl(222,74%,74%)", + success: "hsl(96,44%,68%)", + notice: "hsl(40,62%,73%)", + warning: "hsl(20,79%,70%)", + danger: "hsl(359,68%,71%)", }, components: { dialog: { - surface: 'hsl(240,21%,12%)', + surface: "hsl(240,21%,12%)", }, sidebar: { - surface: 'hsl(229,19%,23%)', - border: 'hsl(229,19%,27%)', + surface: "hsl(229,19%,23%)", + border: "hsl(229,19%,27%)", }, appHeader: { - surface: 'hsl(229,20%,17%)', - border: 'hsl(229,20%,25%)', + surface: "hsl(229,20%,17%)", + border: "hsl(229,20%,25%)", }, responsePane: { - surface: 'hsl(229,19%,23%)', - border: 'hsl(229,19%,27%)', + surface: "hsl(229,19%,23%)", + border: "hsl(229,19%,27%)", }, button: { - primary: 'hsl(277,59%,68%)', - secondary: 'hsl(228,39%,72%)', - info: 'hsl(222,74%,67%)', - success: 'hsl(96,44%,61%)', - notice: 'hsl(40,62%,66%)', - warning: 'hsl(20,79%,63%)', - danger: 'hsl(359,68%,64%)', + primary: "hsl(277,59%,68%)", + secondary: "hsl(228,39%,72%)", + info: "hsl(222,74%,67%)", + success: "hsl(96,44%,61%)", + notice: "hsl(40,62%,66%)", + warning: "hsl(20,79%,63%)", + danger: "hsl(359,68%,64%)", }, }, }; export const catppuccinMacchiato: Theme = { - id: 'catppuccin-macchiato', - label: 'Catppuccin Macchiato', + id: "catppuccin-macchiato", + label: "Catppuccin Macchiato", dark: true, base: { - surface: 'hsl(233,23%,15%)', - text: 'hsl(227,68%,88%)', - textSubtle: 'hsl(227,27%,72%)', - textSubtlest: 'hsl(228,15%,57%)', - primary: 'hsl(267,83%,80%)', - secondary: 'hsl(228,39%,80%)', - info: 'hsl(220,83%,75%)', - success: 'hsl(105,48%,72%)', - notice: 'hsl(40,70%,78%)', - warning: 'hsl(21,86%,73%)', - danger: 'hsl(351,74%,73%)', + surface: "hsl(233,23%,15%)", + text: "hsl(227,68%,88%)", + textSubtle: "hsl(227,27%,72%)", + textSubtlest: "hsl(228,15%,57%)", + primary: "hsl(267,83%,80%)", + secondary: "hsl(228,39%,80%)", + info: "hsl(220,83%,75%)", + success: "hsl(105,48%,72%)", + notice: "hsl(40,70%,78%)", + warning: "hsl(21,86%,73%)", + danger: "hsl(351,74%,73%)", }, components: { dialog: { - surface: 'hsl(240,21%,12%)', + surface: "hsl(240,21%,12%)", }, sidebar: { - surface: 'hsl(232,23%,18%)', - border: 'hsl(231,23%,22%)', + surface: "hsl(232,23%,18%)", + border: "hsl(231,23%,22%)", }, appHeader: { - surface: 'hsl(236,23%,12%)', - border: 'hsl(236,23%,21%)', + surface: "hsl(236,23%,12%)", + border: "hsl(236,23%,21%)", }, responsePane: { - surface: 'hsl(232,23%,18%)', - border: 'hsl(231,23%,22%)', + surface: "hsl(232,23%,18%)", + border: "hsl(231,23%,22%)", }, button: { - primary: 'hsl(267,82%,72%)', - secondary: 'hsl(228,39%,72%)', - info: 'hsl(220,83%,68%)', - success: 'hsl(105,48%,65%)', - notice: 'hsl(40,70%,70%)', - warning: 'hsl(21,86%,66%)', - danger: 'hsl(351,74%,66%)', + primary: "hsl(267,82%,72%)", + secondary: "hsl(228,39%,72%)", + info: "hsl(220,83%,68%)", + success: "hsl(105,48%,65%)", + notice: "hsl(40,70%,70%)", + warning: "hsl(21,86%,66%)", + danger: "hsl(351,74%,66%)", }, }, }; export const catppuccinMocha: Theme = { - id: 'catppuccin-mocha', - label: 'Catppuccin Mocha', + id: "catppuccin-mocha", + label: "Catppuccin Mocha", dark: true, base: { - surface: 'hsl(240,21%,12%)', - text: 'hsl(226,64%,88%)', - textSubtle: 'hsl(228,24%,72%)', - textSubtlest: 'hsl(230,13%,55%)', - primary: 'hsl(267,83%,80%)', - secondary: 'hsl(227,35%,80%)', - info: 'hsl(217,92%,76%)', - success: 'hsl(115,54%,76%)', - notice: 'hsl(41,86%,83%)', - warning: 'hsl(23,92%,75%)', - danger: 'hsl(343,81%,75%)', + surface: "hsl(240,21%,12%)", + text: "hsl(226,64%,88%)", + textSubtle: "hsl(228,24%,72%)", + textSubtlest: "hsl(230,13%,55%)", + primary: "hsl(267,83%,80%)", + secondary: "hsl(227,35%,80%)", + info: "hsl(217,92%,76%)", + success: "hsl(115,54%,76%)", + notice: "hsl(41,86%,83%)", + warning: "hsl(23,92%,75%)", + danger: "hsl(343,81%,75%)", }, components: { dialog: { - surface: 'hsl(240,21%,12%)', + surface: "hsl(240,21%,12%)", }, sidebar: { - surface: 'hsl(240,21%,15%)', - border: 'hsl(240,21%,19%)', + surface: "hsl(240,21%,15%)", + border: "hsl(240,21%,19%)", }, appHeader: { - surface: 'hsl(240,23%,9%)', - border: 'hsl(240,22%,18%)', + surface: "hsl(240,23%,9%)", + border: "hsl(240,22%,18%)", }, responsePane: { - surface: 'hsl(240,21%,15%)', - border: 'hsl(240,21%,19%)', + surface: "hsl(240,21%,15%)", + border: "hsl(240,21%,19%)", }, button: { - primary: 'hsl(267,67%,65%)', - secondary: 'hsl(227,28%,64%)', - info: 'hsl(217,74%,61%)', - success: 'hsl(115,43%,61%)', - notice: 'hsl(41,69%,66%)', - warning: 'hsl(23,74%,60%)', - danger: 'hsl(343,65%,60%)', + primary: "hsl(267,67%,65%)", + secondary: "hsl(227,28%,64%)", + info: "hsl(217,74%,61%)", + success: "hsl(115,43%,61%)", + notice: "hsl(41,69%,66%)", + warning: "hsl(23,74%,60%)", + danger: "hsl(343,65%,60%)", }, }, }; export const catppuccinLatte: Theme = { - id: 'catppuccin-latte', - label: 'Catppuccin Latte', + id: "catppuccin-latte", + label: "Catppuccin Latte", dark: false, base: { - surface: 'hsl(220,23%,95%)', - text: 'hsl(234,16%,35%)', - textSubtle: 'hsl(233,10%,47%)', - textSubtlest: 'hsl(231,10%,59%)', - primary: 'hsl(266,85%,58%)', - secondary: 'hsl(233,10%,47%)', - info: 'hsl(231,97%,72%)', - success: 'hsl(183,74%,35%)', - notice: 'hsl(35,77%,49%)', - warning: 'hsl(22,99%,52%)', - danger: 'hsl(355,76%,59%)', + surface: "hsl(220,23%,95%)", + text: "hsl(234,16%,35%)", + textSubtle: "hsl(233,10%,47%)", + textSubtlest: "hsl(231,10%,59%)", + primary: "hsl(266,85%,58%)", + secondary: "hsl(233,10%,47%)", + info: "hsl(231,97%,72%)", + success: "hsl(183,74%,35%)", + notice: "hsl(35,77%,49%)", + warning: "hsl(22,99%,52%)", + danger: "hsl(355,76%,59%)", }, components: { sidebar: { - surface: 'hsl(220,22%,92%)', - border: 'hsl(220,22%,87%)', + surface: "hsl(220,22%,92%)", + border: "hsl(220,22%,87%)", }, appHeader: { - surface: 'hsl(220,21%,89%)', - border: 'hsl(220,22%,87%)', + surface: "hsl(220,21%,89%)", + border: "hsl(220,22%,87%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/cobalt2.ts b/plugins/themes-yaak/src/themes/cobalt2.ts index 80d11f4b..efabf656 100644 --- a/plugins/themes-yaak/src/themes/cobalt2.ts +++ b/plugins/themes-yaak/src/themes/cobalt2.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const cobalt2: Theme = { - id: 'cobalt2', - label: 'Cobalt2', + id: "cobalt2", + label: "Cobalt2", dark: true, base: { - surface: '#193549', - surfaceHighlight: '#1f4662', - text: '#d2e1f1', - textSubtle: '#709ac8', - textSubtlest: '#55749e', - primary: '#ffc600', - secondary: '#819fc3', - info: '#0088FF', - success: '#3AD900', - notice: '#FFEE80', - warning: '#FF9D00', - danger: '#FF628C', + surface: "#193549", + surfaceHighlight: "#1f4662", + text: "#d2e1f1", + textSubtle: "#709ac8", + textSubtlest: "#55749e", + primary: "#ffc600", + secondary: "#819fc3", + info: "#0088FF", + success: "#3AD900", + notice: "#FFEE80", + warning: "#FF9D00", + danger: "#FF628C", }, components: { sidebar: { - surface: '#13283a', - border: '#102332', + surface: "#13283a", + border: "#102332", }, input: { - border: '#1f4561', + border: "#1f4561", }, appHeader: { - surface: '#13283a', - border: '#112636', + surface: "#13283a", + border: "#112636", }, responsePane: { - surface: '#13283a', - border: '#112636', + surface: "#13283a", + border: "#112636", }, button: { - primary: '#ffc600', - secondary: '#709ac8', - info: '#0088FF', - success: '#3AD900', - notice: '#ecdc6a', - warning: '#FF9D00', - danger: '#FF628C', + primary: "#ffc600", + secondary: "#709ac8", + info: "#0088FF", + success: "#3AD900", + notice: "#ecdc6a", + warning: "#FF9D00", + danger: "#FF628C", }, }, }; diff --git a/plugins/themes-yaak/src/themes/dracula.ts b/plugins/themes-yaak/src/themes/dracula.ts index c5575b44..8f3693a1 100644 --- a/plugins/themes-yaak/src/themes/dracula.ts +++ b/plugins/themes-yaak/src/themes/dracula.ts @@ -1,29 +1,29 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const dracula: Theme = { - id: 'dracula', - label: 'Dracula', + id: "dracula", + label: "Dracula", dark: true, base: { - surface: 'hsl(231,15%,18%)', - surfaceHighlight: 'hsl(230,15%,24%)', - text: 'hsl(60,30%,96%)', - textSubtle: 'hsl(232,14%,65%)', - textSubtlest: 'hsl(232,14%,50%)', - primary: 'hsl(265,89%,78%)', - secondary: 'hsl(225,27%,51%)', - info: 'hsl(191,97%,77%)', - success: 'hsl(135,94%,65%)', - notice: 'hsl(65,92%,76%)', - warning: 'hsl(31,100%,71%)', - danger: 'hsl(0,100%,67%)', + surface: "hsl(231,15%,18%)", + surfaceHighlight: "hsl(230,15%,24%)", + text: "hsl(60,30%,96%)", + textSubtle: "hsl(232,14%,65%)", + textSubtlest: "hsl(232,14%,50%)", + primary: "hsl(265,89%,78%)", + secondary: "hsl(225,27%,51%)", + info: "hsl(191,97%,77%)", + success: "hsl(135,94%,65%)", + notice: "hsl(65,92%,76%)", + warning: "hsl(31,100%,71%)", + danger: "hsl(0,100%,67%)", }, components: { sidebar: { - backdrop: 'hsl(230,15%,24%)', + backdrop: "hsl(230,15%,24%)", }, appHeader: { - backdrop: 'hsl(235,14%,15%)', + backdrop: "hsl(235,14%,15%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/everforest.ts b/plugins/themes-yaak/src/themes/everforest.ts index 41b6f574..6a0c45a9 100644 --- a/plugins/themes-yaak/src/themes/everforest.ts +++ b/plugins/themes-yaak/src/themes/everforest.ts @@ -1,77 +1,77 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const everforestDark: Theme = { - id: 'everforest-dark', - label: 'Everforest Dark', + id: "everforest-dark", + label: "Everforest Dark", dark: true, base: { - surface: 'hsl(150, 8%, 18%)', - surfaceHighlight: 'hsl(150, 7%, 22%)', - text: 'hsl(45, 30%, 78%)', - textSubtle: 'hsl(145, 8%, 55%)', - textSubtlest: 'hsl(145, 6%, 42%)', - primary: 'hsl(142, 35%, 60%)', - secondary: 'hsl(145, 8%, 55%)', - info: 'hsl(200, 35%, 65%)', - success: 'hsl(142, 35%, 60%)', - notice: 'hsl(46, 55%, 68%)', - warning: 'hsl(24, 55%, 65%)', - danger: 'hsl(358, 50%, 68%)', + surface: "hsl(150, 8%, 18%)", + surfaceHighlight: "hsl(150, 7%, 22%)", + text: "hsl(45, 30%, 78%)", + textSubtle: "hsl(145, 8%, 55%)", + textSubtlest: "hsl(145, 6%, 42%)", + primary: "hsl(142, 35%, 60%)", + secondary: "hsl(145, 8%, 55%)", + info: "hsl(200, 35%, 65%)", + success: "hsl(142, 35%, 60%)", + notice: "hsl(46, 55%, 68%)", + warning: "hsl(24, 55%, 65%)", + danger: "hsl(358, 50%, 68%)", }, components: { dialog: { - surface: 'hsl(150, 8%, 15%)', + surface: "hsl(150, 8%, 15%)", }, sidebar: { - surface: 'hsl(150, 7%, 16%)', - border: 'hsl(150, 6%, 20%)', + surface: "hsl(150, 7%, 16%)", + border: "hsl(150, 6%, 20%)", }, appHeader: { - surface: 'hsl(150, 8%, 14%)', - border: 'hsl(150, 6%, 18%)', + surface: "hsl(150, 8%, 14%)", + border: "hsl(150, 6%, 18%)", }, responsePane: { - surface: 'hsl(150, 7%, 16%)', - border: 'hsl(150, 6%, 20%)', + surface: "hsl(150, 7%, 16%)", + border: "hsl(150, 6%, 20%)", }, button: { - primary: 'hsl(142, 35%, 53%)', - secondary: 'hsl(145, 8%, 48%)', - info: 'hsl(200, 35%, 58%)', - success: 'hsl(142, 35%, 53%)', - notice: 'hsl(46, 55%, 61%)', - warning: 'hsl(24, 55%, 58%)', - danger: 'hsl(358, 50%, 61%)', + primary: "hsl(142, 35%, 53%)", + secondary: "hsl(145, 8%, 48%)", + info: "hsl(200, 35%, 58%)", + success: "hsl(142, 35%, 53%)", + notice: "hsl(46, 55%, 61%)", + warning: "hsl(24, 55%, 58%)", + danger: "hsl(358, 50%, 61%)", }, }, }; export const everforestLight: Theme = { - id: 'everforest-light', - label: 'Everforest Light', + id: "everforest-light", + label: "Everforest Light", dark: false, base: { - surface: 'hsl(40, 32%, 93%)', - surfaceHighlight: 'hsl(40, 28%, 89%)', - text: 'hsl(135, 8%, 35%)', - textSubtle: 'hsl(135, 6%, 45%)', - textSubtlest: 'hsl(135, 4%, 55%)', - primary: 'hsl(128, 30%, 45%)', - secondary: 'hsl(135, 6%, 45%)', - info: 'hsl(200, 35%, 45%)', - success: 'hsl(128, 30%, 45%)', - notice: 'hsl(45, 70%, 40%)', - warning: 'hsl(22, 60%, 48%)', - danger: 'hsl(355, 55%, 50%)', + surface: "hsl(40, 32%, 93%)", + surfaceHighlight: "hsl(40, 28%, 89%)", + text: "hsl(135, 8%, 35%)", + textSubtle: "hsl(135, 6%, 45%)", + textSubtlest: "hsl(135, 4%, 55%)", + primary: "hsl(128, 30%, 45%)", + secondary: "hsl(135, 6%, 45%)", + info: "hsl(200, 35%, 45%)", + success: "hsl(128, 30%, 45%)", + notice: "hsl(45, 70%, 40%)", + warning: "hsl(22, 60%, 48%)", + danger: "hsl(355, 55%, 50%)", }, components: { sidebar: { - surface: 'hsl(40, 30%, 91%)', - border: 'hsl(40, 25%, 86%)', + surface: "hsl(40, 30%, 91%)", + border: "hsl(40, 25%, 86%)", }, appHeader: { - surface: 'hsl(40, 30%, 89%)', - border: 'hsl(40, 25%, 84%)', + surface: "hsl(40, 30%, 89%)", + border: "hsl(40, 25%, 84%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/fleet.ts b/plugins/themes-yaak/src/themes/fleet.ts index 422b958e..fd63ae5f 100644 --- a/plugins/themes-yaak/src/themes/fleet.ts +++ b/plugins/themes-yaak/src/themes/fleet.ts @@ -1,173 +1,173 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const fleetLight: Theme = { - id: 'fleet-light', - label: 'Fleet Light', + id: "fleet-light", + label: "Fleet Light", dark: false, base: { - surface: '#FFFFFF', - surfaceHighlight: '#F8F8F9', - surfaceActive: '#EEEFF0', - border: '#18191B33', - text: '#090909', - textSubtle: '#6E747B', - textSubtlest: '#898E94', - primary: '#1D61BA', - secondary: '#6E747B', - info: '#4B8DEC', - success: '#169068', - notice: '#B07203', - warning: '#B07203', - danger: '#E1465E', + surface: "#FFFFFF", + surfaceHighlight: "#F8F8F9", + surfaceActive: "#EEEFF0", + border: "#18191B33", + text: "#090909", + textSubtle: "#6E747B", + textSubtlest: "#898E94", + primary: "#1D61BA", + secondary: "#6E747B", + info: "#4B8DEC", + success: "#169068", + notice: "#B07203", + warning: "#B07203", + danger: "#E1465E", }, components: { sidebar: { - surface: '#EEEFF0', - border: '#18191B33', + surface: "#EEEFF0", + border: "#18191B33", }, appHeader: { - surface: '#EEEFF0', - border: '#18191B33', + surface: "#EEEFF0", + border: "#18191B33", }, responsePane: { - surface: '#FFFFFF', - border: '#18191B33', + surface: "#FFFFFF", + border: "#18191B33", }, dialog: { - surface: '#FFFFFF', - border: '#18191B33', + surface: "#FFFFFF", + border: "#18191B33", }, button: { - surface: '#F8F8F9', - text: '#090909', - primary: '#2A7DEB', - secondary: '#6E747B', - info: '#4B8DEC', - success: '#169068', - notice: '#B07203', - warning: '#B07203', - danger: '#E1465E', + surface: "#F8F8F9", + text: "#090909", + primary: "#2A7DEB", + secondary: "#6E747B", + info: "#4B8DEC", + success: "#169068", + notice: "#B07203", + warning: "#B07203", + danger: "#E1465E", }, editor: { - primary: '#5511BF', - secondary: '#A31D8D', - info: '#14646E', - success: '#086E14', - notice: '#616605', - warning: '#747576', - danger: '#1749BD', + primary: "#5511BF", + secondary: "#A31D8D", + info: "#14646E", + success: "#086E14", + notice: "#616605", + warning: "#747576", + danger: "#1749BD", }, }, }; export const fleetDarkPurple: Theme = { - id: 'fleet-dark-purple', - label: 'Fleet Dark Purple', + id: "fleet-dark-purple", + label: "Fleet Dark Purple", dark: true, base: { - surface: '#1C1827', - surfaceHighlight: '#262136', - surfaceActive: '#3E3852', - border: '#3E3852', - text: '#E0E1E4', - textSubtle: '#E0E1E480', - textSubtlest: '#E0E1E44D', - primary: '#B174D9', - secondary: '#E0E1E480', - info: '#4B8DEC', - success: '#169068', - notice: '#B07203', - warning: '#B07203', - danger: '#E1465E', + surface: "#1C1827", + surfaceHighlight: "#262136", + surfaceActive: "#3E3852", + border: "#3E3852", + text: "#E0E1E4", + textSubtle: "#E0E1E480", + textSubtlest: "#E0E1E44D", + primary: "#B174D9", + secondary: "#E0E1E480", + info: "#4B8DEC", + success: "#169068", + notice: "#B07203", + warning: "#B07203", + danger: "#E1465E", }, components: { appHeader: { - surface: '#13101B', - border: '#3E3852', + surface: "#13101B", + border: "#3E3852", }, responsePane: { - surface: '#1C1827', - border: '#3E3852', + surface: "#1C1827", + border: "#3E3852", }, dialog: { - surface: '#262136', - border: '#3E3852', + surface: "#262136", + border: "#3E3852", }, button: { - surface: '#262136', - text: '#E0E1E4', - primary: '#A660D4', - secondary: '#E0E1E480', - info: '#4B8DEC', - success: '#169068', - notice: '#B07203', - warning: '#B07203', - danger: '#E1465E', + surface: "#262136", + text: "#E0E1E4", + primary: "#A660D4", + secondary: "#E0E1E480", + info: "#4B8DEC", + success: "#169068", + notice: "#B07203", + warning: "#B07203", + danger: "#E1465E", }, editor: { - primary: '#C7A65D', - secondary: '#93A6F5', - info: '#E09B70', - success: '#62A362', - notice: '#85A658', - warning: '#7e7d86', - danger: '#4DACF0', + primary: "#C7A65D", + secondary: "#93A6F5", + info: "#E09B70", + success: "#62A362", + notice: "#85A658", + warning: "#7e7d86", + danger: "#4DACF0", }, }, }; export const fleetDark: Theme = { - id: 'fleet-dark', - label: 'Fleet Dark', + id: "fleet-dark", + label: "Fleet Dark", dark: true, base: { - surface: '#18191B', - surfaceHighlight: '#252629', - surfaceActive: '#3E4147', - border: '#3E4147', - text: '#E0E1E4', - textSubtle: '#898E94', - textSubtlest: '#646B71', - primary: '#4B8DEC', - secondary: '#898E94', - info: '#4B8DEC', - success: '#169068', - notice: '#B07203', - warning: '#B07203', - danger: '#E1465E', + surface: "#18191B", + surfaceHighlight: "#252629", + surfaceActive: "#3E4147", + border: "#3E4147", + text: "#E0E1E4", + textSubtle: "#898E94", + textSubtlest: "#646B71", + primary: "#4B8DEC", + secondary: "#898E94", + info: "#4B8DEC", + success: "#169068", + notice: "#B07203", + warning: "#B07203", + danger: "#E1465E", }, components: { appHeader: { - surface: '#090909', - border: '#3E4147', + surface: "#090909", + border: "#3E4147", }, responsePane: { - surface: '#18191B', - border: '#3E4147', + surface: "#18191B", + border: "#3E4147", }, dialog: { - surface: '#252629', - border: '#3E4147', + surface: "#252629", + border: "#3E4147", }, button: { - surface: '#252629', - text: '#E0E1E4', - primary: '#2A7DEB', - secondary: '#898E94', - info: '#4B8DEC', - success: '#169068', - notice: '#B07203', - warning: '#B07203', - danger: '#E1465E', + surface: "#252629", + text: "#E0E1E4", + primary: "#2A7DEB", + secondary: "#898E94", + info: "#4B8DEC", + success: "#169068", + notice: "#B07203", + warning: "#B07203", + danger: "#E1465E", }, editor: { - primary: '#EBC88D', - secondary: '#AF9CFF', - info: '#82D2CE', - success: '#A8C5A0', - notice: '#C7A65D', - warning: '#909194', - danger: '#87C3FF', + primary: "#EBC88D", + secondary: "#AF9CFF", + info: "#82D2CE", + success: "#A8C5A0", + notice: "#C7A65D", + warning: "#909194", + danger: "#87C3FF", }, }, }; diff --git a/plugins/themes-yaak/src/themes/github-dimmed.ts b/plugins/themes-yaak/src/themes/github-dimmed.ts index 92a12a5a..0a1e25a2 100644 --- a/plugins/themes-yaak/src/themes/github-dimmed.ts +++ b/plugins/themes-yaak/src/themes/github-dimmed.ts @@ -1,46 +1,46 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const githubDarkDimmed: Theme = { - id: 'github-dark-dimmed', - label: 'GitHub Dark Dimmed', + id: "github-dark-dimmed", + label: "GitHub Dark Dimmed", dark: true, base: { - surface: 'hsl(215, 15%, 16%)', - surfaceHighlight: 'hsl(215, 13%, 20%)', - text: 'hsl(212, 15%, 78%)', - textSubtle: 'hsl(212, 10%, 55%)', - textSubtlest: 'hsl(212, 8%, 42%)', - primary: 'hsl(212, 80%, 65%)', - secondary: 'hsl(212, 10%, 55%)', - info: 'hsl(212, 80%, 65%)', - success: 'hsl(140, 50%, 50%)', - notice: 'hsl(42, 75%, 55%)', - warning: 'hsl(27, 80%, 55%)', - danger: 'hsl(355, 70%, 55%)', + surface: "hsl(215, 15%, 16%)", + surfaceHighlight: "hsl(215, 13%, 20%)", + text: "hsl(212, 15%, 78%)", + textSubtle: "hsl(212, 10%, 55%)", + textSubtlest: "hsl(212, 8%, 42%)", + primary: "hsl(212, 80%, 65%)", + secondary: "hsl(212, 10%, 55%)", + info: "hsl(212, 80%, 65%)", + success: "hsl(140, 50%, 50%)", + notice: "hsl(42, 75%, 55%)", + warning: "hsl(27, 80%, 55%)", + danger: "hsl(355, 70%, 55%)", }, components: { dialog: { - surface: 'hsl(215, 15%, 13%)', + surface: "hsl(215, 15%, 13%)", }, sidebar: { - surface: 'hsl(215, 14%, 14%)', - border: 'hsl(215, 12%, 19%)', + surface: "hsl(215, 14%, 14%)", + border: "hsl(215, 12%, 19%)", }, appHeader: { - surface: 'hsl(215, 15%, 12%)', - border: 'hsl(215, 12%, 17%)', + surface: "hsl(215, 15%, 12%)", + border: "hsl(215, 12%, 17%)", }, responsePane: { - surface: 'hsl(215, 14%, 14%)', - border: 'hsl(215, 12%, 19%)', + surface: "hsl(215, 14%, 14%)", + border: "hsl(215, 12%, 19%)", }, button: { - primary: 'hsl(212, 80%, 58%)', - info: 'hsl(212, 80%, 58%)', - success: 'hsl(140, 50%, 45%)', - notice: 'hsl(42, 75%, 48%)', - warning: 'hsl(27, 80%, 48%)', - danger: 'hsl(355, 70%, 48%)', + primary: "hsl(212, 80%, 58%)", + info: "hsl(212, 80%, 58%)", + success: "hsl(140, 50%, 45%)", + notice: "hsl(42, 75%, 48%)", + warning: "hsl(27, 80%, 48%)", + danger: "hsl(355, 70%, 48%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/github.ts b/plugins/themes-yaak/src/themes/github.ts index 93cdf811..59fd041f 100644 --- a/plugins/themes-yaak/src/themes/github.ts +++ b/plugins/themes-yaak/src/themes/github.ts @@ -1,55 +1,55 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const githubDark: Theme = { - id: 'github-dark', - label: 'GitHub', + id: "github-dark", + label: "GitHub", dark: true, base: { - surface: 'hsl(213,30%,7%)', - surfaceHighlight: 'hsl(213,16%,13%)', - text: 'hsl(212,27%,89%)', - textSubtle: 'hsl(212,9%,57%)', - textSubtlest: 'hsl(217,8%,45%)', - border: 'hsl(215,21%,11%)', - primary: 'hsl(262,78%,74%)', - secondary: 'hsl(217,8%,50%)', - info: 'hsl(215,84%,64%)', - success: 'hsl(129,48%,52%)', - notice: 'hsl(39,71%,58%)', - warning: 'hsl(22,83%,60%)', - danger: 'hsl(3,83%,65%)', + surface: "hsl(213,30%,7%)", + surfaceHighlight: "hsl(213,16%,13%)", + text: "hsl(212,27%,89%)", + textSubtle: "hsl(212,9%,57%)", + textSubtlest: "hsl(217,8%,45%)", + border: "hsl(215,21%,11%)", + primary: "hsl(262,78%,74%)", + secondary: "hsl(217,8%,50%)", + info: "hsl(215,84%,64%)", + success: "hsl(129,48%,52%)", + notice: "hsl(39,71%,58%)", + warning: "hsl(22,83%,60%)", + danger: "hsl(3,83%,65%)", }, components: { button: { - primary: 'hsl(262,79%,71%)', - secondary: 'hsl(217,8%,45%)', - info: 'hsl(215,84%,60%)', - success: 'hsl(129,48%,47%)', - notice: 'hsl(39,71%,53%)', - warning: 'hsl(22,83%,56%)', - danger: 'hsl(3,83%,61%)', + primary: "hsl(262,79%,71%)", + secondary: "hsl(217,8%,45%)", + info: "hsl(215,84%,60%)", + success: "hsl(129,48%,47%)", + notice: "hsl(39,71%,53%)", + warning: "hsl(22,83%,56%)", + danger: "hsl(3,83%,61%)", }, }, }; export const githubLight: Theme = { - id: 'github-light', - label: 'GitHub', + id: "github-light", + label: "GitHub", dark: false, base: { - surface: 'hsl(0,0%,100%)', - surfaceHighlight: 'hsl(210,29%,94%)', - text: 'hsl(213,13%,14%)', - textSubtle: 'hsl(212,9%,43%)', - textSubtlest: 'hsl(203,8%,55%)', - border: 'hsl(210,15%,92%)', - borderSubtle: 'hsl(210,15%,92%)', - primary: 'hsl(261,69%,59%)', - secondary: 'hsl(212,8%,47%)', - info: 'hsl(212,92%,48%)', - success: 'hsl(137,66%,32%)', - notice: 'hsl(40,100%,40%)', - warning: 'hsl(24,100%,44%)', - danger: 'hsl(356,71%,48%)', + surface: "hsl(0,0%,100%)", + surfaceHighlight: "hsl(210,29%,94%)", + text: "hsl(213,13%,14%)", + textSubtle: "hsl(212,9%,43%)", + textSubtlest: "hsl(203,8%,55%)", + border: "hsl(210,15%,92%)", + borderSubtle: "hsl(210,15%,92%)", + primary: "hsl(261,69%,59%)", + secondary: "hsl(212,8%,47%)", + info: "hsl(212,92%,48%)", + success: "hsl(137,66%,32%)", + notice: "hsl(40,100%,40%)", + warning: "hsl(24,100%,44%)", + danger: "hsl(356,71%,48%)", }, }; diff --git a/plugins/themes-yaak/src/themes/gruvbox.ts b/plugins/themes-yaak/src/themes/gruvbox.ts index b5074a24..ad6d3b9c 100644 --- a/plugins/themes-yaak/src/themes/gruvbox.ts +++ b/plugins/themes-yaak/src/themes/gruvbox.ts @@ -1,21 +1,21 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const gruvbox: Theme = { - id: 'gruvbox', - label: 'Gruvbox', + id: "gruvbox", + label: "Gruvbox", dark: true, base: { - surface: 'hsl(0,0%,16%)', - surfaceHighlight: 'hsl(20,3%,19%)', - text: 'hsl(53,74%,91%)', - textSubtle: 'hsl(39,24%,66%)', - textSubtlest: 'hsl(30,12%,51%)', - primary: 'hsl(344,47%,68%)', - secondary: 'hsl(157,16%,58%)', - info: 'hsl(104,35%,62%)', - success: 'hsl(61,66%,44%)', - notice: 'hsl(42,95%,58%)', - warning: 'hsl(27,99%,55%)', - danger: 'hsl(6,96%,59%)', + surface: "hsl(0,0%,16%)", + surfaceHighlight: "hsl(20,3%,19%)", + text: "hsl(53,74%,91%)", + textSubtle: "hsl(39,24%,66%)", + textSubtlest: "hsl(30,12%,51%)", + primary: "hsl(344,47%,68%)", + secondary: "hsl(157,16%,58%)", + info: "hsl(104,35%,62%)", + success: "hsl(61,66%,44%)", + notice: "hsl(42,95%,58%)", + warning: "hsl(27,99%,55%)", + danger: "hsl(6,96%,59%)", }, }; diff --git a/plugins/themes-yaak/src/themes/high-contrast.ts b/plugins/themes-yaak/src/themes/high-contrast.ts index 1b97d81f..2d948241 100644 --- a/plugins/themes-yaak/src/themes/high-contrast.ts +++ b/plugins/themes-yaak/src/themes/high-contrast.ts @@ -1,46 +1,46 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const highContrast: Theme = { - id: 'high-contrast', - label: 'High Contrast Light', + id: "high-contrast", + label: "High Contrast Light", dark: false, base: { - surface: 'white', - surfaceHighlight: 'hsl(218,24%,93%)', - text: 'black', - textSubtle: 'hsl(217,24%,40%)', - textSubtlest: 'hsl(217,24%,40%)', - border: 'hsl(217,22%,50%)', - borderSubtle: 'hsl(217,22%,60%)', - primary: 'hsl(267,67%,47%)', - secondary: 'hsl(218,18%,53%)', - info: 'hsl(206,100%,36%)', - success: 'hsl(155,100%,26%)', - notice: 'hsl(45,100%,31%)', - warning: 'hsl(30,99%,34%)', - danger: 'hsl(334,100%,35%)', + surface: "white", + surfaceHighlight: "hsl(218,24%,93%)", + text: "black", + textSubtle: "hsl(217,24%,40%)", + textSubtlest: "hsl(217,24%,40%)", + border: "hsl(217,22%,50%)", + borderSubtle: "hsl(217,22%,60%)", + primary: "hsl(267,67%,47%)", + secondary: "hsl(218,18%,53%)", + info: "hsl(206,100%,36%)", + success: "hsl(155,100%,26%)", + notice: "hsl(45,100%,31%)", + warning: "hsl(30,99%,34%)", + danger: "hsl(334,100%,35%)", }, }; export const highContrastDark: Theme = { - id: 'high-contrast-dark', - label: 'High Contrast Dark', + id: "high-contrast-dark", + label: "High Contrast Dark", dark: true, base: { - surface: 'hsl(0,0%,0%)', - surfaceHighlight: 'hsl(0,0%,20%)', - text: 'hsl(0,0%,100%)', - textSubtle: 'hsl(0,0%,90%)', - textSubtlest: 'hsl(0,0%,80%)', - selection: 'hsl(276,100%,30%)', - surfaceActive: 'hsl(276,100%,30%)', - border: 'hsl(0,0%,60%)', - primary: 'hsl(266,100%,85%)', - secondary: 'hsl(242,20%,72%)', - info: 'hsl(208,100%,83%)', - success: 'hsl(150,100%,63%)', - notice: 'hsl(49,100%,77%)', - warning: 'hsl(28,100%,73%)', - danger: 'hsl(343,100%,79%)', + surface: "hsl(0,0%,0%)", + surfaceHighlight: "hsl(0,0%,20%)", + text: "hsl(0,0%,100%)", + textSubtle: "hsl(0,0%,90%)", + textSubtlest: "hsl(0,0%,80%)", + selection: "hsl(276,100%,30%)", + surfaceActive: "hsl(276,100%,30%)", + border: "hsl(0,0%,60%)", + primary: "hsl(266,100%,85%)", + secondary: "hsl(242,20%,72%)", + info: "hsl(208,100%,83%)", + success: "hsl(150,100%,63%)", + notice: "hsl(49,100%,77%)", + warning: "hsl(28,100%,73%)", + danger: "hsl(343,100%,79%)", }, }; diff --git a/plugins/themes-yaak/src/themes/horizon.ts b/plugins/themes-yaak/src/themes/horizon.ts index 9f153f3e..2d2ec630 100644 --- a/plugins/themes-yaak/src/themes/horizon.ts +++ b/plugins/themes-yaak/src/themes/horizon.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const horizon: Theme = { - id: 'horizon', - label: 'Horizon', + id: "horizon", + label: "Horizon", dark: true, base: { - surface: 'hsl(220, 16%, 13%)', - surfaceHighlight: 'hsl(220, 14%, 18%)', - text: 'hsl(220, 15%, 85%)', - textSubtle: 'hsl(220, 10%, 55%)', - textSubtlest: 'hsl(220, 8%, 45%)', - primary: 'hsl(5, 85%, 68%)', - secondary: 'hsl(220, 10%, 55%)', - info: 'hsl(217, 70%, 68%)', - success: 'hsl(92, 50%, 60%)', - notice: 'hsl(34, 92%, 70%)', - warning: 'hsl(20, 90%, 65%)', - danger: 'hsl(355, 80%, 65%)', + surface: "hsl(220, 16%, 13%)", + surfaceHighlight: "hsl(220, 14%, 18%)", + text: "hsl(220, 15%, 85%)", + textSubtle: "hsl(220, 10%, 55%)", + textSubtlest: "hsl(220, 8%, 45%)", + primary: "hsl(5, 85%, 68%)", + secondary: "hsl(220, 10%, 55%)", + info: "hsl(217, 70%, 68%)", + success: "hsl(92, 50%, 60%)", + notice: "hsl(34, 92%, 70%)", + warning: "hsl(20, 90%, 65%)", + danger: "hsl(355, 80%, 65%)", }, components: { dialog: { - surface: 'hsl(220, 16%, 10%)', + surface: "hsl(220, 16%, 10%)", }, sidebar: { - surface: 'hsl(220, 14%, 15%)', - border: 'hsl(220, 14%, 19%)', + surface: "hsl(220, 14%, 15%)", + border: "hsl(220, 14%, 19%)", }, appHeader: { - surface: 'hsl(220, 16%, 11%)', - border: 'hsl(220, 14%, 17%)', + surface: "hsl(220, 16%, 11%)", + border: "hsl(220, 14%, 17%)", }, responsePane: { - surface: 'hsl(220, 14%, 15%)', - border: 'hsl(220, 14%, 19%)', + surface: "hsl(220, 14%, 15%)", + border: "hsl(220, 14%, 19%)", }, button: { - primary: 'hsl(5, 85%, 61%)', - secondary: 'hsl(224,8%,53%)', - info: 'hsl(217, 70%, 61%)', - success: 'hsl(92, 50%, 53%)', - notice: 'hsl(34, 92%, 63%)', - warning: 'hsl(20, 90%, 58%)', - danger: 'hsl(355, 80%, 58%)', + primary: "hsl(5, 85%, 61%)", + secondary: "hsl(224,8%,53%)", + info: "hsl(217, 70%, 61%)", + success: "hsl(92, 50%, 53%)", + notice: "hsl(34, 92%, 63%)", + warning: "hsl(20, 90%, 58%)", + danger: "hsl(355, 80%, 58%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/hotdog-stand.ts b/plugins/themes-yaak/src/themes/hotdog-stand.ts index 9729bc3b..faa05b6b 100644 --- a/plugins/themes-yaak/src/themes/hotdog-stand.ts +++ b/plugins/themes-yaak/src/themes/hotdog-stand.ts @@ -1,58 +1,58 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const hotdogStand: Theme = { - id: 'hotdog-stand', - label: 'Hotdog Stand', + id: "hotdog-stand", + label: "Hotdog Stand", dark: true, base: { - surface: 'hsl(0,100%,50%)', - surfaceHighlight: 'hsl(0,0%,0%)', - text: 'hsl(0,0%,100%)', - textSubtle: 'hsl(0,0%,100%)', - textSubtlest: 'hsl(60,100%,50%)', - border: 'hsl(0,0%,0%)', - primary: 'hsl(60,100%,50%)', - secondary: 'hsl(60,100%,50%)', - info: 'hsl(60,100%,50%)', - success: 'hsl(60,100%,50%)', - notice: 'hsl(60,100%,50%)', - warning: 'hsl(60,100%,50%)', - danger: 'hsl(60,100%,50%)', + surface: "hsl(0,100%,50%)", + surfaceHighlight: "hsl(0,0%,0%)", + text: "hsl(0,0%,100%)", + textSubtle: "hsl(0,0%,100%)", + textSubtlest: "hsl(60,100%,50%)", + border: "hsl(0,0%,0%)", + primary: "hsl(60,100%,50%)", + secondary: "hsl(60,100%,50%)", + info: "hsl(60,100%,50%)", + success: "hsl(60,100%,50%)", + notice: "hsl(60,100%,50%)", + warning: "hsl(60,100%,50%)", + danger: "hsl(60,100%,50%)", }, components: { appHeader: { - surface: 'hsl(0,0%,0%)', - text: 'hsl(0,0%,100%)', - textSubtle: 'hsl(60,100%,50%)', - textSubtlest: 'hsl(0,100%,50%)', + surface: "hsl(0,0%,0%)", + text: "hsl(0,0%,100%)", + textSubtle: "hsl(60,100%,50%)", + textSubtlest: "hsl(0,100%,50%)", }, menu: { - surface: 'hsl(0,0%,0%)', - border: 'hsl(0,100%,50%)', - surfaceHighlight: 'hsl(0,100%,50%)', - text: 'hsl(0,0%,100%)', - textSubtle: 'hsl(60,100%,50%)', - textSubtlest: 'hsl(60,100%,50%)', + surface: "hsl(0,0%,0%)", + border: "hsl(0,100%,50%)", + surfaceHighlight: "hsl(0,100%,50%)", + text: "hsl(0,0%,100%)", + textSubtle: "hsl(60,100%,50%)", + textSubtlest: "hsl(60,100%,50%)", }, button: { - surface: 'hsl(0,0%,0%)', - text: 'hsl(0,0%,100%)', - primary: 'hsl(0,0%,0%)', - secondary: 'hsl(0,0%,100%)', - info: 'hsl(0,0%,0%)', - success: 'hsl(60,100%,50%)', - notice: 'hsl(60,100%,50%)', - warning: 'hsl(0,0%,0%)', - danger: 'hsl(0,100%,50%)', + surface: "hsl(0,0%,0%)", + text: "hsl(0,0%,100%)", + primary: "hsl(0,0%,0%)", + secondary: "hsl(0,0%,100%)", + info: "hsl(0,0%,0%)", + success: "hsl(60,100%,50%)", + notice: "hsl(60,100%,50%)", + warning: "hsl(0,0%,0%)", + danger: "hsl(0,100%,50%)", }, editor: { - primary: 'hsl(0,0%,100%)', - secondary: 'hsl(0,0%,100%)', - info: 'hsl(0,0%,100%)', - success: 'hsl(0,0%,100%)', - notice: 'hsl(60,100%,50%)', - warning: 'hsl(0,0%,100%)', - danger: 'hsl(0,0%,100%)', + primary: "hsl(0,0%,100%)", + secondary: "hsl(0,0%,100%)", + info: "hsl(0,0%,100%)", + success: "hsl(0,0%,100%)", + notice: "hsl(60,100%,50%)", + warning: "hsl(0,0%,100%)", + danger: "hsl(0,0%,100%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/material-darker.ts b/plugins/themes-yaak/src/themes/material-darker.ts index 4a138b59..3d566a89 100644 --- a/plugins/themes-yaak/src/themes/material-darker.ts +++ b/plugins/themes-yaak/src/themes/material-darker.ts @@ -1,39 +1,39 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const materialDarker: Theme = { - id: 'material-darker', - label: 'Material Darker', + id: "material-darker", + label: "Material Darker", dark: true, base: { - surface: 'hsl(0, 0%, 13%)', - surfaceHighlight: 'hsl(0, 0%, 18%)', - text: 'hsl(0, 0%, 93%)', - textSubtle: 'hsl(0, 0%, 65%)', - textSubtlest: 'hsl(0, 0%, 50%)', - primary: 'hsl(262, 100%, 75%)', - secondary: 'hsl(0, 0%, 60%)', - info: 'hsl(224, 100%, 75%)', - success: 'hsl(84, 60%, 73%)', - notice: 'hsl(43, 100%, 70%)', - warning: 'hsl(14, 85%, 70%)', - danger: 'hsl(1, 77%, 59%)', + surface: "hsl(0, 0%, 13%)", + surfaceHighlight: "hsl(0, 0%, 18%)", + text: "hsl(0, 0%, 93%)", + textSubtle: "hsl(0, 0%, 65%)", + textSubtlest: "hsl(0, 0%, 50%)", + primary: "hsl(262, 100%, 75%)", + secondary: "hsl(0, 0%, 60%)", + info: "hsl(224, 100%, 75%)", + success: "hsl(84, 60%, 73%)", + notice: "hsl(43, 100%, 70%)", + warning: "hsl(14, 85%, 70%)", + danger: "hsl(1, 77%, 59%)", }, components: { sidebar: { - surface: 'hsl(0, 0%, 11%)', - border: 'hsl(0, 0%, 16%)', + surface: "hsl(0, 0%, 11%)", + border: "hsl(0, 0%, 16%)", }, appHeader: { - surface: 'hsl(0, 0%, 9%)', - border: 'hsl(0, 0%, 14%)', + surface: "hsl(0, 0%, 9%)", + border: "hsl(0, 0%, 14%)", }, button: { - primary: 'hsl(262, 100%, 68%)', - info: 'hsl(224, 100%, 68%)', - success: 'hsl(84, 60%, 66%)', - notice: 'hsl(43, 100%, 63%)', - warning: 'hsl(14, 85%, 63%)', - danger: 'hsl(1, 77%, 52%)', + primary: "hsl(262, 100%, 68%)", + info: "hsl(224, 100%, 68%)", + success: "hsl(84, 60%, 66%)", + notice: "hsl(43, 100%, 63%)", + warning: "hsl(14, 85%, 63%)", + danger: "hsl(1, 77%, 52%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/material-ocean.ts b/plugins/themes-yaak/src/themes/material-ocean.ts index 9bad876a..dd4ee5ba 100644 --- a/plugins/themes-yaak/src/themes/material-ocean.ts +++ b/plugins/themes-yaak/src/themes/material-ocean.ts @@ -1,43 +1,43 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const materialOcean: Theme = { - id: 'material-ocean', - label: 'Material Ocean', + id: "material-ocean", + label: "Material Ocean", dark: true, base: { - surface: 'hsl(230, 25%, 14%)', - surfaceHighlight: 'hsl(230, 20%, 18%)', - text: 'hsl(220, 53%, 85%)', - textSubtle: 'hsl(228, 12%, 54%)', - textSubtlest: 'hsl(228, 12%, 42%)', - primary: 'hsl(262, 100%, 75%)', - secondary: 'hsl(228, 12%, 60%)', - info: 'hsl(224, 100%, 75%)', - success: 'hsl(84, 60%, 73%)', - notice: 'hsl(43, 100%, 70%)', - warning: 'hsl(14, 85%, 70%)', - danger: 'hsl(1, 77%, 59%)', + surface: "hsl(230, 25%, 14%)", + surfaceHighlight: "hsl(230, 20%, 18%)", + text: "hsl(220, 53%, 85%)", + textSubtle: "hsl(228, 12%, 54%)", + textSubtlest: "hsl(228, 12%, 42%)", + primary: "hsl(262, 100%, 75%)", + secondary: "hsl(228, 12%, 60%)", + info: "hsl(224, 100%, 75%)", + success: "hsl(84, 60%, 73%)", + notice: "hsl(43, 100%, 70%)", + warning: "hsl(14, 85%, 70%)", + danger: "hsl(1, 77%, 59%)", }, components: { sidebar: { - surface: 'hsl(230, 25%, 12%)', - border: 'hsl(230, 20%, 18%)', + surface: "hsl(230, 25%, 12%)", + border: "hsl(230, 20%, 18%)", }, appHeader: { - surface: 'hsl(230, 25%, 10%)', - border: 'hsl(230, 20%, 16%)', + surface: "hsl(230, 25%, 10%)", + border: "hsl(230, 20%, 16%)", }, responsePane: { - surface: 'hsl(230, 25%, 12%)', - border: 'hsl(230, 20%, 18%)', + surface: "hsl(230, 25%, 12%)", + border: "hsl(230, 20%, 18%)", }, button: { - primary: 'hsl(262, 100%, 68%)', - info: 'hsl(224, 100%, 68%)', - success: 'hsl(84, 60%, 66%)', - notice: 'hsl(43, 100%, 63%)', - warning: 'hsl(14, 85%, 63%)', - danger: 'hsl(1, 77%, 52%)', + primary: "hsl(262, 100%, 68%)", + info: "hsl(224, 100%, 68%)", + success: "hsl(84, 60%, 66%)", + notice: "hsl(43, 100%, 63%)", + warning: "hsl(14, 85%, 63%)", + danger: "hsl(1, 77%, 52%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/material-palenight.ts b/plugins/themes-yaak/src/themes/material-palenight.ts index fbd3bdad..d652f8f4 100644 --- a/plugins/themes-yaak/src/themes/material-palenight.ts +++ b/plugins/themes-yaak/src/themes/material-palenight.ts @@ -1,45 +1,45 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const materialPalenight: Theme = { - id: 'material-palenight', - label: 'Material Palenight', + id: "material-palenight", + label: "Material Palenight", dark: true, base: { - surface: '#292D3E', - surfaceHighlight: '#313850', - text: '#BFC7D5', - textSubtle: '#697098', - textSubtlest: '#4E5579', - primary: '#c792ea', - secondary: '#697098', - info: '#82AAFF', - success: '#C3E88D', - notice: '#FFCB6B', - warning: '#F78C6C', - danger: '#ff5572', + surface: "#292D3E", + surfaceHighlight: "#313850", + text: "#BFC7D5", + textSubtle: "#697098", + textSubtlest: "#4E5579", + primary: "#c792ea", + secondary: "#697098", + info: "#82AAFF", + success: "#C3E88D", + notice: "#FFCB6B", + warning: "#F78C6C", + danger: "#ff5572", }, components: { dialog: { - surface: '#232635', + surface: "#232635", }, sidebar: { - surface: '#292D3E', + surface: "#292D3E", }, appHeader: { - surface: '#282C3D', + surface: "#282C3D", }, responsePane: { - surface: '#313850', - border: '#3a3f58', + surface: "#313850", + border: "#3a3f58", }, button: { - primary: '#c792ea', - secondary: '#697098', - info: '#82AAFF', - success: '#C3E88D', - notice: '#FFCB6B', - warning: '#F78C6C', - danger: '#ff5572', + primary: "#c792ea", + secondary: "#697098", + info: "#82AAFF", + success: "#C3E88D", + notice: "#FFCB6B", + warning: "#F78C6C", + danger: "#ff5572", }, }, }; diff --git a/plugins/themes-yaak/src/themes/monokai-pro.ts b/plugins/themes-yaak/src/themes/monokai-pro.ts index 5656cef0..bbec71fe 100644 --- a/plugins/themes-yaak/src/themes/monokai-pro.ts +++ b/plugins/themes-yaak/src/themes/monokai-pro.ts @@ -1,217 +1,217 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const monokaiPro: Theme = { - id: 'monokai-pro', - label: 'Monokai Pro', + id: "monokai-pro", + label: "Monokai Pro", dark: true, base: { - surface: 'hsl(285,5%,17%)', - text: 'hsl(60,25%,98%)', - textSubtle: 'hsl(0,1%,75%)', - textSubtlest: 'hsl(300,0%,57%)', - primary: 'hsl(250,77%,78%)', - secondary: 'hsl(0,1%,75%)', - info: 'hsl(186,71%,69%)', - success: 'hsl(90,59%,66%)', - notice: 'hsl(45,100%,70%)', - warning: 'hsl(20,96%,70%)', - danger: 'hsl(345,100%,69%)', + surface: "hsl(285,5%,17%)", + text: "hsl(60,25%,98%)", + textSubtle: "hsl(0,1%,75%)", + textSubtlest: "hsl(300,0%,57%)", + primary: "hsl(250,77%,78%)", + secondary: "hsl(0,1%,75%)", + info: "hsl(186,71%,69%)", + success: "hsl(90,59%,66%)", + notice: "hsl(45,100%,70%)", + warning: "hsl(20,96%,70%)", + danger: "hsl(345,100%,69%)", }, components: { appHeader: { - surface: 'hsl(300,5%,13%)', - text: 'hsl(0,1%,75%)', - textSubtle: 'hsl(300,0%,57%)', - textSubtlest: 'hsl(300,1%,44%)', + surface: "hsl(300,5%,13%)", + text: "hsl(0,1%,75%)", + textSubtle: "hsl(300,0%,57%)", + textSubtlest: "hsl(300,1%,44%)", }, button: { - primary: 'hsl(250,77%,70%)', - secondary: 'hsl(0,1%,68%)', - info: 'hsl(186,71%,62%)', - success: 'hsl(90,59%,59%)', - notice: 'hsl(45,100%,63%)', - warning: 'hsl(20,96%,63%)', - danger: 'hsl(345,100%,62%)', + primary: "hsl(250,77%,70%)", + secondary: "hsl(0,1%,68%)", + info: "hsl(186,71%,62%)", + success: "hsl(90,59%,59%)", + notice: "hsl(45,100%,63%)", + warning: "hsl(20,96%,63%)", + danger: "hsl(345,100%,62%)", }, }, }; export const monokaiProClassic: Theme = { - id: 'monokai-pro-classic', - label: 'Monokai Pro Classic', + id: "monokai-pro-classic", + label: "Monokai Pro Classic", dark: true, base: { - surface: 'hsl(70,8%,15%)', - text: 'hsl(69,100%,97%)', - textSubtle: 'hsl(65,9%,73%)', - textSubtlest: 'hsl(66,4%,55%)', - primary: 'hsl(261,100%,75%)', - secondary: 'hsl(202,8%,72%)', - info: 'hsl(190,81%,67%)', - success: 'hsl(80,76%,53%)', - notice: 'hsl(54,70%,68%)', - warning: 'hsl(32,98%,56%)', - danger: 'hsl(338,95%,56%)', + surface: "hsl(70,8%,15%)", + text: "hsl(69,100%,97%)", + textSubtle: "hsl(65,9%,73%)", + textSubtlest: "hsl(66,4%,55%)", + primary: "hsl(261,100%,75%)", + secondary: "hsl(202,8%,72%)", + info: "hsl(190,81%,67%)", + success: "hsl(80,76%,53%)", + notice: "hsl(54,70%,68%)", + warning: "hsl(32,98%,56%)", + danger: "hsl(338,95%,56%)", }, components: { appHeader: { - surface: 'hsl(72,9%,11%)', - text: 'hsl(202,8%,72%)', - textSubtle: 'hsl(213,4%,48%)', - textSubtlest: 'hsl(223,6%,44%)', + surface: "hsl(72,9%,11%)", + text: "hsl(202,8%,72%)", + textSubtle: "hsl(213,4%,48%)", + textSubtlest: "hsl(223,6%,44%)", }, button: { - primary: 'hsl(261,100%,68%)', - secondary: 'hsl(202,8%,65%)', - info: 'hsl(190,81%,60%)', - success: 'hsl(80,76%,48%)', - notice: 'hsl(54,71%,61%)', - warning: 'hsl(32,98%,50%)', - danger: 'hsl(338,95%,50%)', + primary: "hsl(261,100%,68%)", + secondary: "hsl(202,8%,65%)", + info: "hsl(190,81%,60%)", + success: "hsl(80,76%,48%)", + notice: "hsl(54,71%,61%)", + warning: "hsl(32,98%,50%)", + danger: "hsl(338,95%,50%)", }, }, }; export const monokaiProMachine: Theme = { - id: 'monokai-pro-machine', - label: 'Monokai Pro Machine', + id: "monokai-pro-machine", + label: "Monokai Pro Machine", dark: true, base: { - surface: 'hsl(200,16%,18%)', - text: 'hsl(173,24%,93%)', - textSubtle: 'hsl(185,6%,57%)', - textSubtlest: 'hsl(189,6%,45%)', - primary: 'hsl(258,86%,80%)', - secondary: 'hsl(175,9%,75%)', - info: 'hsl(194,81%,72%)', - success: 'hsl(98,67%,69%)', - notice: 'hsl(52,100%,72%)', - warning: 'hsl(28,100%,72%)', - danger: 'hsl(353,100%,71%)', + surface: "hsl(200,16%,18%)", + text: "hsl(173,24%,93%)", + textSubtle: "hsl(185,6%,57%)", + textSubtlest: "hsl(189,6%,45%)", + primary: "hsl(258,86%,80%)", + secondary: "hsl(175,9%,75%)", + info: "hsl(194,81%,72%)", + success: "hsl(98,67%,69%)", + notice: "hsl(52,100%,72%)", + warning: "hsl(28,100%,72%)", + danger: "hsl(353,100%,71%)", }, components: { appHeader: { - surface: 'hsl(196,16%,14%)', - text: 'hsl(202,8%,72%)', - textSubtle: 'hsl(213,4%,48%)', - textSubtlest: 'hsl(223,6%,44%)', + surface: "hsl(196,16%,14%)", + text: "hsl(202,8%,72%)", + textSubtle: "hsl(213,4%,48%)", + textSubtlest: "hsl(223,6%,44%)", }, button: { - primary: 'hsl(258,86%,72%)', - secondary: 'hsl(175,9%,68%)', - info: 'hsl(194,80%,65%)', - success: 'hsl(98,67%,62%)', - notice: 'hsl(52,100%,65%)', - warning: 'hsl(28,100%,65%)', - danger: 'hsl(353,100%,64%)', + primary: "hsl(258,86%,72%)", + secondary: "hsl(175,9%,68%)", + info: "hsl(194,80%,65%)", + success: "hsl(98,67%,62%)", + notice: "hsl(52,100%,65%)", + warning: "hsl(28,100%,65%)", + danger: "hsl(353,100%,64%)", }, }, }; export const monokaiProOctagon: Theme = { - id: 'monokai-pro-octagon', - label: 'Monokai Pro Octagon', + id: "monokai-pro-octagon", + label: "Monokai Pro Octagon", dark: true, base: { - surface: 'hsl(233,18%,19%)', - text: 'hsl(173,24%,93%)', - textSubtle: 'hsl(202,8%,72%)', - textSubtlest: 'hsl(213,4%,48%)', - primary: 'hsl(292,30%,70%)', - secondary: 'hsl(202,8%,72%)', - info: 'hsl(155,37%,72%)', - success: 'hsl(75,60%,61%)', - notice: 'hsl(44,100%,71%)', - warning: 'hsl(23,100%,68%)', - danger: 'hsl(352,100%,70%)', + surface: "hsl(233,18%,19%)", + text: "hsl(173,24%,93%)", + textSubtle: "hsl(202,8%,72%)", + textSubtlest: "hsl(213,4%,48%)", + primary: "hsl(292,30%,70%)", + secondary: "hsl(202,8%,72%)", + info: "hsl(155,37%,72%)", + success: "hsl(75,60%,61%)", + notice: "hsl(44,100%,71%)", + warning: "hsl(23,100%,68%)", + danger: "hsl(352,100%,70%)", }, components: { appHeader: { - surface: 'hsl(235,18%,14%)', - text: 'hsl(202,8%,72%)', - textSubtle: 'hsl(213,4%,48%)', - textSubtlest: 'hsl(223,6%,44%)', + surface: "hsl(235,18%,14%)", + text: "hsl(202,8%,72%)", + textSubtle: "hsl(213,4%,48%)", + textSubtlest: "hsl(223,6%,44%)", }, button: { - primary: 'hsl(292,26%,63%)', - secondary: 'hsl(201,7%,65%)', - info: 'hsl(155,33%,65%)', - success: 'hsl(75,54%,55%)', - notice: 'hsl(44,90%,64%)', - warning: 'hsl(23,90%,61%)', - danger: 'hsl(352,90%,63%)', + primary: "hsl(292,26%,63%)", + secondary: "hsl(201,7%,65%)", + info: "hsl(155,33%,65%)", + success: "hsl(75,54%,55%)", + notice: "hsl(44,90%,64%)", + warning: "hsl(23,90%,61%)", + danger: "hsl(352,90%,63%)", }, }, }; export const monokaiProRistretto: Theme = { - id: 'monokai-pro-ristretto', - label: 'Monokai Pro Ristretto', + id: "monokai-pro-ristretto", + label: "Monokai Pro Ristretto", dark: true, base: { - surface: 'hsl(0,9%,16%)', - text: 'hsl(351,100%,97%)', - textSubtle: 'hsl(355,9%,74%)', - textSubtlest: 'hsl(354,4%,56%)', - primary: 'hsl(239,63%,79%)', - secondary: 'hsl(355,9%,74%)', - info: 'hsl(170,53%,69%)', - success: 'hsl(88,57%,66%)', - notice: 'hsl(41,92%,70%)', - warning: 'hsl(13,85%,70%)', - danger: 'hsl(349,97%,70%)', + surface: "hsl(0,9%,16%)", + text: "hsl(351,100%,97%)", + textSubtle: "hsl(355,9%,74%)", + textSubtlest: "hsl(354,4%,56%)", + primary: "hsl(239,63%,79%)", + secondary: "hsl(355,9%,74%)", + info: "hsl(170,53%,69%)", + success: "hsl(88,57%,66%)", + notice: "hsl(41,92%,70%)", + warning: "hsl(13,85%,70%)", + danger: "hsl(349,97%,70%)", }, components: { appHeader: { - surface: 'hsl(0,8%,12%)', - text: 'hsl(355,9%,74%)', - textSubtle: 'hsl(354,4%,56%)', - textSubtlest: 'hsl(353,4%,43%)', + surface: "hsl(0,8%,12%)", + text: "hsl(355,9%,74%)", + textSubtle: "hsl(354,4%,56%)", + textSubtlest: "hsl(353,4%,43%)", }, button: { - primary: 'hsl(239,63%,71%)', - secondary: 'hsl(355,9%,67%)', - info: 'hsl(170,53%,62%)', - success: 'hsl(88,57%,59%)', - notice: 'hsl(41,92%,63%)', - warning: 'hsl(13,86%,63%)', - danger: 'hsl(349,97%,63%)', + primary: "hsl(239,63%,71%)", + secondary: "hsl(355,9%,67%)", + info: "hsl(170,53%,62%)", + success: "hsl(88,57%,59%)", + notice: "hsl(41,92%,63%)", + warning: "hsl(13,86%,63%)", + danger: "hsl(349,97%,63%)", }, }, }; export const monokaiProSpectrum: Theme = { - id: 'monokai-pro-spectrum', - label: 'Monokai Pro Spectrum', + id: "monokai-pro-spectrum", + label: "Monokai Pro Spectrum", dark: true, base: { - surface: 'hsl(0,0%,13%)', - text: 'hsl(266,100%,97%)', - textSubtle: 'hsl(264,7%,73%)', - textSubtlest: 'hsl(266,3%,55%)', - primary: 'hsl(247,61%,72%)', - secondary: 'hsl(264,7%,73%)', - info: 'hsl(188,74%,63%)', - success: 'hsl(133,54%,66%)', - notice: 'hsl(51,96%,69%)', - warning: 'hsl(23,98%,66%)', - danger: 'hsl(343,96%,68%)', + surface: "hsl(0,0%,13%)", + text: "hsl(266,100%,97%)", + textSubtle: "hsl(264,7%,73%)", + textSubtlest: "hsl(266,3%,55%)", + primary: "hsl(247,61%,72%)", + secondary: "hsl(264,7%,73%)", + info: "hsl(188,74%,63%)", + success: "hsl(133,54%,66%)", + notice: "hsl(51,96%,69%)", + warning: "hsl(23,98%,66%)", + danger: "hsl(343,96%,68%)", }, components: { appHeader: { - surface: 'hsl(0,0%,10%)', - text: 'hsl(264,7%,73%)', - textSubtle: 'hsl(266,3%,55%)', - textSubtlest: 'hsl(264,2%,41%)', + surface: "hsl(0,0%,10%)", + text: "hsl(264,7%,73%)", + textSubtle: "hsl(266,3%,55%)", + textSubtlest: "hsl(264,2%,41%)", }, button: { - primary: 'hsl(247,61%,65%)', - secondary: 'hsl(264,7%,66%)', - info: 'hsl(188,74%,57%)', - success: 'hsl(133,54%,59%)', - notice: 'hsl(51,96%,62%)', - warning: 'hsl(23,98%,59%)', - danger: 'hsl(343,96%,61%)', + primary: "hsl(247,61%,65%)", + secondary: "hsl(264,7%,66%)", + info: "hsl(188,74%,57%)", + success: "hsl(133,54%,59%)", + notice: "hsl(51,96%,62%)", + warning: "hsl(23,98%,59%)", + danger: "hsl(343,96%,61%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/moonlight.ts b/plugins/themes-yaak/src/themes/moonlight.ts index 29a9c884..bb1e1b8d 100644 --- a/plugins/themes-yaak/src/themes/moonlight.ts +++ b/plugins/themes-yaak/src/themes/moonlight.ts @@ -1,28 +1,28 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const moonlight: Theme = { - id: 'moonlight', - label: 'Moonlight', + id: "moonlight", + label: "Moonlight", dark: true, base: { - surface: 'hsl(234,23%,17%)', - text: 'hsl(225,71%,90%)', - textSubtle: 'hsl(230,28%,62%)', - textSubtlest: 'hsl(232,26%,43%)', - primary: 'hsl(262,100%,82%)', - secondary: 'hsl(232,18%,65%)', - info: 'hsl(217,100%,74%)', - success: 'hsl(174,66%,54%)', - notice: 'hsl(35,100%,73%)', - warning: 'hsl(17,100%,71%)', - danger: 'hsl(356,100%,73%)', + surface: "hsl(234,23%,17%)", + text: "hsl(225,71%,90%)", + textSubtle: "hsl(230,28%,62%)", + textSubtlest: "hsl(232,26%,43%)", + primary: "hsl(262,100%,82%)", + secondary: "hsl(232,18%,65%)", + info: "hsl(217,100%,74%)", + success: "hsl(174,66%,54%)", + notice: "hsl(35,100%,73%)", + warning: "hsl(17,100%,71%)", + danger: "hsl(356,100%,73%)", }, components: { appHeader: { - surface: 'hsl(233,23%,15%)', + surface: "hsl(233,23%,15%)", }, sidebar: { - surface: 'hsl(233,23%,15%)', + surface: "hsl(233,23%,15%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/night-owl.ts b/plugins/themes-yaak/src/themes/night-owl.ts index 28c7f302..ea18e2b5 100644 --- a/plugins/themes-yaak/src/themes/night-owl.ts +++ b/plugins/themes-yaak/src/themes/night-owl.ts @@ -1,78 +1,78 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const nightOwl: Theme = { - id: 'night-owl', - label: 'Night Owl', + id: "night-owl", + label: "Night Owl", dark: true, base: { - surface: 'hsl(207, 95%, 8%)', - surfaceHighlight: 'hsl(207, 50%, 14%)', - text: 'hsl(213, 50%, 90%)', - textSubtle: 'hsl(213, 30%, 70%)', - textSubtlest: 'hsl(213, 20%, 50%)', - border: 'hsl(207, 50%, 14%)', - primary: 'hsl(261, 51%, 51%)', - secondary: 'hsl(213, 30%, 60%)', - info: 'hsl(220, 100%, 75%)', - success: 'hsl(145, 100%, 43%)', - notice: 'hsl(62, 61%, 71%)', - warning: 'hsl(4, 90%, 58%)', - danger: 'hsl(4, 90%, 58%)', + surface: "hsl(207, 95%, 8%)", + surfaceHighlight: "hsl(207, 50%, 14%)", + text: "hsl(213, 50%, 90%)", + textSubtle: "hsl(213, 30%, 70%)", + textSubtlest: "hsl(213, 20%, 50%)", + border: "hsl(207, 50%, 14%)", + primary: "hsl(261, 51%, 51%)", + secondary: "hsl(213, 30%, 60%)", + info: "hsl(220, 100%, 75%)", + success: "hsl(145, 100%, 43%)", + notice: "hsl(62, 61%, 71%)", + warning: "hsl(4, 90%, 58%)", + danger: "hsl(4, 90%, 58%)", }, components: { dialog: { - surface: 'hsl(207, 95%, 6%)', + surface: "hsl(207, 95%, 6%)", }, sidebar: { - surface: 'hsl(207, 95%, 8%)', - border: 'hsl(207, 50%, 14%)', + surface: "hsl(207, 95%, 8%)", + border: "hsl(207, 50%, 14%)", }, appHeader: { - surface: 'hsl(207, 95%, 5%)', - border: 'hsl(207, 50%, 12%)', + surface: "hsl(207, 95%, 5%)", + border: "hsl(207, 50%, 12%)", }, responsePane: { - surface: 'hsl(207, 70%, 10%)', - border: 'hsl(207, 50%, 14%)', + surface: "hsl(207, 70%, 10%)", + border: "hsl(207, 50%, 14%)", }, button: { - primary: 'hsl(261, 51%, 45%)', - secondary: 'hsl(213, 30%, 60%)', - info: 'hsl(220, 100%, 68%)', - success: 'hsl(145, 100%, 38%)', - notice: 'hsl(62, 61%, 64%)', - warning: 'hsl(4, 90%, 52%)', - danger: 'hsl(4, 90%, 52%)', + primary: "hsl(261, 51%, 45%)", + secondary: "hsl(213, 30%, 60%)", + info: "hsl(220, 100%, 68%)", + success: "hsl(145, 100%, 38%)", + notice: "hsl(62, 61%, 64%)", + warning: "hsl(4, 90%, 52%)", + danger: "hsl(4, 90%, 52%)", }, }, }; export const lightOwl: Theme = { - id: 'light-owl', - label: 'Light Owl', + id: "light-owl", + label: "Light Owl", dark: false, base: { - surface: 'hsl(0, 0%, 98%)', - surfaceHighlight: 'hsl(210, 18%, 94%)', - text: 'hsl(224, 26%, 27%)', - textSubtle: 'hsl(224, 15%, 45%)', - textSubtlest: 'hsl(224, 10%, 55%)', - primary: 'hsl(283, 100%, 41%)', - secondary: 'hsl(224, 15%, 50%)', - info: 'hsl(219, 75%, 40%)', - success: 'hsl(145, 70%, 35%)', - notice: 'hsl(36, 95%, 40%)', - warning: 'hsl(0, 55%, 55%)', - danger: 'hsl(0, 55%, 50%)', + surface: "hsl(0, 0%, 98%)", + surfaceHighlight: "hsl(210, 18%, 94%)", + text: "hsl(224, 26%, 27%)", + textSubtle: "hsl(224, 15%, 45%)", + textSubtlest: "hsl(224, 10%, 55%)", + primary: "hsl(283, 100%, 41%)", + secondary: "hsl(224, 15%, 50%)", + info: "hsl(219, 75%, 40%)", + success: "hsl(145, 70%, 35%)", + notice: "hsl(36, 95%, 40%)", + warning: "hsl(0, 55%, 55%)", + danger: "hsl(0, 55%, 50%)", }, components: { sidebar: { - surface: 'hsl(210, 20%, 96%)', - border: 'hsl(210, 15%, 90%)', + surface: "hsl(210, 20%, 96%)", + border: "hsl(210, 15%, 90%)", }, appHeader: { - surface: 'hsl(210, 20%, 94%)', - border: 'hsl(210, 15%, 88%)', + surface: "hsl(210, 20%, 94%)", + border: "hsl(210, 15%, 88%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/noctis.ts b/plugins/themes-yaak/src/themes/noctis.ts index 03c30ca2..4d1c54bd 100644 --- a/plugins/themes-yaak/src/themes/noctis.ts +++ b/plugins/themes-yaak/src/themes/noctis.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const noctisAzureus: Theme = { - id: 'noctis-azureus', - label: 'Noctis Azureus', + id: "noctis-azureus", + label: "Noctis Azureus", dark: true, base: { - surface: 'hsl(210, 35%, 14%)', - surfaceHighlight: 'hsl(210, 30%, 19%)', - text: 'hsl(180, 45%, 85%)', - textSubtle: 'hsl(180, 25%, 60%)', - textSubtlest: 'hsl(180, 18%, 45%)', - primary: 'hsl(175, 60%, 55%)', - secondary: 'hsl(200, 70%, 65%)', - info: 'hsl(200, 70%, 65%)', - success: 'hsl(85, 55%, 60%)', - notice: 'hsl(45, 90%, 60%)', - warning: 'hsl(25, 85%, 58%)', - danger: 'hsl(355, 75%, 62%)', + surface: "hsl(210, 35%, 14%)", + surfaceHighlight: "hsl(210, 30%, 19%)", + text: "hsl(180, 45%, 85%)", + textSubtle: "hsl(180, 25%, 60%)", + textSubtlest: "hsl(180, 18%, 45%)", + primary: "hsl(175, 60%, 55%)", + secondary: "hsl(200, 70%, 65%)", + info: "hsl(200, 70%, 65%)", + success: "hsl(85, 55%, 60%)", + notice: "hsl(45, 90%, 60%)", + warning: "hsl(25, 85%, 58%)", + danger: "hsl(355, 75%, 62%)", }, components: { dialog: { - surface: 'hsl(210, 35%, 11%)', + surface: "hsl(210, 35%, 11%)", }, sidebar: { - surface: 'hsl(210, 33%, 12%)', - border: 'hsl(210, 30%, 17%)', + surface: "hsl(210, 33%, 12%)", + border: "hsl(210, 30%, 17%)", }, appHeader: { - surface: 'hsl(210, 35%, 10%)', - border: 'hsl(210, 30%, 15%)', + surface: "hsl(210, 35%, 10%)", + border: "hsl(210, 30%, 15%)", }, responsePane: { - surface: 'hsl(210, 33%, 12%)', - border: 'hsl(210, 30%, 17%)', + surface: "hsl(210, 33%, 12%)", + border: "hsl(210, 30%, 17%)", }, button: { - primary: 'hsl(175, 60%, 48%)', - secondary: 'hsl(200, 70%, 58%)', - info: 'hsl(200, 70%, 58%)', - success: 'hsl(85, 55%, 53%)', - notice: 'hsl(45, 90%, 53%)', - warning: 'hsl(25, 85%, 51%)', - danger: 'hsl(355, 75%, 55%)', + primary: "hsl(175, 60%, 48%)", + secondary: "hsl(200, 70%, 58%)", + info: "hsl(200, 70%, 58%)", + success: "hsl(85, 55%, 53%)", + notice: "hsl(45, 90%, 53%)", + warning: "hsl(25, 85%, 51%)", + danger: "hsl(355, 75%, 55%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/nord.ts b/plugins/themes-yaak/src/themes/nord.ts index 85905daa..ba3b4431 100644 --- a/plugins/themes-yaak/src/themes/nord.ts +++ b/plugins/themes-yaak/src/themes/nord.ts @@ -1,85 +1,85 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const nord: Theme = { - id: 'nord', - label: 'Nord', + id: "nord", + label: "Nord", dark: true, base: { - surface: 'hsl(220,16%,22%)', - surfaceHighlight: 'hsl(220,14%,28%)', - text: 'hsl(220,28%,93%)', - textSubtle: 'hsl(220,26%,90%)', - textSubtlest: 'hsl(220,24%,86%)', - primary: 'hsl(193,38%,68%)', - secondary: 'hsl(210,34%,63%)', - info: 'hsl(174,25%,69%)', - success: 'hsl(89,26%,66%)', - notice: 'hsl(40,66%,73%)', - warning: 'hsl(17,48%,64%)', - danger: 'hsl(353,43%,56%)', + surface: "hsl(220,16%,22%)", + surfaceHighlight: "hsl(220,14%,28%)", + text: "hsl(220,28%,93%)", + textSubtle: "hsl(220,26%,90%)", + textSubtlest: "hsl(220,24%,86%)", + primary: "hsl(193,38%,68%)", + secondary: "hsl(210,34%,63%)", + info: "hsl(174,25%,69%)", + success: "hsl(89,26%,66%)", + notice: "hsl(40,66%,73%)", + warning: "hsl(17,48%,64%)", + danger: "hsl(353,43%,56%)", }, components: { sidebar: { - surface: 'hsl(220,16%,22%)', + surface: "hsl(220,16%,22%)", }, appHeader: { - surface: 'hsl(220,14%,28%)', + surface: "hsl(220,14%,28%)", }, }, }; export const nordLight: Theme = { - id: 'nord-light', - label: 'Nord Light', + id: "nord-light", + label: "Nord Light", dark: false, base: { - surface: '#eceff4', - surfaceHighlight: '#e5e9f0', - text: '#24292e', - textSubtle: '#444d56', - textSubtlest: '#586069', - primary: '#2188ff', - secondary: '#586069', - info: '#005cc5', - success: '#28a745', - notice: '#e36209', - warning: '#e36209', - danger: '#cb2431', + surface: "#eceff4", + surfaceHighlight: "#e5e9f0", + text: "#24292e", + textSubtle: "#444d56", + textSubtlest: "#586069", + primary: "#2188ff", + secondary: "#586069", + info: "#005cc5", + success: "#28a745", + notice: "#e36209", + warning: "#e36209", + danger: "#cb2431", }, components: { sidebar: { - surface: '#e5e9f0', + surface: "#e5e9f0", }, appHeader: { - surface: '#e5e9f0', + surface: "#e5e9f0", }, }, }; export const nordLightBrighter: Theme = { - id: 'nord-light-brighter', - label: 'Nord Light Brighter', + id: "nord-light-brighter", + label: "Nord Light Brighter", dark: false, base: { - surface: '#ffffff', - surfaceHighlight: '#f6f8fa', - text: '#24292e', - textSubtle: '#444d56', - textSubtlest: '#586069', - primary: '#2188ff', - secondary: '#586069', - info: '#005cc5', - success: '#28a745', - notice: '#e36209', - warning: '#e36209', - danger: '#cb2431', + surface: "#ffffff", + surfaceHighlight: "#f6f8fa", + text: "#24292e", + textSubtle: "#444d56", + textSubtlest: "#586069", + primary: "#2188ff", + secondary: "#586069", + info: "#005cc5", + success: "#28a745", + notice: "#e36209", + warning: "#e36209", + danger: "#cb2431", }, components: { sidebar: { - surface: '#f6f8fa', + surface: "#f6f8fa", }, appHeader: { - surface: '#f6f8fa', + surface: "#f6f8fa", }, }, }; diff --git a/plugins/themes-yaak/src/themes/one-dark-pro.ts b/plugins/themes-yaak/src/themes/one-dark-pro.ts index 0be776c0..d8c3e1af 100644 --- a/plugins/themes-yaak/src/themes/one-dark-pro.ts +++ b/plugins/themes-yaak/src/themes/one-dark-pro.ts @@ -1,44 +1,44 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const oneDarkPro: Theme = { - id: 'one-dark-pro', - label: 'One Dark Pro', + id: "one-dark-pro", + label: "One Dark Pro", dark: true, base: { - surface: 'hsl(220, 13%, 18%)', - surfaceHighlight: 'hsl(220, 13%, 22%)', - text: 'hsl(219, 14%, 71%)', - textSubtle: 'hsl(219, 10%, 53%)', - textSubtlest: 'hsl(220, 9%, 45%)', - primary: 'hsl(286, 60%, 67%)', - secondary: 'hsl(219, 14%, 60%)', - info: 'hsl(207, 82%, 66%)', - success: 'hsl(95, 38%, 62%)', - notice: 'hsl(39, 67%, 69%)', - warning: 'hsl(29, 54%, 61%)', - danger: 'hsl(355, 65%, 65%)', + surface: "hsl(220, 13%, 18%)", + surfaceHighlight: "hsl(220, 13%, 22%)", + text: "hsl(219, 14%, 71%)", + textSubtle: "hsl(219, 10%, 53%)", + textSubtlest: "hsl(220, 9%, 45%)", + primary: "hsl(286, 60%, 67%)", + secondary: "hsl(219, 14%, 60%)", + info: "hsl(207, 82%, 66%)", + success: "hsl(95, 38%, 62%)", + notice: "hsl(39, 67%, 69%)", + warning: "hsl(29, 54%, 61%)", + danger: "hsl(355, 65%, 65%)", }, components: { sidebar: { - surface: 'hsl(220, 13%, 16%)', - border: 'hsl(220, 13%, 20%)', + surface: "hsl(220, 13%, 16%)", + border: "hsl(220, 13%, 20%)", }, appHeader: { - surface: 'hsl(220, 13%, 14%)', - border: 'hsl(220, 13%, 20%)', + surface: "hsl(220, 13%, 14%)", + border: "hsl(220, 13%, 20%)", }, responsePane: { - surface: 'hsl(220, 13%, 16%)', - border: 'hsl(220, 13%, 20%)', + surface: "hsl(220, 13%, 16%)", + border: "hsl(220, 13%, 20%)", }, button: { - primary: 'hsl(286, 60%, 60%)', - secondary: 'hsl(219, 14%, 53%)', - info: 'hsl(207, 82%, 59%)', - success: 'hsl(95, 38%, 55%)', - notice: 'hsl(39, 67%, 62%)', - warning: 'hsl(29, 54%, 54%)', - danger: 'hsl(355, 65%, 58%)', + primary: "hsl(286, 60%, 60%)", + secondary: "hsl(219, 14%, 53%)", + info: "hsl(207, 82%, 59%)", + success: "hsl(95, 38%, 55%)", + notice: "hsl(39, 67%, 62%)", + warning: "hsl(29, 54%, 54%)", + danger: "hsl(355, 65%, 58%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/panda.ts b/plugins/themes-yaak/src/themes/panda.ts index c95ff485..aa2fc600 100644 --- a/plugins/themes-yaak/src/themes/panda.ts +++ b/plugins/themes-yaak/src/themes/panda.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const pandaSyntax: Theme = { - id: 'panda', - label: 'Panda Syntax', + id: "panda", + label: "Panda Syntax", dark: true, base: { - surface: 'hsl(225, 15%, 15%)', - surfaceHighlight: 'hsl(225, 12%, 20%)', - text: 'hsl(0, 0%, 90%)', - textSubtle: 'hsl(0, 0%, 65%)', - textSubtlest: 'hsl(0, 0%, 50%)', - primary: 'hsl(353, 95%, 70%)', - secondary: 'hsl(0, 0%, 65%)', - info: 'hsl(200, 85%, 65%)', - success: 'hsl(175, 90%, 65%)', - notice: 'hsl(40, 100%, 65%)', - warning: 'hsl(40, 100%, 65%)', - danger: 'hsl(0, 90%, 65%)', + surface: "hsl(225, 15%, 15%)", + surfaceHighlight: "hsl(225, 12%, 20%)", + text: "hsl(0, 0%, 90%)", + textSubtle: "hsl(0, 0%, 65%)", + textSubtlest: "hsl(0, 0%, 50%)", + primary: "hsl(353, 95%, 70%)", + secondary: "hsl(0, 0%, 65%)", + info: "hsl(200, 85%, 65%)", + success: "hsl(175, 90%, 65%)", + notice: "hsl(40, 100%, 65%)", + warning: "hsl(40, 100%, 65%)", + danger: "hsl(0, 90%, 65%)", }, components: { dialog: { - surface: 'hsl(225, 15%, 12%)', + surface: "hsl(225, 15%, 12%)", }, sidebar: { - surface: 'hsl(225, 14%, 13%)', - border: 'hsl(225, 12%, 18%)', + surface: "hsl(225, 14%, 13%)", + border: "hsl(225, 12%, 18%)", }, appHeader: { - surface: 'hsl(225, 15%, 11%)', - border: 'hsl(225, 12%, 16%)', + surface: "hsl(225, 15%, 11%)", + border: "hsl(225, 12%, 16%)", }, responsePane: { - surface: 'hsl(225, 14%, 13%)', - border: 'hsl(225, 12%, 18%)', + surface: "hsl(225, 14%, 13%)", + border: "hsl(225, 12%, 18%)", }, button: { - primary: 'hsl(353, 95%, 63%)', - secondary: 'hsl(0, 0%, 58%)', - info: 'hsl(200, 85%, 58%)', - success: 'hsl(175, 90%, 58%)', - notice: 'hsl(40, 100%, 58%)', - warning: 'hsl(40, 100%, 58%)', - danger: 'hsl(0, 90%, 58%)', + primary: "hsl(353, 95%, 63%)", + secondary: "hsl(0, 0%, 58%)", + info: "hsl(200, 85%, 58%)", + success: "hsl(175, 90%, 58%)", + notice: "hsl(40, 100%, 58%)", + warning: "hsl(40, 100%, 58%)", + danger: "hsl(0, 90%, 58%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/relaxing.ts b/plugins/themes-yaak/src/themes/relaxing.ts index 80025b64..360b2068 100644 --- a/plugins/themes-yaak/src/themes/relaxing.ts +++ b/plugins/themes-yaak/src/themes/relaxing.ts @@ -1,18 +1,18 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const relaxing: Theme = { - id: 'relaxing', - label: 'Relaxing', + id: "relaxing", + label: "Relaxing", dark: true, base: { - surface: 'hsl(267,33%,17%)', - text: 'hsl(275,49%,92%)', - primary: 'hsl(267,84%,81%)', - secondary: 'hsl(227,35%,80%)', - info: 'hsl(217,92%,76%)', - success: 'hsl(115,54%,76%)', - notice: 'hsl(41,86%,83%)', - warning: 'hsl(23,92%,75%)', - danger: 'hsl(343,81%,75%)', + surface: "hsl(267,33%,17%)", + text: "hsl(275,49%,92%)", + primary: "hsl(267,84%,81%)", + secondary: "hsl(227,35%,80%)", + info: "hsl(217,92%,76%)", + success: "hsl(115,54%,76%)", + notice: "hsl(41,86%,83%)", + warning: "hsl(23,92%,75%)", + danger: "hsl(343,81%,75%)", }, }; diff --git a/plugins/themes-yaak/src/themes/rose-pine.ts b/plugins/themes-yaak/src/themes/rose-pine.ts index 47113dfb..f799b95f 100644 --- a/plugins/themes-yaak/src/themes/rose-pine.ts +++ b/plugins/themes-yaak/src/themes/rose-pine.ts @@ -1,111 +1,111 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const rosePine: Theme = { - id: 'rose-pine', - label: 'Rosé Pine', + id: "rose-pine", + label: "Rosé Pine", dark: true, base: { - surface: 'hsl(249,22%,12%)', - text: 'hsl(245,50%,91%)', - textSubtle: 'hsl(248,15%,61%)', - textSubtlest: 'hsl(249,12%,47%)', - primary: 'hsl(267,57%,78%)', - secondary: 'hsl(249,12%,47%)', - info: 'hsl(199,49%,60%)', - success: 'hsl(180,43%,73%)', - notice: 'hsl(35,88%,72%)', - warning: 'hsl(1,74%,79%)', - danger: 'hsl(343,76%,68%)', + surface: "hsl(249,22%,12%)", + text: "hsl(245,50%,91%)", + textSubtle: "hsl(248,15%,61%)", + textSubtlest: "hsl(249,12%,47%)", + primary: "hsl(267,57%,78%)", + secondary: "hsl(249,12%,47%)", + info: "hsl(199,49%,60%)", + success: "hsl(180,43%,73%)", + notice: "hsl(35,88%,72%)", + warning: "hsl(1,74%,79%)", + danger: "hsl(343,76%,68%)", }, components: { responsePane: { - surface: 'hsl(247,23%,15%)', + surface: "hsl(247,23%,15%)", }, sidebar: { - surface: 'hsl(247,23%,15%)', + surface: "hsl(247,23%,15%)", }, menu: { - surface: 'hsl(248,21%,26%)', - textSubtle: 'hsl(248,15%,66%)', - textSubtlest: 'hsl(249,12%,52%)', - border: 'hsl(248,21%,35%)', - borderSubtle: 'hsl(248,21%,33%)', + surface: "hsl(248,21%,26%)", + textSubtle: "hsl(248,15%,66%)", + textSubtlest: "hsl(249,12%,52%)", + border: "hsl(248,21%,35%)", + borderSubtle: "hsl(248,21%,33%)", }, }, }; export const rosePineMoon: Theme = { - id: 'rose-pine-moon', - label: 'Rosé Pine Moon', + id: "rose-pine-moon", + label: "Rosé Pine Moon", dark: true, base: { - surface: 'hsl(246,24%,17%)', - text: 'hsl(245,50%,91%)', - textSubtle: 'hsl(248,15%,61%)', - textSubtlest: 'hsl(249,12%,47%)', - primary: 'hsl(267,57%,78%)', - secondary: 'hsl(248,15%,61%)', - info: 'hsl(197,48%,60%)', - success: 'hsl(197,48%,60%)', - notice: 'hsl(35,88%,72%)', - warning: 'hsl(2,66%,75%)', - danger: 'hsl(343,76%,68%)', + surface: "hsl(246,24%,17%)", + text: "hsl(245,50%,91%)", + textSubtle: "hsl(248,15%,61%)", + textSubtlest: "hsl(249,12%,47%)", + primary: "hsl(267,57%,78%)", + secondary: "hsl(248,15%,61%)", + info: "hsl(197,48%,60%)", + success: "hsl(197,48%,60%)", + notice: "hsl(35,88%,72%)", + warning: "hsl(2,66%,75%)", + danger: "hsl(343,76%,68%)", }, components: { responsePane: { - surface: 'hsl(247,24%,20%)', + surface: "hsl(247,24%,20%)", }, sidebar: { - surface: 'hsl(247,24%,20%)', + surface: "hsl(247,24%,20%)", }, menu: { - surface: 'hsl(248,21%,26%)', - textSubtle: 'hsl(248,15%,61%)', - textSubtlest: 'hsl(249,12%,55%)', - border: 'hsl(248,21%,35%)', - borderSubtle: 'hsl(248,21%,31%)', + surface: "hsl(248,21%,26%)", + textSubtle: "hsl(248,15%,61%)", + textSubtlest: "hsl(249,12%,55%)", + border: "hsl(248,21%,35%)", + borderSubtle: "hsl(248,21%,31%)", }, }, }; export const rosePineDawn: Theme = { - id: 'rose-pine-dawn', - label: 'Rosé Pine Dawn', + id: "rose-pine-dawn", + label: "Rosé Pine Dawn", dark: false, base: { - surface: 'hsl(32,57%,95%)', - border: 'hsl(10,9%,86%)', - surfaceHighlight: 'hsl(25,35%,93%)', - text: 'hsl(248,19%,40%)', - textSubtle: 'hsl(248,12%,52%)', - textSubtlest: 'hsl(257,9%,61%)', - primary: 'hsl(271,27%,56%)', - secondary: 'hsl(249,12%,47%)', - info: 'hsl(197,52%,36%)', - success: 'hsl(188,31%,45%)', - notice: 'hsl(34,64%,49%)', - warning: 'hsl(2,47%,64%)', - danger: 'hsl(343,35%,55%)', + surface: "hsl(32,57%,95%)", + border: "hsl(10,9%,86%)", + surfaceHighlight: "hsl(25,35%,93%)", + text: "hsl(248,19%,40%)", + textSubtle: "hsl(248,12%,52%)", + textSubtlest: "hsl(257,9%,61%)", + primary: "hsl(271,27%,56%)", + secondary: "hsl(249,12%,47%)", + info: "hsl(197,52%,36%)", + success: "hsl(188,31%,45%)", + notice: "hsl(34,64%,49%)", + warning: "hsl(2,47%,64%)", + danger: "hsl(343,35%,55%)", }, components: { responsePane: { - border: 'hsl(20,12%,90%)', + border: "hsl(20,12%,90%)", }, sidebar: { - border: 'hsl(20,12%,90%)', + border: "hsl(20,12%,90%)", }, appHeader: { - border: 'hsl(20,12%,90%)', + border: "hsl(20,12%,90%)", }, input: { - border: 'hsl(10,9%,86%)', + border: "hsl(10,9%,86%)", }, dialog: { - border: 'hsl(20,12%,90%)', + border: "hsl(20,12%,90%)", }, menu: { - surface: 'hsl(28,40%,92%)', - border: 'hsl(10,9%,86%)', + surface: "hsl(28,40%,92%)", + border: "hsl(10,9%,86%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/shades-of-purple.ts b/plugins/themes-yaak/src/themes/shades-of-purple.ts index ca9e66a9..f297397a 100644 --- a/plugins/themes-yaak/src/themes/shades-of-purple.ts +++ b/plugins/themes-yaak/src/themes/shades-of-purple.ts @@ -1,99 +1,99 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const shadesOfPurple: Theme = { - id: 'shades-of-purple', - label: 'Shades of Purple', + id: "shades-of-purple", + label: "Shades of Purple", dark: true, base: { - surface: '#2D2B55', - surfaceHighlight: '#1F1F41', - text: '#FFFFFF', - textSubtle: '#A599E9', - textSubtlest: '#7E72C4', - primary: '#FAD000', - secondary: '#A599E9', - info: '#80FFBB', - success: '#3AD900', - notice: '#FAD000', - warning: '#FF9D00', - danger: '#EC3A37F5', + surface: "#2D2B55", + surfaceHighlight: "#1F1F41", + text: "#FFFFFF", + textSubtle: "#A599E9", + textSubtlest: "#7E72C4", + primary: "#FAD000", + secondary: "#A599E9", + info: "#80FFBB", + success: "#3AD900", + notice: "#FAD000", + warning: "#FF9D00", + danger: "#EC3A37F5", }, components: { dialog: { - surface: '#1E1E3F', + surface: "#1E1E3F", }, sidebar: { - surface: '#222244', - border: '#1E1E3F', + surface: "#222244", + border: "#1E1E3F", }, input: { - border: '#7E72C4', + border: "#7E72C4", }, appHeader: { - surface: '#1E1E3F', - border: '#1E1E3F', + surface: "#1E1E3F", + border: "#1E1E3F", }, responsePane: { - surface: 'hsl(240,33%,20%)', - border: 'hsl(240,33%,20%)', + surface: "hsl(240,33%,20%)", + border: "hsl(240,33%,20%)", }, button: { - primary: '#FAD000', - secondary: '#A599E9', - info: '#80FFBB', - success: '#3AD900', - notice: '#FAD000', - warning: '#FF9D00', - danger: '#EC3A37F5', + primary: "#FAD000", + secondary: "#A599E9", + info: "#80FFBB", + success: "#3AD900", + notice: "#FAD000", + warning: "#FF9D00", + danger: "#EC3A37F5", }, }, }; export const shadesOfPurpleSuperDark: Theme = { - id: 'shades-of-purple-super-dark', - label: 'Shades of Purple (Super Dark)', + id: "shades-of-purple-super-dark", + label: "Shades of Purple (Super Dark)", dark: true, base: { - surface: '#191830', - surfaceHighlight: '#1F1E3A', - text: '#FFFFFF', - textSubtle: '#A599E9', - textSubtlest: '#7E72C4', - primary: '#FAD000', - secondary: '#A599E9', - info: '#80FFBB', - success: '#3AD900', - notice: '#FAD000', - warning: '#FF9D00', - danger: '#EC3A37F5', + surface: "#191830", + surfaceHighlight: "#1F1E3A", + text: "#FFFFFF", + textSubtle: "#A599E9", + textSubtlest: "#7E72C4", + primary: "#FAD000", + secondary: "#A599E9", + info: "#80FFBB", + success: "#3AD900", + notice: "#FAD000", + warning: "#FF9D00", + danger: "#EC3A37F5", }, components: { dialog: { - surface: '#15152b', + surface: "#15152b", }, input: { - border: '#2D2B55', + border: "#2D2B55", }, sidebar: { - surface: '#131327', - border: '#131327', + surface: "#131327", + border: "#131327", }, appHeader: { - surface: '#15152a', - border: '#15152a', + surface: "#15152a", + border: "#15152a", }, responsePane: { - surface: '#131327', - border: '#131327', + surface: "#131327", + border: "#131327", }, button: { - primary: '#FAD000', - secondary: '#A599E9', - info: '#80FFBB', - success: '#3AD900', - notice: '#FAD000', - warning: '#FF9D00', - danger: '#EC3A37F5', + primary: "#FAD000", + secondary: "#A599E9", + info: "#80FFBB", + success: "#3AD900", + notice: "#FAD000", + warning: "#FF9D00", + danger: "#EC3A37F5", }, }, }; diff --git a/plugins/themes-yaak/src/themes/slack.ts b/plugins/themes-yaak/src/themes/slack.ts index 908fe00a..08539c34 100644 --- a/plugins/themes-yaak/src/themes/slack.ts +++ b/plugins/themes-yaak/src/themes/slack.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const slackAubergine: Theme = { - id: 'slack-aubergine', - label: 'Slack Aubergine', + id: "slack-aubergine", + label: "Slack Aubergine", dark: true, base: { - surface: 'hsl(270, 25%, 18%)', - surfaceHighlight: 'hsl(270, 22%, 24%)', - text: 'hsl(0, 0%, 100%)', - textSubtle: 'hsl(270, 15%, 75%)', - textSubtlest: 'hsl(270, 12%, 58%)', - primary: 'hsl(165, 100%, 40%)', - secondary: 'hsl(270, 12%, 65%)', - info: 'hsl(195, 95%, 55%)', - success: 'hsl(145, 80%, 50%)', - notice: 'hsl(43, 100%, 55%)', - warning: 'hsl(43, 100%, 50%)', - danger: 'hsl(0, 80%, 55%)', + surface: "hsl(270, 25%, 18%)", + surfaceHighlight: "hsl(270, 22%, 24%)", + text: "hsl(0, 0%, 100%)", + textSubtle: "hsl(270, 15%, 75%)", + textSubtlest: "hsl(270, 12%, 58%)", + primary: "hsl(165, 100%, 40%)", + secondary: "hsl(270, 12%, 65%)", + info: "hsl(195, 95%, 55%)", + success: "hsl(145, 80%, 50%)", + notice: "hsl(43, 100%, 55%)", + warning: "hsl(43, 100%, 50%)", + danger: "hsl(0, 80%, 55%)", }, components: { dialog: { - surface: 'hsl(270, 25%, 14%)', + surface: "hsl(270, 25%, 14%)", }, sidebar: { - surface: 'hsl(270, 23%, 15%)', - border: 'hsl(270, 22%, 22%)', + surface: "hsl(270, 23%, 15%)", + border: "hsl(270, 22%, 22%)", }, appHeader: { - surface: 'hsl(270, 25%, 13%)', - border: 'hsl(270, 22%, 20%)', + surface: "hsl(270, 25%, 13%)", + border: "hsl(270, 22%, 20%)", }, responsePane: { - surface: 'hsl(270, 23%, 15%)', - border: 'hsl(270, 22%, 22%)', + surface: "hsl(270, 23%, 15%)", + border: "hsl(270, 22%, 22%)", }, button: { - primary: 'hsl(165, 100%, 35%)', - secondary: 'hsl(270, 12%, 58%)', - info: 'hsl(195, 95%, 48%)', - success: 'hsl(145, 80%, 45%)', - notice: 'hsl(43, 100%, 48%)', - warning: 'hsl(43, 100%, 45%)', - danger: 'hsl(0, 80%, 48%)', + primary: "hsl(165, 100%, 35%)", + secondary: "hsl(270, 12%, 58%)", + info: "hsl(195, 95%, 48%)", + success: "hsl(145, 80%, 45%)", + notice: "hsl(43, 100%, 48%)", + warning: "hsl(43, 100%, 45%)", + danger: "hsl(0, 80%, 48%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/solarized.ts b/plugins/themes-yaak/src/themes/solarized.ts index 84fad9f4..ca9c7d89 100644 --- a/plugins/themes-yaak/src/themes/solarized.ts +++ b/plugins/themes-yaak/src/themes/solarized.ts @@ -1,77 +1,77 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const solarizedDark: Theme = { - id: 'solarized-dark', - label: 'Solarized Dark', + id: "solarized-dark", + label: "Solarized Dark", dark: true, base: { - surface: '#002b36', - surfaceHighlight: '#073642', - text: '#839496', - textSubtle: '#657b83', - textSubtlest: '#586e75', - primary: '#268bd2', - secondary: '#657b83', - info: '#268bd2', - success: '#859900', - notice: '#b58900', - warning: '#cb4b16', - danger: '#dc322f', + surface: "#002b36", + surfaceHighlight: "#073642", + text: "#839496", + textSubtle: "#657b83", + textSubtlest: "#586e75", + primary: "#268bd2", + secondary: "#657b83", + info: "#268bd2", + success: "#859900", + notice: "#b58900", + warning: "#cb4b16", + danger: "#dc322f", }, components: { dialog: { - surface: '#002b36', + surface: "#002b36", }, sidebar: { - surface: '#073642', - border: 'hsl(192,81%,17%)', + surface: "#073642", + border: "hsl(192,81%,17%)", }, appHeader: { - surface: '#002b36', - border: 'hsl(192,81%,16%)', + surface: "#002b36", + border: "hsl(192,81%,16%)", }, responsePane: { - surface: '#073642', - border: 'hsl(192,81%,17%)', + surface: "#073642", + border: "hsl(192,81%,17%)", }, button: { - primary: '#268bd2', - secondary: '#657b83', - info: '#268bd2', - success: '#859900', - notice: '#b58900', - warning: '#cb4b16', - danger: '#dc322f', + primary: "#268bd2", + secondary: "#657b83", + info: "#268bd2", + success: "#859900", + notice: "#b58900", + warning: "#cb4b16", + danger: "#dc322f", }, }, }; export const solarizedLight: Theme = { - id: 'solarized-light', - label: 'Solarized Light', + id: "solarized-light", + label: "Solarized Light", dark: false, base: { - surface: '#fdf6e3', - surfaceHighlight: '#eee8d5', - text: '#657b83', - textSubtle: '#839496', - textSubtlest: '#93a1a1', - primary: '#268bd2', - secondary: '#839496', - info: '#268bd2', - success: '#859900', - notice: '#b58900', - warning: '#cb4b16', - danger: '#dc322f', + surface: "#fdf6e3", + surfaceHighlight: "#eee8d5", + text: "#657b83", + textSubtle: "#839496", + textSubtlest: "#93a1a1", + primary: "#268bd2", + secondary: "#839496", + info: "#268bd2", + success: "#859900", + notice: "#b58900", + warning: "#cb4b16", + danger: "#dc322f", }, components: { sidebar: { - surface: '#eee8d5', - border: '#d3cbb7', + surface: "#eee8d5", + border: "#d3cbb7", }, appHeader: { - surface: '#eee8d5', - border: '#d3cbb7', + surface: "#eee8d5", + border: "#d3cbb7", }, }, }; diff --git a/plugins/themes-yaak/src/themes/synthwave-84.ts b/plugins/themes-yaak/src/themes/synthwave-84.ts index 2a0977f0..85febf93 100644 --- a/plugins/themes-yaak/src/themes/synthwave-84.ts +++ b/plugins/themes-yaak/src/themes/synthwave-84.ts @@ -1,53 +1,53 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const synthwave84: Theme = { - id: 'synthwave-84', + id: "synthwave-84", label: "SynthWave '84", dark: true, base: { - surface: 'hsl(253, 45%, 15%)', - surfaceHighlight: 'hsl(253, 40%, 20%)', - text: 'hsl(300, 50%, 90%)', - textSubtle: 'hsl(280, 25%, 65%)', - textSubtlest: 'hsl(280, 20%, 50%)', - primary: 'hsl(320, 100%, 75%)', - secondary: 'hsl(280, 20%, 60%)', - info: 'hsl(177, 100%, 55%)', - success: 'hsl(83, 100%, 60%)', - notice: 'hsl(57, 100%, 60%)', - warning: 'hsl(30, 100%, 60%)', - danger: 'hsl(340, 100%, 65%)', + surface: "hsl(253, 45%, 15%)", + surfaceHighlight: "hsl(253, 40%, 20%)", + text: "hsl(300, 50%, 90%)", + textSubtle: "hsl(280, 25%, 65%)", + textSubtlest: "hsl(280, 20%, 50%)", + primary: "hsl(320, 100%, 75%)", + secondary: "hsl(280, 20%, 60%)", + info: "hsl(177, 100%, 55%)", + success: "hsl(83, 100%, 60%)", + notice: "hsl(57, 100%, 60%)", + warning: "hsl(30, 100%, 60%)", + danger: "hsl(340, 100%, 65%)", }, components: { sidebar: { - surface: 'hsl(253, 42%, 18%)', - border: 'hsl(253, 40%, 22%)', + surface: "hsl(253, 42%, 18%)", + border: "hsl(253, 40%, 22%)", }, appHeader: { - surface: 'hsl(253, 45%, 11%)', - border: 'hsl(253, 40%, 18%)', + surface: "hsl(253, 45%, 11%)", + border: "hsl(253, 40%, 18%)", }, responsePane: { - surface: 'hsl(253, 42%, 18%)', - border: 'hsl(253, 40%, 22%)', + surface: "hsl(253, 42%, 18%)", + border: "hsl(253, 40%, 22%)", }, button: { - primary: 'hsl(320, 100%, 68%)', - secondary: 'hsl(280, 20%, 53%)', - info: 'hsl(177, 100%, 48%)', - success: 'hsl(83, 100%, 53%)', - notice: 'hsl(57, 100%, 53%)', - warning: 'hsl(30, 100%, 53%)', - danger: 'hsl(340, 100%, 58%)', + primary: "hsl(320, 100%, 68%)", + secondary: "hsl(280, 20%, 53%)", + info: "hsl(177, 100%, 48%)", + success: "hsl(83, 100%, 53%)", + notice: "hsl(57, 100%, 53%)", + warning: "hsl(30, 100%, 53%)", + danger: "hsl(340, 100%, 58%)", }, editor: { - primary: 'hsl(177, 100%, 55%)', - secondary: 'hsl(280, 20%, 60%)', - info: 'hsl(320, 100%, 75%)', - success: 'hsl(83, 100%, 60%)', - notice: 'hsl(57, 100%, 60%)', - warning: 'hsl(30, 100%, 60%)', - danger: 'hsl(340, 100%, 65%)', + primary: "hsl(177, 100%, 55%)", + secondary: "hsl(280, 20%, 60%)", + info: "hsl(320, 100%, 75%)", + success: "hsl(83, 100%, 60%)", + notice: "hsl(57, 100%, 60%)", + warning: "hsl(30, 100%, 60%)", + danger: "hsl(340, 100%, 65%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/tokyo-night.ts b/plugins/themes-yaak/src/themes/tokyo-night.ts index 1b01a9d1..e99bf2a4 100644 --- a/plugins/themes-yaak/src/themes/tokyo-night.ts +++ b/plugins/themes-yaak/src/themes/tokyo-night.ts @@ -1,121 +1,121 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const tokyoNight: Theme = { - id: 'tokyo-night', - label: 'Tokyo Night', + id: "tokyo-night", + label: "Tokyo Night", dark: true, base: { - surface: 'hsl(235, 21%, 13%)', - surfaceHighlight: 'hsl(235, 18%, 18%)', - text: 'hsl(229, 28%, 76%)', - textSubtle: 'hsl(232, 18%, 52%)', - textSubtlest: 'hsl(234, 16%, 40%)', - primary: 'hsl(266, 100%, 78%)', - secondary: 'hsl(232, 18%, 52%)', - info: 'hsl(217, 100%, 73%)', - success: 'hsl(158, 57%, 63%)', - notice: 'hsl(40, 67%, 65%)', - warning: 'hsl(25, 75%, 58%)', - danger: 'hsl(358, 100%, 70%)', + surface: "hsl(235, 21%, 13%)", + surfaceHighlight: "hsl(235, 18%, 18%)", + text: "hsl(229, 28%, 76%)", + textSubtle: "hsl(232, 18%, 52%)", + textSubtlest: "hsl(234, 16%, 40%)", + primary: "hsl(266, 100%, 78%)", + secondary: "hsl(232, 18%, 52%)", + info: "hsl(217, 100%, 73%)", + success: "hsl(158, 57%, 63%)", + notice: "hsl(40, 67%, 65%)", + warning: "hsl(25, 75%, 58%)", + danger: "hsl(358, 100%, 70%)", }, components: { dialog: { - surface: 'hsl(235, 21%, 11%)', + surface: "hsl(235, 21%, 11%)", }, sidebar: { - surface: 'hsl(235, 21%, 11%)', - border: 'hsl(235, 18%, 16%)', + surface: "hsl(235, 21%, 11%)", + border: "hsl(235, 18%, 16%)", }, appHeader: { - surface: 'hsl(235, 21%, 9%)', - border: 'hsl(235, 18%, 14%)', + surface: "hsl(235, 21%, 9%)", + border: "hsl(235, 18%, 14%)", }, responsePane: { - surface: 'hsl(235, 21%, 11%)', - border: 'hsl(235, 18%, 16%)', + surface: "hsl(235, 21%, 11%)", + border: "hsl(235, 18%, 16%)", }, button: { - primary: 'hsl(266, 100%, 71%)', - info: 'hsl(217, 100%, 66%)', - success: 'hsl(158, 57%, 56%)', - notice: 'hsl(40, 67%, 58%)', - warning: 'hsl(25, 75%, 52%)', - danger: 'hsl(358, 100%, 63%)', + primary: "hsl(266, 100%, 71%)", + info: "hsl(217, 100%, 66%)", + success: "hsl(158, 57%, 56%)", + notice: "hsl(40, 67%, 58%)", + warning: "hsl(25, 75%, 52%)", + danger: "hsl(358, 100%, 63%)", }, }, }; export const tokyoNightStorm: Theme = { - id: 'tokyo-night-storm', - label: 'Tokyo Night Storm', + id: "tokyo-night-storm", + label: "Tokyo Night Storm", dark: true, base: { - surface: 'hsl(232, 25%, 17%)', - surfaceHighlight: 'hsl(232, 22%, 22%)', - text: 'hsl(229, 28%, 76%)', - textSubtle: 'hsl(232, 18%, 52%)', - textSubtlest: 'hsl(234, 16%, 40%)', - primary: 'hsl(266, 100%, 78%)', - secondary: 'hsl(232, 18%, 52%)', - info: 'hsl(217, 100%, 73%)', - success: 'hsl(158, 57%, 63%)', - notice: 'hsl(40, 67%, 65%)', - warning: 'hsl(25, 75%, 58%)', - danger: 'hsl(358, 100%, 70%)', + surface: "hsl(232, 25%, 17%)", + surfaceHighlight: "hsl(232, 22%, 22%)", + text: "hsl(229, 28%, 76%)", + textSubtle: "hsl(232, 18%, 52%)", + textSubtlest: "hsl(234, 16%, 40%)", + primary: "hsl(266, 100%, 78%)", + secondary: "hsl(232, 18%, 52%)", + info: "hsl(217, 100%, 73%)", + success: "hsl(158, 57%, 63%)", + notice: "hsl(40, 67%, 65%)", + warning: "hsl(25, 75%, 58%)", + danger: "hsl(358, 100%, 70%)", }, components: { dialog: { - surface: 'hsl(232, 25%, 14%)', + surface: "hsl(232, 25%, 14%)", }, sidebar: { - surface: 'hsl(232, 25%, 14%)', - border: 'hsl(232, 22%, 20%)', + surface: "hsl(232, 25%, 14%)", + border: "hsl(232, 22%, 20%)", }, appHeader: { - surface: 'hsl(232, 25%, 12%)', - border: 'hsl(232, 22%, 18%)', + surface: "hsl(232, 25%, 12%)", + border: "hsl(232, 22%, 18%)", }, responsePane: { - surface: 'hsl(232, 25%, 14%)', - border: 'hsl(232, 22%, 20%)', + surface: "hsl(232, 25%, 14%)", + border: "hsl(232, 22%, 20%)", }, button: { - primary: 'hsl(266, 100%, 71%)', - info: 'hsl(217, 100%, 66%)', - success: 'hsl(158, 57%, 56%)', - notice: 'hsl(40, 67%, 58%)', - warning: 'hsl(25, 75%, 52%)', - danger: 'hsl(358, 100%, 63%)', + primary: "hsl(266, 100%, 71%)", + info: "hsl(217, 100%, 66%)", + success: "hsl(158, 57%, 56%)", + notice: "hsl(40, 67%, 58%)", + warning: "hsl(25, 75%, 52%)", + danger: "hsl(358, 100%, 63%)", }, }, }; export const tokyoNightDay: Theme = { - id: 'tokyo-night-day', - label: 'Tokyo Night Day', + id: "tokyo-night-day", + label: "Tokyo Night Day", dark: false, base: { - surface: 'hsl(212, 100%, 98%)', - surfaceHighlight: 'hsl(212, 60%, 93%)', - text: 'hsl(233, 26%, 27%)', - textSubtle: 'hsl(232, 18%, 45%)', - textSubtlest: 'hsl(232, 12%, 55%)', - primary: 'hsl(290, 80%, 45%)', - secondary: 'hsl(232, 18%, 50%)', - info: 'hsl(217, 88%, 52%)', - success: 'hsl(160, 75%, 35%)', - notice: 'hsl(41, 80%, 40%)', - warning: 'hsl(20, 80%, 48%)', - danger: 'hsl(359, 65%, 48%)', + surface: "hsl(212, 100%, 98%)", + surfaceHighlight: "hsl(212, 60%, 93%)", + text: "hsl(233, 26%, 27%)", + textSubtle: "hsl(232, 18%, 45%)", + textSubtlest: "hsl(232, 12%, 55%)", + primary: "hsl(290, 80%, 45%)", + secondary: "hsl(232, 18%, 50%)", + info: "hsl(217, 88%, 52%)", + success: "hsl(160, 75%, 35%)", + notice: "hsl(41, 80%, 40%)", + warning: "hsl(20, 80%, 48%)", + danger: "hsl(359, 65%, 48%)", }, components: { sidebar: { - surface: 'hsl(212, 60%, 95%)', - border: 'hsl(212, 40%, 88%)', + surface: "hsl(212, 60%, 95%)", + border: "hsl(212, 40%, 88%)", }, appHeader: { - surface: 'hsl(212, 60%, 93%)', - border: 'hsl(212, 40%, 86%)', + surface: "hsl(212, 60%, 93%)", + border: "hsl(212, 40%, 86%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/triangle.ts b/plugins/themes-yaak/src/themes/triangle.ts index c3434d3a..e7db0fda 100644 --- a/plugins/themes-yaak/src/themes/triangle.ts +++ b/plugins/themes-yaak/src/themes/triangle.ts @@ -1,44 +1,44 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const triangle: Theme = { - id: 'triangle', + id: "triangle", dark: true, - label: 'Triangle', + label: "Triangle", base: { - surface: 'rgb(0,0,0)', - surfaceHighlight: 'rgb(21,21,21)', - surfaceActive: 'rgb(31,31,31)', - text: 'rgb(237,237,237)', - textSubtle: 'rgb(161,161,161)', - textSubtlest: 'rgb(115,115,115)', - border: 'rgb(31,31,31)', - primary: 'rgb(196,114,251)', - secondary: 'rgb(161,161,161)', - info: 'rgb(71,168,255)', - success: 'rgb(0,202,81)', - notice: 'rgb(255,175,0)', - warning: '#FF4C8D', - danger: '#fd495a', + surface: "rgb(0,0,0)", + surfaceHighlight: "rgb(21,21,21)", + surfaceActive: "rgb(31,31,31)", + text: "rgb(237,237,237)", + textSubtle: "rgb(161,161,161)", + textSubtlest: "rgb(115,115,115)", + border: "rgb(31,31,31)", + primary: "rgb(196,114,251)", + secondary: "rgb(161,161,161)", + info: "rgb(71,168,255)", + success: "rgb(0,202,81)", + notice: "rgb(255,175,0)", + warning: "#FF4C8D", + danger: "#fd495a", }, components: { editor: { - danger: '#FF4C8D', - warning: '#fd495a', + danger: "#FF4C8D", + warning: "#fd495a", }, dialog: { - surface: 'rgb(10,10,10)', - border: 'rgb(31,31,31)', + surface: "rgb(10,10,10)", + border: "rgb(31,31,31)", }, sidebar: { - border: 'rgb(31,31,31)', + border: "rgb(31,31,31)", }, responsePane: { - surface: 'rgb(10,10,10)', - border: 'rgb(31,31,31)', + surface: "rgb(10,10,10)", + border: "rgb(31,31,31)", }, appHeader: { - surface: 'rgb(10,10,10)', - border: 'rgb(31,31,31)', + surface: "rgb(10,10,10)", + border: "rgb(31,31,31)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/vitesse.ts b/plugins/themes-yaak/src/themes/vitesse.ts index 6f5b4391..fa52cc41 100644 --- a/plugins/themes-yaak/src/themes/vitesse.ts +++ b/plugins/themes-yaak/src/themes/vitesse.ts @@ -1,77 +1,77 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const vitesseDark: Theme = { - id: 'vitesse-dark', - label: 'Vitesse Dark', + id: "vitesse-dark", + label: "Vitesse Dark", dark: true, base: { - surface: 'hsl(220, 13%, 10%)', - surfaceHighlight: 'hsl(220, 12%, 15%)', - text: 'hsl(220, 10%, 80%)', - textSubtle: 'hsl(220, 8%, 55%)', - textSubtlest: 'hsl(220, 6%, 42%)', - primary: 'hsl(143, 50%, 55%)', - secondary: 'hsl(220, 8%, 55%)', - info: 'hsl(214, 60%, 65%)', - success: 'hsl(143, 50%, 55%)', - notice: 'hsl(45, 65%, 65%)', - warning: 'hsl(30, 60%, 60%)', - danger: 'hsl(355, 60%, 60%)', + surface: "hsl(220, 13%, 10%)", + surfaceHighlight: "hsl(220, 12%, 15%)", + text: "hsl(220, 10%, 80%)", + textSubtle: "hsl(220, 8%, 55%)", + textSubtlest: "hsl(220, 6%, 42%)", + primary: "hsl(143, 50%, 55%)", + secondary: "hsl(220, 8%, 55%)", + info: "hsl(214, 60%, 65%)", + success: "hsl(143, 50%, 55%)", + notice: "hsl(45, 65%, 65%)", + warning: "hsl(30, 60%, 60%)", + danger: "hsl(355, 60%, 60%)", }, components: { dialog: { - surface: 'hsl(220, 13%, 7%)', + surface: "hsl(220, 13%, 7%)", }, sidebar: { - surface: 'hsl(220, 12%, 8%)', - border: 'hsl(220, 10%, 14%)', + surface: "hsl(220, 12%, 8%)", + border: "hsl(220, 10%, 14%)", }, appHeader: { - surface: 'hsl(220, 13%, 6%)', - border: 'hsl(220, 10%, 12%)', + surface: "hsl(220, 13%, 6%)", + border: "hsl(220, 10%, 12%)", }, responsePane: { - surface: 'hsl(220, 12%, 8%)', - border: 'hsl(220, 10%, 14%)', + surface: "hsl(220, 12%, 8%)", + border: "hsl(220, 10%, 14%)", }, button: { - primary: 'hsl(143, 50%, 48%)', - secondary: 'hsl(220, 8%, 48%)', - info: 'hsl(214, 60%, 58%)', - success: 'hsl(143, 50%, 48%)', - notice: 'hsl(45, 65%, 58%)', - warning: 'hsl(30, 60%, 53%)', - danger: 'hsl(355, 60%, 53%)', + primary: "hsl(143, 50%, 48%)", + secondary: "hsl(220, 8%, 48%)", + info: "hsl(214, 60%, 58%)", + success: "hsl(143, 50%, 48%)", + notice: "hsl(45, 65%, 58%)", + warning: "hsl(30, 60%, 53%)", + danger: "hsl(355, 60%, 53%)", }, }, }; export const vitesseLight: Theme = { - id: 'vitesse-light', - label: 'Vitesse Light', + id: "vitesse-light", + label: "Vitesse Light", dark: false, base: { - surface: 'hsl(0, 0%, 100%)', - surfaceHighlight: 'hsl(40, 20%, 96%)', - text: 'hsl(0, 0%, 24%)', - textSubtle: 'hsl(0, 0%, 45%)', - textSubtlest: 'hsl(0, 0%, 55%)', - primary: 'hsl(143, 40%, 40%)', - secondary: 'hsl(0, 0%, 45%)', - info: 'hsl(214, 50%, 48%)', - success: 'hsl(143, 40%, 40%)', - notice: 'hsl(40, 60%, 42%)', - warning: 'hsl(25, 60%, 48%)', - danger: 'hsl(345, 50%, 48%)', + surface: "hsl(0, 0%, 100%)", + surfaceHighlight: "hsl(40, 20%, 96%)", + text: "hsl(0, 0%, 24%)", + textSubtle: "hsl(0, 0%, 45%)", + textSubtlest: "hsl(0, 0%, 55%)", + primary: "hsl(143, 40%, 40%)", + secondary: "hsl(0, 0%, 45%)", + info: "hsl(214, 50%, 48%)", + success: "hsl(143, 40%, 40%)", + notice: "hsl(40, 60%, 42%)", + warning: "hsl(25, 60%, 48%)", + danger: "hsl(345, 50%, 48%)", }, components: { sidebar: { - surface: 'hsl(40, 20%, 97%)', - border: 'hsl(40, 15%, 92%)', + surface: "hsl(40, 20%, 97%)", + border: "hsl(40, 15%, 92%)", }, appHeader: { - surface: 'hsl(40, 20%, 95%)', - border: 'hsl(40, 15%, 90%)', + surface: "hsl(40, 20%, 95%)", + border: "hsl(40, 15%, 90%)", }, }, }; diff --git a/plugins/themes-yaak/src/themes/winter-is-coming.ts b/plugins/themes-yaak/src/themes/winter-is-coming.ts index 91025041..97409cfd 100644 --- a/plugins/themes-yaak/src/themes/winter-is-coming.ts +++ b/plugins/themes-yaak/src/themes/winter-is-coming.ts @@ -1,47 +1,47 @@ -import type { Theme } from '@yaakapp/api'; +import type { Theme } from "@yaakapp/api"; export const winterIsComing: Theme = { - id: 'winter-is-coming', - label: 'Winter is Coming', + id: "winter-is-coming", + label: "Winter is Coming", dark: true, base: { - surface: 'hsl(216, 50%, 10%)', - surfaceHighlight: 'hsl(216, 40%, 15%)', - text: 'hsl(210, 20%, 88%)', - textSubtle: 'hsl(210, 15%, 60%)', - textSubtlest: 'hsl(210, 10%, 45%)', - primary: 'hsl(176, 85%, 60%)', - secondary: 'hsl(210, 15%, 60%)', - info: 'hsl(210, 65%, 65%)', - success: 'hsl(100, 65%, 55%)', - notice: 'hsl(45, 100%, 65%)', - warning: 'hsl(30, 90%, 55%)', - danger: 'hsl(350, 100%, 65%)', + surface: "hsl(216, 50%, 10%)", + surfaceHighlight: "hsl(216, 40%, 15%)", + text: "hsl(210, 20%, 88%)", + textSubtle: "hsl(210, 15%, 60%)", + textSubtlest: "hsl(210, 10%, 45%)", + primary: "hsl(176, 85%, 60%)", + secondary: "hsl(210, 15%, 60%)", + info: "hsl(210, 65%, 65%)", + success: "hsl(100, 65%, 55%)", + notice: "hsl(45, 100%, 65%)", + warning: "hsl(30, 90%, 55%)", + danger: "hsl(350, 100%, 65%)", }, components: { dialog: { - surface: 'hsl(216, 50%, 7%)', + surface: "hsl(216, 50%, 7%)", }, sidebar: { - surface: 'hsl(216, 45%, 12%)', - border: 'hsl(216, 40%, 17%)', + surface: "hsl(216, 45%, 12%)", + border: "hsl(216, 40%, 17%)", }, appHeader: { - surface: 'hsl(216, 50%, 8%)', - border: 'hsl(216, 40%, 14%)', + surface: "hsl(216, 50%, 8%)", + border: "hsl(216, 40%, 14%)", }, responsePane: { - surface: 'hsl(216, 45%, 12%)', - border: 'hsl(216, 40%, 17%)', + surface: "hsl(216, 45%, 12%)", + border: "hsl(216, 40%, 17%)", }, button: { - primary: 'hsl(176, 85%, 53%)', - secondary: 'hsl(210, 15%, 53%)', - info: 'hsl(210, 65%, 58%)', - success: 'hsl(100, 65%, 48%)', - notice: 'hsl(45, 100%, 58%)', - warning: 'hsl(30, 90%, 48%)', - danger: 'hsl(350, 100%, 58%)', + primary: "hsl(176, 85%, 53%)", + secondary: "hsl(210, 15%, 53%)", + info: "hsl(210, 65%, 58%)", + success: "hsl(100, 65%, 48%)", + notice: "hsl(45, 100%, 58%)", + warning: "hsl(30, 90%, 48%)", + danger: "hsl(350, 100%, 58%)", }, }, }; diff --git a/scripts/create-migration.cjs b/scripts/create-migration.cjs index 30e6d782..b1505a79 100644 --- a/scripts/create-migration.cjs +++ b/scripts/create-migration.cjs @@ -1,7 +1,7 @@ -const fs = require('fs'); -const path = require('path'); -const readline = require('readline'); -const slugify = require('slugify'); +const fs = require("fs"); +const path = require("path"); +const readline = require("readline"); +const slugify = require("slugify"); const rl = readline.createInterface({ input: process.stdin, @@ -11,33 +11,33 @@ const rl = readline.createInterface({ function generateTimestamp() { const now = new Date(); const year = now.getFullYear(); - const month = String(now.getMonth() + 1).padStart(2, '0'); - const day = String(now.getDate()).padStart(2, '0'); - const hours = String(now.getHours()).padStart(2, '0'); - const minutes = String(now.getMinutes()).padStart(2, '0'); - const seconds = String(now.getSeconds()).padStart(2, '0'); + const month = String(now.getMonth() + 1).padStart(2, "0"); + const day = String(now.getDate()).padStart(2, "0"); + const hours = String(now.getHours()).padStart(2, "0"); + const minutes = String(now.getMinutes()).padStart(2, "0"); + const seconds = String(now.getSeconds()).padStart(2, "0"); return `${year}${month}${day}${hours}${minutes}${seconds}`; } async function createMigration() { try { const migrationName = await new Promise((resolve) => { - rl.question('Enter migration name: ', resolve); + rl.question("Enter migration name: ", resolve); }); const timestamp = generateTimestamp(); const fileName = `${timestamp}_${slugify(String(migrationName), { lower: true })}.sql`; - const migrationsDir = path.join(__dirname, '../crates/yaak-models/migrations'); + const migrationsDir = path.join(__dirname, "../crates/yaak-models/migrations"); const filePath = path.join(migrationsDir, fileName); if (!fs.existsSync(migrationsDir)) { fs.mkdirSync(migrationsDir, { recursive: true }); } - fs.writeFileSync(filePath, '-- Add migration SQL here\n'); + fs.writeFileSync(filePath, "-- Add migration SQL here\n"); console.log(`Created migration file: ${fileName}`); } catch (error) { - console.error('Error creating migration:', error); + console.error("Error creating migration:", error); } finally { rl.close(); } diff --git a/scripts/git-hooks/post-checkout.mjs b/scripts/git-hooks/post-checkout.mjs index 55700776..3c96dd5d 100644 --- a/scripts/git-hooks/post-checkout.mjs +++ b/scripts/git-hooks/post-checkout.mjs @@ -10,35 +10,35 @@ * process.argv[4] - flag (1 = branch checkout, 0 = file checkout) */ -import fs from 'fs'; -import path from 'path'; -import { execSync, execFileSync } from 'child_process'; -import { fileURLToPath } from 'url'; +import fs from "fs"; +import path from "path"; +import { execSync, execFileSync } from "child_process"; +import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const isBranchCheckout = process.argv[4] === '1'; +const isBranchCheckout = process.argv[4] === "1"; if (!isBranchCheckout) { process.exit(0); } // Check if we're in a worktree by looking for .git file (not directory) -const gitPath = path.join(process.cwd(), '.git'); +const gitPath = path.join(process.cwd(), ".git"); const isWorktree = fs.existsSync(gitPath) && fs.statSync(gitPath).isFile(); if (!isWorktree) { process.exit(0); } -const envLocalPath = path.join(process.cwd(), '.env.local'); +const envLocalPath = path.join(process.cwd(), ".env.local"); // Don't overwrite existing .env.local if (fs.existsSync(envLocalPath)) { process.exit(0); } -console.log('Detected new worktree - configuring ports in .env.local'); +console.log("Detected new worktree - configuring ports in .env.local"); // Find the highest ports in use across all worktrees // Main worktree (first in list) is assumed to use default ports 1420/64343 @@ -46,19 +46,19 @@ let maxMcpPort = 64343; let maxDevPort = 1420; try { - const worktreeList = execSync('git worktree list --porcelain', { encoding: 'utf8' }); + const worktreeList = execSync("git worktree list --porcelain", { encoding: "utf8" }); const worktreePaths = worktreeList - .split('\n') - .filter(line => line.startsWith('worktree ')) - .map(line => line.replace('worktree ', '').trim()); + .split("\n") + .filter((line) => line.startsWith("worktree ")) + .map((line) => line.replace("worktree ", "").trim()); // Skip the first worktree (main) since it uses default ports for (let i = 1; i < worktreePaths.length; i++) { const worktreePath = worktreePaths[i]; - const envPath = path.join(worktreePath, '.env.local'); + const envPath = path.join(worktreePath, ".env.local"); if (fs.existsSync(envPath)) { - const content = fs.readFileSync(envPath, 'utf8'); + const content = fs.readFileSync(envPath, "utf8"); const mcpMatch = content.match(/^YAAK_PLUGIN_MCP_SERVER_PORT=(\d+)/m); if (mcpMatch) { @@ -82,7 +82,7 @@ try { maxDevPort++; maxMcpPort++; } catch (err) { - console.error('Warning: Could not check other worktrees for port conflicts:', err.message); + console.error("Warning: Could not check other worktrees for port conflicts:", err.message); // Continue with default ports } @@ -100,37 +100,44 @@ YAAK_DEV_PORT=${maxDevPort} YAAK_PLUGIN_MCP_SERVER_PORT=${maxMcpPort} `; -fs.writeFileSync(envLocalPath, envContent, 'utf8'); -console.log(`Created .env.local with YAAK_DEV_PORT=${maxDevPort} and YAAK_PLUGIN_MCP_SERVER_PORT=${maxMcpPort}`); +fs.writeFileSync(envLocalPath, envContent, "utf8"); +console.log( + `Created .env.local with YAAK_DEV_PORT=${maxDevPort} and YAAK_PLUGIN_MCP_SERVER_PORT=${maxMcpPort}`, +); // Create tauri.worktree.conf.json with unique app identifier for complete isolation // This gives each worktree its own app data directory, avoiding the need for DB path prefixes const tauriWorktreeConfig = { identifier: `app.yaak.desktop.dev.${worktreeName}`, - productName: `Daak (${worktreeName})` + productName: `Daak (${worktreeName})`, }; -const tauriConfigPath = path.join(process.cwd(), 'crates-tauri', 'yaak-app', 'tauri.worktree.conf.json'); -fs.writeFileSync(tauriConfigPath, JSON.stringify(tauriWorktreeConfig, null, 2) + '\n', 'utf8'); +const tauriConfigPath = path.join( + process.cwd(), + "crates-tauri", + "yaak-app", + "tauri.worktree.conf.json", +); +fs.writeFileSync(tauriConfigPath, JSON.stringify(tauriWorktreeConfig, null, 2) + "\n", "utf8"); console.log(`Created tauri.worktree.conf.json with identifier: ${tauriWorktreeConfig.identifier}`); // Copy gitignored editor config folders from main worktree (.zed, .vscode, .claude, etc.) // This ensures your editor settings, tasks, and configurations are available in the new worktree // without needing to manually copy them or commit them to git. try { - const worktreeList = execSync('git worktree list --porcelain', { encoding: 'utf8' }); + const worktreeList = execSync("git worktree list --porcelain", { encoding: "utf8" }); const mainWorktreePath = worktreeList - .split('\n') - .find(line => line.startsWith('worktree ')) - ?.replace('worktree ', '') + .split("\n") + .find((line) => line.startsWith("worktree ")) + ?.replace("worktree ", "") .trim(); if (mainWorktreePath) { // Find all .* folders in main worktree root that are gitignored const entries = fs.readdirSync(mainWorktreePath, { withFileTypes: true }); const dotFolders = entries - .filter(entry => entry.isDirectory() && entry.name.startsWith('.')) - .map(entry => entry.name); + .filter((entry) => entry.isDirectory() && entry.name.startsWith(".")) + .map((entry) => entry.name); for (const folder of dotFolders) { const sourcePath = path.join(mainWorktreePath, folder); @@ -138,9 +145,9 @@ try { try { // Check if it's gitignored - run from main worktree directory - execFileSync('git', ['check-ignore', '-q', folder], { - stdio: 'pipe', - cwd: mainWorktreePath + execFileSync("git", ["check-ignore", "-q", folder], { + stdio: "pipe", + cwd: mainWorktreePath, }); // It's gitignored, copy it @@ -152,8 +159,8 @@ try { } } } catch (err) { - console.warn('Warning: Could not copy files from main worktree:', err.message); + console.warn("Warning: Could not copy files from main worktree:", err.message); // Continue anyway } -console.log('\n✓ Worktree setup complete! Run `npm run init` to install dependencies.'); +console.log("\n✓ Worktree setup complete! Run `npm run init` to install dependencies."); diff --git a/scripts/install-wasm-pack.cjs b/scripts/install-wasm-pack.cjs index ab637941..8be4c6a9 100644 --- a/scripts/install-wasm-pack.cjs +++ b/scripts/install-wasm-pack.cjs @@ -1,18 +1,18 @@ -const { execSync } = require('node:child_process'); +const { execSync } = require("node:child_process"); -const version = tryExecSync('wasm-pack --version'); -if (version.startsWith('wasm-pack ')) { - console.log('wasm-pack already installed'); +const version = tryExecSync("wasm-pack --version"); +if (version.startsWith("wasm-pack ")) { + console.log("wasm-pack already installed"); return; } -console.log('Installing wasm-pack via cargo...'); -execSync('cargo install wasm-pack --locked', { stdio: 'inherit' }); +console.log("Installing wasm-pack via cargo..."); +execSync("cargo install wasm-pack --locked", { stdio: "inherit" }); function tryExecSync(cmd) { try { - return execSync(cmd, { stdio: 'pipe' }).toString('utf-8'); + return execSync(cmd, { stdio: "pipe" }).toString("utf-8"); } catch { - return ''; + return ""; } } diff --git a/scripts/publish-core-plugins.cjs b/scripts/publish-core-plugins.cjs index d9a2b5d9..db77b139 100644 --- a/scripts/publish-core-plugins.cjs +++ b/scripts/publish-core-plugins.cjs @@ -1,15 +1,19 @@ -const { readdirSync } = require('node:fs'); -const path = require('node:path'); -const { execSync } = require('node:child_process'); +const { readdirSync } = require("node:fs"); +const path = require("node:path"); +const { execSync } = require("node:child_process"); -const pluginsDir = path.join(__dirname, '..', 'plugins'); +const pluginsDir = path.join(__dirname, "..", "plugins"); -console.log('Publishing core Yaak plugins'); +console.log("Publishing core Yaak plugins"); for (const name of readdirSync(pluginsDir)) { const dir = path.join(pluginsDir, name); - if (name.startsWith('.')) continue; - console.log('Building plugin', dir); - execSync('npm run build', { stdio: 'inherit', cwd: dir }); - execSync('yaakcli publish', { stdio: 'inherit', cwd: dir, env: { ...process.env, ENVIRONMENT: 'development' } }); + if (name.startsWith(".")) continue; + console.log("Building plugin", dir); + execSync("npm run build", { stdio: "inherit", cwd: dir }); + execSync("yaakcli publish", { + stdio: "inherit", + cwd: dir, + env: { ...process.env, ENVIRONMENT: "development" }, + }); } diff --git a/scripts/replace-version.cjs b/scripts/replace-version.cjs index f5bc6edc..ad71f8ad 100644 --- a/scripts/replace-version.cjs +++ b/scripts/replace-version.cjs @@ -1,15 +1,15 @@ -const path = require('path'); -const fs = require('fs'); +const path = require("path"); +const fs = require("fs"); -const version = process.env.YAAK_VERSION?.replace('v', ''); +const version = process.env.YAAK_VERSION?.replace("v", ""); if (!version) { - throw new Error('YAAK_VERSION environment variable not set') + throw new Error("YAAK_VERSION environment variable not set"); } -const tauriConfigPath = path.join(__dirname, '../crates-tauri/yaak-app/tauri.conf.json'); -const tauriConfig = JSON.parse(fs.readFileSync(tauriConfigPath, 'utf8')); +const tauriConfigPath = path.join(__dirname, "../crates-tauri/yaak-app/tauri.conf.json"); +const tauriConfig = JSON.parse(fs.readFileSync(tauriConfigPath, "utf8")); tauriConfig.version = version; -console.log('Writing version ' + version + ' to ' + tauriConfigPath) +console.log("Writing version " + version + " to " + tauriConfigPath); fs.writeFileSync(tauriConfigPath, JSON.stringify(tauriConfig, null, 2)); diff --git a/scripts/run-dev.mjs b/scripts/run-dev.mjs index e6ded1c3..002d303c 100644 --- a/scripts/run-dev.mjs +++ b/scripts/run-dev.mjs @@ -5,23 +5,23 @@ * Loads port from .env.local if present, otherwise uses default port 1420. */ -import { spawnSync } from 'child_process'; -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; +import { spawnSync } from "child_process"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const rootDir = path.join(__dirname, '..'); +const rootDir = path.join(__dirname, ".."); // Load .env.local if it exists -const envLocalPath = path.join(rootDir, '.env.local'); +const envLocalPath = path.join(rootDir, ".env.local"); if (fs.existsSync(envLocalPath)) { - const envContent = fs.readFileSync(envLocalPath, 'utf8'); + const envContent = fs.readFileSync(envLocalPath, "utf8"); const envVars = envContent - .split('\n') - .filter(line => line && !line.startsWith('#')) + .split("\n") + .filter((line) => line && !line.startsWith("#")) .reduce((acc, line) => { - const [key, value] = line.split('='); + const [key, value] = line.split("="); if (key && value) { acc[key.trim()] = value.trim(); } @@ -31,23 +31,28 @@ if (fs.existsSync(envLocalPath)) { Object.assign(process.env, envVars); } -const port = process.env.YAAK_DEV_PORT || '1420'; +const port = process.env.YAAK_DEV_PORT || "1420"; const config = JSON.stringify({ build: { devUrl: `http://localhost:${port}` } }); // Get additional arguments passed after npm run app-dev -- const additionalArgs = process.argv.slice(2); const args = [ - 'dev', - '--no-watch', - '--config', 'crates-tauri/yaak-app/tauri.development.conf.json', - '--config', config, - ...additionalArgs + "dev", + "--no-watch", + "--config", + "crates-tauri/yaak-app/tauri.development.conf.json", + "--config", + config, + ...additionalArgs, ]; // Invoke the tauri CLI JS entry point directly via node to avoid shell escaping issues on Windows -const tauriJs = path.join(rootDir, 'node_modules', '@tauri-apps', 'cli', 'tauri.js'); +const tauriJs = path.join(rootDir, "node_modules", "@tauri-apps", "cli", "tauri.js"); -const result = spawnSync(process.execPath, [tauriJs, ...args], { stdio: 'inherit', env: process.env }); +const result = spawnSync(process.execPath, [tauriJs, ...args], { + stdio: "inherit", + env: process.env, +}); process.exit(result.status || 0); diff --git a/scripts/run-workspaces-dev.mjs b/scripts/run-workspaces-dev.mjs index 72070fbc..ce4faee9 100644 --- a/scripts/run-workspaces-dev.mjs +++ b/scripts/run-workspaces-dev.mjs @@ -5,28 +5,28 @@ * Handles cleanup of child processes on exit. */ -import { spawn } from 'child_process'; -import fs from 'fs'; -import path from 'path'; -import { fileURLToPath } from 'url'; +import { spawn } from "child_process"; +import fs from "fs"; +import path from "path"; +import { fileURLToPath } from "url"; const __dirname = path.dirname(fileURLToPath(import.meta.url)); -const rootDir = path.join(__dirname, '..'); +const rootDir = path.join(__dirname, ".."); // Read root package.json to get workspaces -const rootPkg = JSON.parse(fs.readFileSync(path.join(rootDir, 'package.json'), 'utf8')); +const rootPkg = JSON.parse(fs.readFileSync(path.join(rootDir, "package.json"), "utf8")); const workspaces = rootPkg.workspaces || []; // Find all workspaces with a dev script const workspacesWithDev = workspaces.filter((ws) => { - const pkgPath = path.join(rootDir, ws, 'package.json'); + const pkgPath = path.join(rootDir, ws, "package.json"); if (!fs.existsSync(pkgPath)) return false; - const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8")); return pkg.scripts?.dev != null; }); if (workspacesWithDev.length === 0) { - console.log('No workspaces with dev script found'); + console.log("No workspaces with dev script found"); process.exit(0); } @@ -37,13 +37,13 @@ const children = []; // Spawn all dev processes for (const ws of workspacesWithDev) { const cwd = path.join(rootDir, ws); - const child = spawn('npm', ['run', 'dev'], { + const child = spawn("npm", ["run", "dev"], { cwd, - stdio: 'inherit', - shell: process.platform === 'win32', + stdio: "inherit", + shell: process.platform === "win32", }); - child.on('error', (err) => { + child.on("error", (err) => { console.error(`Error in ${ws}:`, err.message); }); @@ -55,27 +55,27 @@ function cleanup() { for (const { child } of children) { if (child.exitCode === null) { // Process still running - if (process.platform === 'win32') { - spawn('taskkill', ['/pid', child.pid, '/f', '/t'], { shell: true }); + if (process.platform === "win32") { + spawn("taskkill", ["/pid", child.pid, "/f", "/t"], { shell: true }); } else { - child.kill('SIGTERM'); + child.kill("SIGTERM"); } } } } // Handle various exit signals -process.on('SIGINT', () => { +process.on("SIGINT", () => { cleanup(); process.exit(0); }); -process.on('SIGTERM', () => { +process.on("SIGTERM", () => { cleanup(); process.exit(0); }); -process.on('exit', cleanup); +process.on("exit", cleanup); // Keep the process running process.stdin.resume(); diff --git a/scripts/vendor-node.cjs b/scripts/vendor-node.cjs index 1d7b180b..d40eab02 100644 --- a/scripts/vendor-node.cjs +++ b/scripts/vendor-node.cjs @@ -1,20 +1,20 @@ -const path = require('node:path'); -const crypto = require('node:crypto'); -const fs = require('node:fs'); -const decompress = require('decompress'); -const Downloader = require('nodejs-file-downloader'); -const { rmSync, cpSync, mkdirSync, existsSync } = require('node:fs'); -const { execSync } = require('node:child_process'); +const path = require("node:path"); +const crypto = require("node:crypto"); +const fs = require("node:fs"); +const decompress = require("decompress"); +const Downloader = require("nodejs-file-downloader"); +const { rmSync, cpSync, mkdirSync, existsSync } = require("node:fs"); +const { execSync } = require("node:child_process"); -const NODE_VERSION = 'v24.11.1'; +const NODE_VERSION = "v24.11.1"; // `${process.platform}_${process.arch}` -const MAC_ARM = 'darwin_arm64'; -const MAC_X64 = 'darwin_x64'; -const LNX_ARM = 'linux_arm64'; -const LNX_X64 = 'linux_x64'; -const WIN_X64 = 'win32_x64'; -const WIN_ARM = 'win32_arm64'; +const MAC_ARM = "darwin_arm64"; +const MAC_X64 = "darwin_x64"; +const LNX_ARM = "linux_arm64"; +const LNX_X64 = "linux_x64"; +const WIN_X64 = "win32_x64"; +const WIN_ARM = "win32_arm64"; const URL_MAP = { [MAC_ARM]: `https://nodejs.org/download/release/${NODE_VERSION}/node-${NODE_VERSION}-darwin-arm64.tar.gz`, @@ -35,31 +35,31 @@ const SRC_BIN_MAP = { }; const DST_BIN_MAP = { - [MAC_ARM]: 'yaaknode', - [MAC_X64]: 'yaaknode', - [LNX_ARM]: 'yaaknode', - [LNX_X64]: 'yaaknode', - [WIN_X64]: 'yaaknode.exe', - [WIN_ARM]: 'yaaknode.exe', + [MAC_ARM]: "yaaknode", + [MAC_X64]: "yaaknode", + [LNX_ARM]: "yaaknode", + [LNX_X64]: "yaaknode", + [WIN_X64]: "yaaknode.exe", + [WIN_ARM]: "yaaknode.exe", }; const SHA256_MAP = { - [MAC_ARM]: 'b05aa3a66efe680023f930bd5af3fdbbd542794da5644ca2ad711d68cbd4dc35', - [MAC_X64]: '096081b6d6fcdd3f5ba0f5f1d44a47e83037ad2e78eada26671c252fe64dd111', - [LNX_ARM]: '0dc93ec5c798b0d347f068db6d205d03dea9a71765e6a53922b682b91265d71f', - [LNX_X64]: '58a5ff5cc8f2200e458bea22e329d5c1994aa1b111d499ca46ec2411d58239ca', - [WIN_X64]: '5355ae6d7c49eddcfde7d34ac3486820600a831bf81dc3bdca5c8db6a9bb0e76', - [WIN_ARM]: 'ce9ee4e547ebdff355beb48e309b166c24df6be0291c9eaf103ce15f3de9e5b4', + [MAC_ARM]: "b05aa3a66efe680023f930bd5af3fdbbd542794da5644ca2ad711d68cbd4dc35", + [MAC_X64]: "096081b6d6fcdd3f5ba0f5f1d44a47e83037ad2e78eada26671c252fe64dd111", + [LNX_ARM]: "0dc93ec5c798b0d347f068db6d205d03dea9a71765e6a53922b682b91265d71f", + [LNX_X64]: "58a5ff5cc8f2200e458bea22e329d5c1994aa1b111d499ca46ec2411d58239ca", + [WIN_X64]: "5355ae6d7c49eddcfde7d34ac3486820600a831bf81dc3bdca5c8db6a9bb0e76", + [WIN_ARM]: "ce9ee4e547ebdff355beb48e309b166c24df6be0291c9eaf103ce15f3de9e5b4", }; const key = `${process.platform}_${process.env.YAAK_TARGET_ARCH ?? process.arch}`; -const destDir = path.join(__dirname, `..`, 'crates-tauri', 'yaak-app', 'vendored', 'node'); +const destDir = path.join(__dirname, `..`, "crates-tauri", "yaak-app", "vendored", "node"); const binDest = path.join(destDir, DST_BIN_MAP[key]); console.log(`Vendoring NodeJS ${NODE_VERSION} for ${key}`); if (existsSync(binDest) && tryExecSync(`${binDest} --version`).trim() === NODE_VERSION) { - console.log('NodeJS already vendored'); + console.log("NodeJS already vendored"); return; } @@ -67,12 +67,12 @@ rmSync(destDir, { recursive: true, force: true }); mkdirSync(destDir, { recursive: true }); const url = URL_MAP[key]; -const tmpDir = path.join(__dirname, 'tmp-node'); +const tmpDir = path.join(__dirname, "tmp-node"); rmSync(tmpDir, { recursive: true, force: true }); (async function () { // Download GitHub release artifact - console.log('Downloading NodeJS at', url); + console.log("Downloading NodeJS at", url); const { filePath } = await new Downloader({ url, directory: tmpDir, @@ -82,11 +82,13 @@ rmSync(tmpDir, { recursive: true, force: true }); // Verify SHA256 const expectedHash = SHA256_MAP[key]; const fileBuffer = fs.readFileSync(filePath); - const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex'); + const actualHash = crypto.createHash("sha256").update(fileBuffer).digest("hex"); if (actualHash !== expectedHash) { - throw new Error(`SHA256 mismatch for ${path.basename(filePath)}\n expected: ${expectedHash}\n actual: ${actualHash}`); + throw new Error( + `SHA256 mismatch for ${path.basename(filePath)}\n expected: ${expectedHash}\n actual: ${actualHash}`, + ); } - console.log('SHA256 verified:', actualHash); + console.log("SHA256 verified:", actualHash); // Decompress to the same directory await decompress(filePath, tmpDir, {}); @@ -96,16 +98,16 @@ rmSync(tmpDir, { recursive: true, force: true }); cpSync(binSrc, binDest); rmSync(tmpDir, { recursive: true, force: true }); - console.log('Downloaded NodeJS to', binDest); + console.log("Downloaded NodeJS to", binDest); })().catch((err) => { - console.log('Script failed:', err); + console.log("Script failed:", err); process.exit(1); }); function tryExecSync(cmd) { try { - return execSync(cmd, { stdio: 'pipe' }).toString('utf-8'); + return execSync(cmd, { stdio: "pipe" }).toString("utf-8"); } catch { - return ''; + return ""; } } diff --git a/scripts/vendor-plugins.cjs b/scripts/vendor-plugins.cjs index 6b900929..1a8afff0 100644 --- a/scripts/vendor-plugins.cjs +++ b/scripts/vendor-plugins.cjs @@ -1,31 +1,31 @@ -const { readdirSync, cpSync, existsSync, mkdirSync } = require('node:fs'); -const path = require('node:path'); +const { readdirSync, cpSync, existsSync, mkdirSync } = require("node:fs"); +const path = require("node:path"); -const pluginsDir = path.join(__dirname, '..', 'plugins'); -const externalPluginsDir = path.join(__dirname, '..', 'plugins-external'); +const pluginsDir = path.join(__dirname, "..", "plugins"); +const externalPluginsDir = path.join(__dirname, "..", "plugins-external"); // Get list of external (non-bundled) plugins const externalPlugins = new Set(); if (existsSync(externalPluginsDir)) { for (const name of readdirSync(externalPluginsDir)) { - if (!name.startsWith('.')) { + if (!name.startsWith(".")) { externalPlugins.add(name); } } } -console.log('Copying Yaak plugins to', pluginsDir); +console.log("Copying Yaak plugins to", pluginsDir); for (const name of readdirSync(pluginsDir)) { const dir = path.join(pluginsDir, name); - if (name.startsWith('.')) continue; + if (name.startsWith(".")) continue; if (externalPlugins.has(name)) { console.log(`Skipping ${name} (external plugin)`); continue; } - const destDir = path.join(__dirname, '../crates-tauri/yaak-app/vendored/plugins/', name); + const destDir = path.join(__dirname, "../crates-tauri/yaak-app/vendored/plugins/", name); mkdirSync(destDir, { recursive: true }); console.log(`Copying ${name} to ${destDir}`); - cpSync(path.join(dir, 'package.json'), path.join(destDir, 'package.json')); - cpSync(path.join(dir, 'build'), path.join(destDir, 'build'), { recursive: true }); + cpSync(path.join(dir, "package.json"), path.join(destDir, "package.json")); + cpSync(path.join(dir, "build"), path.join(destDir, "build"), { recursive: true }); } diff --git a/scripts/vendor-protoc.cjs b/scripts/vendor-protoc.cjs index 0d0deb49..a8787a66 100644 --- a/scripts/vendor-protoc.cjs +++ b/scripts/vendor-protoc.cjs @@ -1,20 +1,20 @@ -const crypto = require('node:crypto'); -const fs = require('node:fs'); -const decompress = require('decompress'); -const Downloader = require('nodejs-file-downloader'); -const path = require('node:path'); -const { rmSync, mkdirSync, cpSync, existsSync, statSync, chmodSync } = require('node:fs'); -const { execSync } = require('node:child_process'); +const crypto = require("node:crypto"); +const fs = require("node:fs"); +const decompress = require("decompress"); +const Downloader = require("nodejs-file-downloader"); +const path = require("node:path"); +const { rmSync, mkdirSync, cpSync, existsSync, statSync, chmodSync } = require("node:fs"); +const { execSync } = require("node:child_process"); -const VERSION = '33.1'; +const VERSION = "33.1"; // `${process.platform}_${process.arch}` -const MAC_ARM = 'darwin_arm64'; -const MAC_X64 = 'darwin_x64'; -const LNX_ARM = 'linux_arm64'; -const LNX_X64 = 'linux_x64'; -const WIN_X64 = 'win32_x64'; -const WIN_ARM = 'win32_arm64'; +const MAC_ARM = "darwin_arm64"; +const MAC_X64 = "darwin_x64"; +const LNX_ARM = "linux_arm64"; +const LNX_X64 = "linux_x64"; +const WIN_X64 = "win32_x64"; +const WIN_ARM = "win32_arm64"; const URL_MAP = { [MAC_ARM]: `https://github.com/protocolbuffers/protobuf/releases/download/v${VERSION}/protoc-${VERSION}-osx-aarch_64.zip`, @@ -26,43 +26,43 @@ const URL_MAP = { }; const SRC_BIN_MAP = { - [MAC_ARM]: 'bin/protoc', - [MAC_X64]: 'bin/protoc', - [LNX_ARM]: 'bin/protoc', - [LNX_X64]: 'bin/protoc', - [WIN_X64]: 'bin/protoc.exe', - [WIN_ARM]: 'bin/protoc.exe', + [MAC_ARM]: "bin/protoc", + [MAC_X64]: "bin/protoc", + [LNX_ARM]: "bin/protoc", + [LNX_X64]: "bin/protoc", + [WIN_X64]: "bin/protoc.exe", + [WIN_ARM]: "bin/protoc.exe", }; const DST_BIN_MAP = { - [MAC_ARM]: 'yaakprotoc', - [MAC_X64]: 'yaakprotoc', - [LNX_ARM]: 'yaakprotoc', - [LNX_X64]: 'yaakprotoc', - [WIN_X64]: 'yaakprotoc.exe', - [WIN_ARM]: 'yaakprotoc.exe', + [MAC_ARM]: "yaakprotoc", + [MAC_X64]: "yaakprotoc", + [LNX_ARM]: "yaakprotoc", + [LNX_X64]: "yaakprotoc", + [WIN_X64]: "yaakprotoc.exe", + [WIN_ARM]: "yaakprotoc.exe", }; const SHA256_MAP = { - [MAC_ARM]: 'db7e66ff7f9080614d0f5505a6b0ac488cf89a15621b6a361672d1332ec2e14e', - [MAC_X64]: 'e20b5f930e886da85e7402776a4959efb1ed60c57e72794bcade765e67abaa82', - [LNX_ARM]: '6018147740548e0e0f764408c87f4cd040e6e1c1203e13aeacaf811892b604f3', - [LNX_X64]: 'f3340e28a83d1c637d8bafdeed92b9f7db6a384c26bca880a6e5217b40a4328b', - [WIN_X64]: 'd7a207fb6eec0e4b1b6613be3b7d11905375b6fd1147a071116eb8e9f24ac53b', - [WIN_ARM]: 'd7a207fb6eec0e4b1b6613be3b7d11905375b6fd1147a071116eb8e9f24ac53b', + [MAC_ARM]: "db7e66ff7f9080614d0f5505a6b0ac488cf89a15621b6a361672d1332ec2e14e", + [MAC_X64]: "e20b5f930e886da85e7402776a4959efb1ed60c57e72794bcade765e67abaa82", + [LNX_ARM]: "6018147740548e0e0f764408c87f4cd040e6e1c1203e13aeacaf811892b604f3", + [LNX_X64]: "f3340e28a83d1c637d8bafdeed92b9f7db6a384c26bca880a6e5217b40a4328b", + [WIN_X64]: "d7a207fb6eec0e4b1b6613be3b7d11905375b6fd1147a071116eb8e9f24ac53b", + [WIN_ARM]: "d7a207fb6eec0e4b1b6613be3b7d11905375b6fd1147a071116eb8e9f24ac53b", }; -const dstDir = path.join(__dirname, `..`, 'crates-tauri', 'yaak-app', 'vendored', 'protoc'); +const dstDir = path.join(__dirname, `..`, "crates-tauri", "yaak-app", "vendored", "protoc"); const key = `${process.platform}_${process.env.YAAK_TARGET_ARCH ?? process.arch}`; console.log(`Vendoring protoc ${VERSION} for ${key}`); const url = URL_MAP[key]; -const tmpDir = path.join(__dirname, 'tmp-protoc'); +const tmpDir = path.join(__dirname, "tmp-protoc"); const binSrc = path.join(tmpDir, SRC_BIN_MAP[key]); const binDst = path.join(dstDir, DST_BIN_MAP[key]); if (existsSync(binDst) && tryExecSync(`${binDst} --version`).trim().includes(VERSION)) { - console.log('Protoc already vendored'); + console.log("Protoc already vendored"); return; } @@ -77,11 +77,13 @@ mkdirSync(dstDir, { recursive: true }); // Verify SHA256 const expectedHash = SHA256_MAP[key]; const fileBuffer = fs.readFileSync(filePath); - const actualHash = crypto.createHash('sha256').update(fileBuffer).digest('hex'); + const actualHash = crypto.createHash("sha256").update(fileBuffer).digest("hex"); if (actualHash !== expectedHash) { - throw new Error(`SHA256 mismatch for ${path.basename(filePath)}\n expected: ${expectedHash}\n actual: ${actualHash}`); + throw new Error( + `SHA256 mismatch for ${path.basename(filePath)}\n expected: ${expectedHash}\n actual: ${actualHash}`, + ); } - console.log('SHA256 verified:', actualHash); + console.log("SHA256 verified:", actualHash); // Decompress to the same directory await decompress(filePath, tmpDir, {}); @@ -90,8 +92,8 @@ mkdirSync(dstDir, { recursive: true }); cpSync(binSrc, binDst); // Copy other files - const includeSrc = path.join(tmpDir, 'include'); - const includeDst = path.join(dstDir, 'include'); + const includeSrc = path.join(tmpDir, "include"); + const includeDst = path.join(dstDir, "include"); cpSync(includeSrc, includeDst, { recursive: true }); rmSync(tmpDir, { recursive: true, force: true }); @@ -100,13 +102,13 @@ mkdirSync(dstDir, { recursive: true }); const newMode = stat.mode | 0o200; chmodSync(binDst, newMode); - console.log('Downloaded protoc to', binDst); -})().catch((err) => console.log('Script failed:', err)); + console.log("Downloaded protoc to", binDst); +})().catch((err) => console.log("Script failed:", err)); function tryExecSync(cmd) { try { - return execSync(cmd, { stdio: 'pipe' }).toString('utf-8'); + return execSync(cmd, { stdio: "pipe" }).toString("utf-8"); } catch { - return ''; + return ""; } } diff --git a/src-web/commands/commands.tsx b/src-web/commands/commands.tsx index 846ec262..5a44d67d 100644 --- a/src-web/commands/commands.tsx +++ b/src-web/commands/commands.tsx @@ -1,8 +1,8 @@ -import { createWorkspaceModel, type Folder, modelTypeLabel } from '@yaakapp-internal/models'; -import { applySync, calculateSync } from '@yaakapp-internal/sync'; -import { Banner } from '../components/core/Banner'; -import { Button } from '../components/core/Button'; -import { InlineCode } from '../components/core/InlineCode'; +import { createWorkspaceModel, type Folder, modelTypeLabel } from "@yaakapp-internal/models"; +import { applySync, calculateSync } from "@yaakapp-internal/sync"; +import { Banner } from "../components/core/Banner"; +import { Button } from "../components/core/Button"; +import { InlineCode } from "../components/core/InlineCode"; import { Table, TableBody, @@ -11,21 +11,21 @@ import { TableHeaderCell, TableRow, TruncatedWideTableCell, -} from '../components/core/Table'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; -import { pluralizeCount } from '../lib/pluralize'; -import { showPrompt } from '../lib/prompt'; -import { resolvedModelNameWithFolders } from '../lib/resolvedModelName'; +} from "../components/core/Table"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; +import { pluralizeCount } from "../lib/pluralize"; +import { showPrompt } from "../lib/prompt"; +import { resolvedModelNameWithFolders } from "../lib/resolvedModelName"; export const createFolder = createFastMutation< string | null, void, - Partial> + Partial> >({ - mutationKey: ['create_folder'], + mutationKey: ["create_folder"], mutationFn: async (patch) => { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) { @@ -34,12 +34,12 @@ export const createFolder = createFastMutation< if (!patch.name) { const name = await showPrompt({ - id: 'new-folder', - label: 'Name', - defaultValue: 'Folder', - title: 'New Folder', - confirmText: 'Create', - placeholder: 'Name', + id: "new-folder", + label: "Name", + defaultValue: "Folder", + title: "New Folder", + confirmText: "Create", + placeholder: "Name", }); if (name == null) return null; @@ -47,7 +47,7 @@ export const createFolder = createFastMutation< } patch.sortPriority = patch.sortPriority || -Date.now(); - const id = await createWorkspaceModel({ model: 'folder', workspaceId, ...patch }); + const id = await createWorkspaceModel({ model: "folder", workspaceId, ...patch }); return id; }, }); @@ -61,12 +61,12 @@ export const syncWorkspace = createFastMutation< mutationFn: async ({ workspaceId, syncDir, force }) => { const ops = (await calculateSync(workspaceId, syncDir)) ?? []; if (ops.length === 0) { - console.log('Nothing to sync', workspaceId, syncDir); + console.log("Nothing to sync", workspaceId, syncDir); return; } - console.log('Syncing workspace', workspaceId, syncDir, ops); + console.log("Syncing workspace", workspaceId, syncDir, ops); - const dbOps = ops.filter((o) => o.type.startsWith('db')); + const dbOps = ops.filter((o) => o.type.startsWith("db")); if (dbOps.length === 0) { await applySync(workspaceId, syncDir, ops); @@ -74,10 +74,10 @@ export const syncWorkspace = createFastMutation< } const isDeletingWorkspace = ops.some( - (o) => o.type === 'dbDelete' && o.model.model === 'workspace', + (o) => o.type === "dbDelete" && o.model.model === "workspace", ); - console.log('Directory changes detected', { dbOps, ops }); + console.log("Directory changes detected", { dbOps, ops }); if (force) { await applySync(workspaceId, syncDir, ops); @@ -85,9 +85,9 @@ export const syncWorkspace = createFastMutation< } showDialog({ - id: 'commit-sync', - title: 'Changes Detected', - size: 'md', + id: "commit-sync", + title: "Changes Detected", + size: "md", render: ({ hide }) => (
)}

- {pluralizeCount('file', dbOps.length)} in the directory{' '} - {dbOps.length === 1 ? 'has' : 'have'} changed. Do you want to update your workspace? + {pluralizeCount("file", dbOps.length)} in the directory{" "} + {dbOps.length === 1 ? "has" : "have"} changed. Do you want to update your workspace?

@@ -123,20 +123,20 @@ export const syncWorkspace = createFastMutation< let color: string; let model: string; - if (op.type === 'dbCreate') { - label = 'create'; + if (op.type === "dbCreate") { + label = "create"; name = resolvedModelNameWithFolders(op.fs.model); - color = 'text-success'; + color = "text-success"; model = modelTypeLabel(op.fs.model); - } else if (op.type === 'dbUpdate') { - label = 'update'; + } else if (op.type === "dbUpdate") { + label = "update"; name = resolvedModelNameWithFolders(op.fs.model); - color = 'text-info'; + color = "text-info"; model = modelTypeLabel(op.fs.model); - } else if (op.type === 'dbDelete') { - label = 'delete'; + } else if (op.type === "dbDelete") { + label = "delete"; name = resolvedModelNameWithFolders(op.model); - color = 'text-danger'; + color = "text-danger"; model = modelTypeLabel(op.model); } else { return null; diff --git a/src-web/commands/createEnvironment.tsx b/src-web/commands/createEnvironment.tsx index 1dcc5675..80f68e10 100644 --- a/src-web/commands/createEnvironment.tsx +++ b/src-web/commands/createEnvironment.tsx @@ -1,33 +1,33 @@ -import type { Environment } from '@yaakapp-internal/models'; -import { CreateEnvironmentDialog } from '../components/CreateEnvironmentDialog'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; -import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams'; +import type { Environment } from "@yaakapp-internal/models"; +import { CreateEnvironmentDialog } from "../components/CreateEnvironmentDialog"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; +import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams"; export const createSubEnvironmentAndActivate = createFastMutation< string | null, unknown, Environment | null >({ - mutationKey: ['create_environment'], + mutationKey: ["create_environment"], mutationFn: async (baseEnvironment) => { if (baseEnvironment == null) { - throw new Error('No base environment passed'); + throw new Error("No base environment passed"); } const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) { - throw new Error('Cannot create environment when no active workspace'); + throw new Error("Cannot create environment when no active workspace"); } return new Promise((resolve) => { showDialog({ - id: 'new-environment', - title: 'New Environment', - description: 'Create multiple environments with different sets of variables', - size: 'sm', + id: "new-environment", + title: "New Environment", + description: "Create multiple environments with different sets of variables", + size: "sm", onClose: () => resolve(null), render: ({ hide }) => ( cmdDeleteWebsocketConnections(request.id), }); diff --git a/src-web/commands/moveToWorkspace.tsx b/src-web/commands/moveToWorkspace.tsx index 63672b4f..3428a580 100644 --- a/src-web/commands/moveToWorkspace.tsx +++ b/src-web/commands/moveToWorkspace.tsx @@ -1,28 +1,26 @@ -import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; +import type { GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; -import { MoveToWorkspaceDialog } from '../components/MoveToWorkspaceDialog'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { pluralizeCount } from '../lib/pluralize'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; +import { MoveToWorkspaceDialog } from "../components/MoveToWorkspaceDialog"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { pluralizeCount } from "../lib/pluralize"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; export const moveToWorkspace = createFastMutation({ - mutationKey: ['move_workspace'], + mutationKey: ["move_workspace"], mutationFn: async (requests: (HttpRequest | GrpcRequest | WebsocketRequest)[]) => { const activeWorkspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (activeWorkspaceId == null) return; if (requests.length === 0) return; const title = - requests.length === 1 - ? 'Move Request' - : `Move ${pluralizeCount('Request', requests.length)}`; + requests.length === 1 ? "Move Request" : `Move ${pluralizeCount("Request", requests.length)}`; showDialog({ - id: 'change-workspace', + id: "change-workspace", title, - size: 'sm', + size: "sm", render: ({ hide }) => ( , }); diff --git a/src-web/commands/openSettings.tsx b/src-web/commands/openSettings.tsx index 099e4bf1..0db60659 100644 --- a/src-web/commands/openSettings.tsx +++ b/src-web/commands/openSettings.tsx @@ -1,29 +1,29 @@ -import type { SettingsTab } from '../components/Settings/Settings'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { jotaiStore } from '../lib/jotai'; -import { router } from '../lib/router'; -import { invokeCmd } from '../lib/tauri'; +import type { SettingsTab } from "../components/Settings/Settings"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { jotaiStore } from "../lib/jotai"; +import { router } from "../lib/router"; +import { invokeCmd } from "../lib/tauri"; // Allow tab with optional subtab (e.g., "plugins:installed") type SettingsTabWithSubtab = SettingsTab | `${SettingsTab}:${string}` | null; export const openSettings = createFastMutation({ - mutationKey: ['open_settings'], + mutationKey: ["open_settings"], mutationFn: async (tab) => { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) return; const location = router.buildLocation({ - to: '/workspaces/$workspaceId/settings', + to: "/workspaces/$workspaceId/settings", params: { workspaceId }, search: { tab: (tab ?? undefined) as SettingsTab | undefined }, }); - await invokeCmd('cmd_new_child_window', { + await invokeCmd("cmd_new_child_window", { url: location.href, - label: 'settings', - title: 'Yaak Settings', + label: "settings", + title: "Yaak Settings", innerSize: [750, 600], }); }, diff --git a/src-web/commands/openWorkspaceFromSyncDir.tsx b/src-web/commands/openWorkspaceFromSyncDir.tsx index 1c1f10e4..9c5b6d2f 100644 --- a/src-web/commands/openWorkspaceFromSyncDir.tsx +++ b/src-web/commands/openWorkspaceFromSyncDir.tsx @@ -1,7 +1,7 @@ -import { applySync, calculateSyncFsOnly } from '@yaakapp-internal/sync'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { showSimpleAlert } from '../lib/alert'; -import { router } from '../lib/router'; +import { applySync, calculateSyncFsOnly } from "@yaakapp-internal/sync"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { showSimpleAlert } from "../lib/alert"; +import { router } from "../lib/router"; export const openWorkspaceFromSyncDir = createFastMutation({ mutationKey: [], @@ -9,18 +9,18 @@ export const openWorkspaceFromSyncDir = createFastMutation({ const ops = await calculateSyncFsOnly(dir); const workspace = ops - .map((o) => (o.type === 'dbCreate' && o.fs.model.type === 'workspace' ? o.fs.model : null)) + .map((o) => (o.type === "dbCreate" && o.fs.model.type === "workspace" ? o.fs.model : null)) .filter((m) => m)[0]; if (workspace == null) { - showSimpleAlert('Failed to Open', 'No workspace found in directory'); + showSimpleAlert("Failed to Open", "No workspace found in directory"); return; } await applySync(workspace.id, dir, ops); await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: workspace.id }, }); }, diff --git a/src-web/commands/openWorkspaceSettings.tsx b/src-web/commands/openWorkspaceSettings.tsx index 2ad53fbe..4f68f87c 100644 --- a/src-web/commands/openWorkspaceSettings.tsx +++ b/src-web/commands/openWorkspaceSettings.tsx @@ -1,16 +1,16 @@ -import type { WorkspaceSettingsTab } from '../components/WorkspaceSettingsDialog'; -import { WorkspaceSettingsDialog } from '../components/WorkspaceSettingsDialog'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { showDialog } from '../lib/dialog'; -import { jotaiStore } from '../lib/jotai'; +import type { WorkspaceSettingsTab } from "../components/WorkspaceSettingsDialog"; +import { WorkspaceSettingsDialog } from "../components/WorkspaceSettingsDialog"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { showDialog } from "../lib/dialog"; +import { jotaiStore } from "../lib/jotai"; export function openWorkspaceSettings(tab?: WorkspaceSettingsTab) { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); if (workspaceId == null) return; showDialog({ - id: 'workspace-settings', - size: 'md', - className: 'h-[calc(100vh-5rem)] !max-h-[40rem]', + id: "workspace-settings", + size: "md", + className: "h-[calc(100vh-5rem)] !max-h-[40rem]", noPadding: true, render: ({ hide }) => ( diff --git a/src-web/commands/switchWorkspace.tsx b/src-web/commands/switchWorkspace.tsx index 461b4922..56356148 100644 --- a/src-web/commands/switchWorkspace.tsx +++ b/src-web/commands/switchWorkspace.tsx @@ -1,9 +1,9 @@ -import { createFastMutation } from '../hooks/useFastMutation'; -import { getRecentCookieJars } from '../hooks/useRecentCookieJars'; -import { getRecentEnvironments } from '../hooks/useRecentEnvironments'; -import { getRecentRequests } from '../hooks/useRecentRequests'; -import { router } from '../lib/router'; -import { invokeCmd } from '../lib/tauri'; +import { createFastMutation } from "../hooks/useFastMutation"; +import { getRecentCookieJars } from "../hooks/useRecentCookieJars"; +import { getRecentEnvironments } from "../hooks/useRecentEnvironments"; +import { getRecentRequests } from "../hooks/useRecentRequests"; +import { router } from "../lib/router"; +import { invokeCmd } from "../lib/tauri"; export const switchWorkspace = createFastMutation< void, @@ -13,7 +13,7 @@ export const switchWorkspace = createFastMutation< inNewWindow: boolean; } >({ - mutationKey: ['open_workspace'], + mutationKey: ["open_workspace"], mutationFn: async ({ workspaceId, inNewWindow }) => { const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? undefined; const requestId = (await getRecentRequests(workspaceId))[0] ?? undefined; @@ -26,16 +26,16 @@ export const switchWorkspace = createFastMutation< if (inNewWindow) { const location = router.buildLocation({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId }, search, }); - await invokeCmd('cmd_new_main_window', { url: location.href }); + await invokeCmd("cmd_new_main_window", { url: location.href }); return; } await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId }, search, }); diff --git a/src-web/components/BinaryFileEditor.tsx b/src-web/components/BinaryFileEditor.tsx index 87676dda..a4a80d3d 100644 --- a/src-web/components/BinaryFileEditor.tsx +++ b/src-web/components/BinaryFileEditor.tsx @@ -1,17 +1,17 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import mime from 'mime'; -import { useKeyValue } from '../hooks/useKeyValue'; -import { Banner } from './core/Banner'; -import { Button } from './core/Button'; -import { InlineCode } from './core/InlineCode'; -import { HStack, VStack } from './core/Stacks'; -import { SelectFile } from './SelectFile'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import mime from "mime"; +import { useKeyValue } from "../hooks/useKeyValue"; +import { Banner } from "./core/Banner"; +import { Button } from "./core/Button"; +import { InlineCode } from "./core/InlineCode"; +import { HStack, VStack } from "./core/Stacks"; +import { SelectFile } from "./SelectFile"; type Props = { requestId: string; contentType: string | null; - body: HttpRequest['body']; - onChange: (body: HttpRequest['body']) => void; + body: HttpRequest["body"]; + onChange: (body: HttpRequest["body"]) => void; onChangeContentType: (contentType: string | null) => void; }; @@ -23,8 +23,8 @@ export function BinaryFileEditor({ requestId, }: Props) { const ignoreContentType = useKeyValue({ - namespace: 'global', - key: ['ignore_content_type', requestId], + namespace: "global", + key: ["ignore_content_type", requestId], fallback: false, }); @@ -33,8 +33,8 @@ export function BinaryFileEditor({ onChange({ filePath: filePath ?? undefined }); }; - const filePath = typeof body.filePath === 'string' ? body.filePath : null; - const mimeType = mime.getType(filePath ?? '') ?? 'application/octet-stream'; + const filePath = typeof body.filePath === "string" ? body.filePath : null; + const mimeType = mime.getType(filePath ?? "") ?? "application/octet-stream"; return ( diff --git a/src-web/components/CargoFeature.tsx b/src-web/components/CargoFeature.tsx index 0186c35b..7a5d1fe5 100644 --- a/src-web/components/CargoFeature.tsx +++ b/src-web/components/CargoFeature.tsx @@ -1,12 +1,12 @@ -import type { ReactNode } from 'react'; -import { appInfo } from '../lib/appInfo'; +import type { ReactNode } from "react"; +import { appInfo } from "../lib/appInfo"; interface Props { children: ReactNode; - feature: 'updater' | 'license'; + feature: "updater" | "license"; } -const featureMap: Record = { +const featureMap: Record = { updater: appInfo.featureUpdater, license: appInfo.featureLicense, }; diff --git a/src-web/components/CloneGitRepositoryDialog.tsx b/src-web/components/CloneGitRepositoryDialog.tsx index 9804dd9d..b0e27373 100644 --- a/src-web/components/CloneGitRepositoryDialog.tsx +++ b/src-web/components/CloneGitRepositoryDialog.tsx @@ -1,16 +1,16 @@ -import { open } from '@tauri-apps/plugin-dialog'; -import { gitClone } from '@yaakapp-internal/git'; -import { useState } from 'react'; -import { openWorkspaceFromSyncDir } from '../commands/openWorkspaceFromSyncDir'; -import { appInfo } from '../lib/appInfo'; -import { showErrorToast } from '../lib/toast'; -import { Banner } from './core/Banner'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { IconButton } from './core/IconButton'; -import { PlainInput } from './core/PlainInput'; -import { VStack } from './core/Stacks'; -import { promptCredentials } from './git/credentials'; +import { open } from "@tauri-apps/plugin-dialog"; +import { gitClone } from "@yaakapp-internal/git"; +import { useState } from "react"; +import { openWorkspaceFromSyncDir } from "../commands/openWorkspaceFromSyncDir"; +import { appInfo } from "../lib/appInfo"; +import { showErrorToast } from "../lib/toast"; +import { Banner } from "./core/Banner"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { IconButton } from "./core/IconButton"; +import { PlainInput } from "./core/PlainInput"; +import { VStack } from "./core/Stacks"; +import { promptCredentials } from "./git/credentials"; interface Props { hide: () => void; @@ -18,15 +18,15 @@ interface Props { // Detect path separator from an existing path (defaults to /) function getPathSeparator(path: string): string { - return path.includes('\\') ? '\\' : '/'; + return path.includes("\\") ? "\\" : "/"; } export function CloneGitRepositoryDialog({ hide }: Props) { - const [url, setUrl] = useState(''); + const [url, setUrl] = useState(""); const [baseDirectory, setBaseDirectory] = useState(appInfo.defaultProjectDir); const [directoryOverride, setDirectoryOverride] = useState(null); const [hasSubdirectory, setHasSubdirectory] = useState(false); - const [subdirectory, setSubdirectory] = useState(''); + const [subdirectory, setSubdirectory] = useState(""); const [isCloning, setIsCloning] = useState(false); const [error, setError] = useState(null); @@ -39,7 +39,7 @@ export function CloneGitRepositoryDialog({ hide }: Props) { const handleSelectDirectory = async () => { const dir = await open({ - title: 'Select Directory', + title: "Select Directory", directory: true, multiple: false, }); @@ -59,9 +59,9 @@ export function CloneGitRepositoryDialog({ hide }: Props) { try { const result = await gitClone(url, directory, promptCredentials); - if (result.type === 'needs_credentials') { + if (result.type === "needs_credentials") { setError( - result.error ?? 'Authentication failed. Please check your credentials and try again.', + result.error ?? "Authentication failed. Please check your credentials and try again.", ); return; } @@ -73,8 +73,8 @@ export function CloneGitRepositoryDialog({ hide }: Props) { } catch (err) { setError(String(err)); showErrorToast({ - id: 'git-clone-error', - title: 'Clone Failed', + id: "git-clone-error", + title: "Clone Failed", message: String(err), }); } finally { @@ -137,7 +137,7 @@ export function CloneGitRepositoryDialog({ hide }: Props) { disabled={!url || !directory || isCloning} isLoading={isCloning} > - {isCloning ? 'Cloning...' : 'Clone Repository'} + {isCloning ? "Cloning..." : "Clone Repository"} ); @@ -157,5 +157,5 @@ function extractRepoName(url: string): string { if (sshMatch?.[1]) { return sshMatch[1]; } - return ''; + return ""; } diff --git a/src-web/components/ColorIndicator.tsx b/src-web/components/ColorIndicator.tsx index 931fabcf..60cdf14a 100644 --- a/src-web/components/ColorIndicator.tsx +++ b/src-web/components/ColorIndicator.tsx @@ -1,5 +1,5 @@ -import classNames from 'classnames'; -import type { CSSProperties } from 'react'; +import classNames from "classnames"; +import type { CSSProperties } from "react"; interface Props { color: string | null; @@ -11,7 +11,7 @@ export function ColorIndicator({ color, onClick, className }: Props) { const style: CSSProperties = { backgroundColor: color ?? undefined }; const finalClassName = classNames( className, - 'inline-block w-[0.75em] h-[0.75em] rounded-full mr-1.5 border border-transparent flex-shrink-0', + "inline-block w-[0.75em] h-[0.75em] rounded-full mr-1.5 border border-transparent flex-shrink-0", ); if (onClick) { @@ -20,7 +20,7 @@ export function ColorIndicator({ color, onClick, className }: Props) { type="button" onClick={onClick} style={style} - className={classNames(finalClassName, 'hover:border-text')} + className={classNames(finalClassName, "hover:border-text")} /> ); } diff --git a/src-web/components/CommandPaletteDialog.tsx b/src-web/components/CommandPaletteDialog.tsx index 94b79ebb..e0505f0c 100644 --- a/src-web/components/CommandPaletteDialog.tsx +++ b/src-web/components/CommandPaletteDialog.tsx @@ -1,7 +1,7 @@ -import { workspacesAtom } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { fuzzyFilter } from 'fuzzbunny'; -import { useAtomValue } from 'jotai'; +import { workspacesAtom } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { fuzzyFilter } from "fuzzbunny"; +import { useAtomValue } from "jotai"; import { Fragment, type KeyboardEvent, @@ -10,48 +10,48 @@ import { useMemo, useRef, useState, -} from 'react'; -import { createFolder } from '../commands/commands'; -import { createSubEnvironmentAndActivate } from '../commands/createEnvironment'; -import { openSettings } from '../commands/openSettings'; -import { switchWorkspace } from '../commands/switchWorkspace'; -import { useActiveCookieJar } from '../hooks/useActiveCookieJar'; -import { useActiveEnvironment } from '../hooks/useActiveEnvironment'; -import { useActiveRequest } from '../hooks/useActiveRequest'; -import { activeWorkspaceIdAtom } from '../hooks/useActiveWorkspace'; -import { useAllRequests } from '../hooks/useAllRequests'; -import { useCreateWorkspace } from '../hooks/useCreateWorkspace'; -import { useDebouncedState } from '../hooks/useDebouncedState'; -import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown'; -import { useGrpcRequestActions } from '../hooks/useGrpcRequestActions'; -import type { HotkeyAction } from '../hooks/useHotKey'; -import { useHttpRequestActions } from '../hooks/useHttpRequestActions'; -import { useRecentEnvironments } from '../hooks/useRecentEnvironments'; -import { useRecentRequests } from '../hooks/useRecentRequests'; -import { useRecentWorkspaces } from '../hooks/useRecentWorkspaces'; -import { useScrollIntoView } from '../hooks/useScrollIntoView'; -import { useSendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest'; -import { useSidebarHidden } from '../hooks/useSidebarHidden'; -import { appInfo } from '../lib/appInfo'; -import { copyToClipboard } from '../lib/copy'; -import { createRequestAndNavigate } from '../lib/createRequestAndNavigate'; -import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm'; -import { showDialog } from '../lib/dialog'; -import { editEnvironment } from '../lib/editEnvironment'; -import { renameModelWithPrompt } from '../lib/renameModelWithPrompt'; +} from "react"; +import { createFolder } from "../commands/commands"; +import { createSubEnvironmentAndActivate } from "../commands/createEnvironment"; +import { openSettings } from "../commands/openSettings"; +import { switchWorkspace } from "../commands/switchWorkspace"; +import { useActiveCookieJar } from "../hooks/useActiveCookieJar"; +import { useActiveEnvironment } from "../hooks/useActiveEnvironment"; +import { useActiveRequest } from "../hooks/useActiveRequest"; +import { activeWorkspaceIdAtom } from "../hooks/useActiveWorkspace"; +import { useAllRequests } from "../hooks/useAllRequests"; +import { useCreateWorkspace } from "../hooks/useCreateWorkspace"; +import { useDebouncedState } from "../hooks/useDebouncedState"; +import { useEnvironmentsBreakdown } from "../hooks/useEnvironmentsBreakdown"; +import { useGrpcRequestActions } from "../hooks/useGrpcRequestActions"; +import type { HotkeyAction } from "../hooks/useHotKey"; +import { useHttpRequestActions } from "../hooks/useHttpRequestActions"; +import { useRecentEnvironments } from "../hooks/useRecentEnvironments"; +import { useRecentRequests } from "../hooks/useRecentRequests"; +import { useRecentWorkspaces } from "../hooks/useRecentWorkspaces"; +import { useScrollIntoView } from "../hooks/useScrollIntoView"; +import { useSendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest"; +import { useSidebarHidden } from "../hooks/useSidebarHidden"; +import { appInfo } from "../lib/appInfo"; +import { copyToClipboard } from "../lib/copy"; +import { createRequestAndNavigate } from "../lib/createRequestAndNavigate"; +import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm"; +import { showDialog } from "../lib/dialog"; +import { editEnvironment } from "../lib/editEnvironment"; +import { renameModelWithPrompt } from "../lib/renameModelWithPrompt"; import { resolvedModelNameWithFolders, resolvedModelNameWithFoldersArray, -} from '../lib/resolvedModelName'; -import { router } from '../lib/router'; -import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams'; -import { CookieDialog } from './CookieDialog'; -import { Button } from './core/Button'; -import { Heading } from './core/Heading'; -import { Hotkey } from './core/Hotkey'; -import { HttpMethodTag } from './core/HttpMethodTag'; -import { Icon } from './core/Icon'; -import { PlainInput } from './core/PlainInput'; +} from "../lib/resolvedModelName"; +import { router } from "../lib/router"; +import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams"; +import { CookieDialog } from "./CookieDialog"; +import { Button } from "./core/Button"; +import { Heading } from "./core/Heading"; +import { Hotkey } from "./core/Hotkey"; +import { HttpMethodTag } from "./core/HttpMethodTag"; +import { Icon } from "./core/Icon"; +import { PlainInput } from "./core/PlainInput"; interface CommandPaletteGroup { key: string; @@ -68,7 +68,7 @@ type CommandPaletteItem = { const MAX_PER_GROUP = 8; export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { - const [command, setCommand] = useDebouncedState('', 150); + const [command, setCommand] = useDebouncedState("", 150); const [selectedItemKey, setSelectedItemKey] = useState(null); const activeEnvironment = useActiveEnvironment(); const httpRequestActions = useHttpRequestActions(); @@ -96,79 +96,79 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const commands: CommandPaletteItem[] = [ { - key: 'settings.open', - label: 'Open Settings', - action: 'settings.show', + key: "settings.open", + label: "Open Settings", + action: "settings.show", onSelect: () => openSettings.mutate(null), }, { - key: 'app.create', - label: 'Create Workspace', + key: "app.create", + label: "Create Workspace", onSelect: createWorkspace, }, { - key: 'model.create', - label: 'Create HTTP Request', - onSelect: () => createRequestAndNavigate({ model: 'http_request', workspaceId }), + key: "model.create", + label: "Create HTTP Request", + onSelect: () => createRequestAndNavigate({ model: "http_request", workspaceId }), }, { - key: 'grpc_request.create', - label: 'Create GRPC Request', - onSelect: () => createRequestAndNavigate({ model: 'grpc_request', workspaceId }), + key: "grpc_request.create", + label: "Create GRPC Request", + onSelect: () => createRequestAndNavigate({ model: "grpc_request", workspaceId }), }, { - key: 'websocket_request.create', - label: 'Create Websocket Request', - onSelect: () => createRequestAndNavigate({ model: 'websocket_request', workspaceId }), + key: "websocket_request.create", + label: "Create Websocket Request", + onSelect: () => createRequestAndNavigate({ model: "websocket_request", workspaceId }), }, { - key: 'folder.create', - label: 'Create Folder', + key: "folder.create", + label: "Create Folder", onSelect: () => createFolder.mutate({}), }, { - key: 'cookies.show', - label: 'Show Cookies', + key: "cookies.show", + label: "Show Cookies", onSelect: async () => { showDialog({ - id: 'cookies', - title: 'Manage Cookies', - size: 'full', + id: "cookies", + title: "Manage Cookies", + size: "full", render: () => , }); }, }, { - key: 'environment.edit', - label: 'Edit Environment', - action: 'environment_editor.toggle', + key: "environment.edit", + label: "Edit Environment", + action: "environment_editor.toggle", onSelect: () => editEnvironment(activeEnvironment), }, { - key: 'environment.create', - label: 'Create Environment', + key: "environment.create", + label: "Create Environment", onSelect: () => createSubEnvironmentAndActivate.mutate(baseEnvironment), }, { - key: 'sidebar.toggle', - label: 'Toggle Sidebar', - action: 'sidebar.focus', + key: "sidebar.toggle", + label: "Toggle Sidebar", + action: "sidebar.focus", onSelect: () => setSidebarHidden((h) => !h), }, ]; - if (activeRequest?.model === 'http_request') { + if (activeRequest?.model === "http_request") { commands.push({ - key: 'request.send', - action: 'request.send', - label: 'Send Request', + key: "request.send", + action: "request.send", + label: "Send Request", onSelect: () => sendRequest(activeRequest.id), }); if (appInfo.cliVersion != null) { commands.push({ - key: 'request.copy_cli_send', + key: "request.copy_cli_send", searchText: `copy cli send yaak request send ${activeRequest.id}`, - label: 'Copy CLI Send Command', + label: "Copy CLI Send Command", onSelect: () => copyToClipboard(`yaak request send ${activeRequest.id}`), }); } @@ -181,7 +181,7 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { }); } - if (activeRequest?.model === 'grpc_request') { + if (activeRequest?.model === "grpc_request") { grpcRequestActions.forEach((a, i) => { commands.push({ key: `grpc_request_action.${i}`, @@ -193,21 +193,21 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { if (activeRequest != null) { commands.push({ - key: 'http_request.rename', - label: 'Rename Request', + key: "http_request.rename", + label: "Rename Request", onSelect: () => renameModelWithPrompt(activeRequest), }); commands.push({ - key: 'sidebar.selected.delete', - label: 'Delete Request', + key: "sidebar.selected.delete", + label: "Delete Request", onSelect: () => deleteModelWithConfirm(activeRequest), }); } return commands.sort((a, b) => - ('searchText' in a ? a.searchText : a.label).localeCompare( - 'searchText' in b ? b.searchText : b.label, + ("searchText" in a ? a.searchText : a.label).localeCompare( + "searchText" in b ? b.searchText : b.label, ), ); }, [ @@ -284,14 +284,14 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const groups = useMemo(() => { const actionsGroup: CommandPaletteGroup = { - key: 'actions', - label: 'Actions', + key: "actions", + label: "Actions", items: workspaceCommands, }; const requestGroup: CommandPaletteGroup = { - key: 'requests', - label: 'Switch Request', + key: "requests", + label: "Switch Request", items: [], }; @@ -305,14 +305,14 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { {resolvedModelNameWithFoldersArray(r).map((name, i, all) => ( {i !== 0 && } -
{name}
+
{name}
))} ), onSelect: async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: r.workspaceId }, search: (prev) => ({ ...prev, request_id: r.id }), }); @@ -321,8 +321,8 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { } const environmentGroup: CommandPaletteGroup = { - key: 'environments', - label: 'Switch Environment', + key: "environments", + label: "Switch Environment", items: [], }; @@ -338,8 +338,8 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { } const workspaceGroup: CommandPaletteGroup = { - key: 'workspaces', - label: 'Switch Workspace', + key: "workspaces", + label: "Switch Workspace", items: [], }; @@ -367,10 +367,10 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { ? fuzzyFilter( allItems.map((i) => ({ ...i, - filterBy: 'searchText' in i ? i.searchText : i.label, + filterBy: "searchText" in i ? i.searchText : i.label, })), command, - { fields: ['filterBy'] }, + { fields: ["filterBy"] }, ) .sort((a, b) => b.score - a.score) .map((v) => v.item) @@ -408,13 +408,13 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) { const handleKeyDown = useCallback( (e: KeyboardEvent) => { const index = filteredAllItems.findIndex((v) => v.key === selectedItem?.key); - if (e.key === 'ArrowDown' || (e.ctrlKey && e.key === 'n')) { + if (e.key === "ArrowDown" || (e.ctrlKey && e.key === "n")) { const next = filteredAllItems[index + 1] ?? filteredAllItems[0]; setSelectedItemKey(next?.key ?? null); - } else if (e.key === 'ArrowUp' || (e.ctrlKey && e.key === 'k')) { + } else if (e.key === "ArrowUp" || (e.ctrlKey && e.key === "k")) { const prev = filteredAllItems[index - 1] ?? filteredAllItems[filteredAllItems.length - 1]; setSelectedItemKey(prev?.key ?? null); - } else if (e.key === 'Enter') { + } else if (e.key === "Enter") { const selected = filteredAllItems[index]; setSelectedItemKey(selected?.key ?? null); if (selected) { @@ -491,10 +491,10 @@ function CommandPaletteItem({ color="custom" justify="start" className={classNames( - 'w-full h-sm flex items-center rounded px-1.5', - 'hover:text-text', - active && 'bg-surface-highlight', - !active && 'text-text-subtle', + "w-full h-sm flex items-center rounded px-1.5", + "hover:text-text", + active && "bg-surface-highlight", + !active && "text-text-subtle", )} > {children} diff --git a/src-web/components/ConfirmLargeRequestBody.tsx b/src-web/components/ConfirmLargeRequestBody.tsx index eab5a07e..574c5336 100644 --- a/src-web/components/ConfirmLargeRequestBody.tsx +++ b/src-web/components/ConfirmLargeRequestBody.tsx @@ -1,14 +1,14 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import type { ReactNode } from 'react'; -import { useToggle } from '../hooks/useToggle'; -import { showConfirm } from '../lib/confirm'; -import { Banner } from './core/Banner'; -import { Button } from './core/Button'; -import { InlineCode } from './core/InlineCode'; -import { Link } from './core/Link'; -import { SizeTag } from './core/SizeTag'; -import { HStack } from './core/Stacks'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import type { ReactNode } from "react"; +import { useToggle } from "../hooks/useToggle"; +import { showConfirm } from "../lib/confirm"; +import { Banner } from "./core/Banner"; +import { Button } from "./core/Button"; +import { InlineCode } from "./core/InlineCode"; +import { Link } from "./core/Link"; +import { SizeTag } from "./core/SizeTag"; +import { HStack } from "./core/Stacks"; interface Props { children: ReactNode; @@ -31,17 +31,17 @@ export function ConfirmLargeRequestBody({ children, request }: Props) { return (

- Rendering content over{' '} + Rendering content over{" "} - {' '} + {" "} may impact performance.

- See{' '} + See{" "} Working With Large Values - {' '} + {" "} for tips.

@@ -55,13 +55,13 @@ export function ConfirmLargeRequestBody({ children, request }: Props) { onClick={async () => { const confirm = await showConfirm({ id: `delete-body-${request.id}`, - confirmText: 'Delete Body', - title: 'Delete Body Text', - description: 'Are you sure you want to delete the request body text?', - color: 'danger', + confirmText: "Delete Body", + title: "Delete Body Text", + description: "Are you sure you want to delete the request body text?", + color: "danger", }); if (confirm) { - await patchModel(request, { body: { ...request.body, text: '' } }); + await patchModel(request, { body: { ...request.body, text: "" } }); } }} > diff --git a/src-web/components/ConfirmLargeResponse.tsx b/src-web/components/ConfirmLargeResponse.tsx index 11a074c4..a2b58cf9 100644 --- a/src-web/components/ConfirmLargeResponse.tsx +++ b/src-web/components/ConfirmLargeResponse.tsx @@ -1,16 +1,16 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { type ReactNode, useMemo } from 'react'; -import { useSaveResponse } from '../hooks/useSaveResponse'; -import { useToggle } from '../hooks/useToggle'; -import { isProbablyTextContentType } from '../lib/contentType'; -import { getContentTypeFromHeaders } from '../lib/model_util'; -import { getResponseBodyText } from '../lib/responseBody'; -import { CopyButton } from './CopyButton'; -import { Banner } from './core/Banner'; -import { Button } from './core/Button'; -import { InlineCode } from './core/InlineCode'; -import { SizeTag } from './core/SizeTag'; -import { HStack } from './core/Stacks'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { type ReactNode, useMemo } from "react"; +import { useSaveResponse } from "../hooks/useSaveResponse"; +import { useToggle } from "../hooks/useToggle"; +import { isProbablyTextContentType } from "../lib/contentType"; +import { getContentTypeFromHeaders } from "../lib/model_util"; +import { getResponseBodyText } from "../lib/responseBody"; +import { CopyButton } from "./CopyButton"; +import { Banner } from "./core/Banner"; +import { Button } from "./core/Button"; +import { InlineCode } from "./core/InlineCode"; +import { SizeTag } from "./core/SizeTag"; +import { HStack } from "./core/Stacks"; interface Props { children: ReactNode; @@ -33,10 +33,10 @@ export function ConfirmLargeResponse({ children, response }: Props) { return (

- Showing responses over{' '} + Showing responses over{" "} - {' '} + {" "} may impact performance

diff --git a/src-web/components/ConfirmLargeResponseRequest.tsx b/src-web/components/ConfirmLargeResponseRequest.tsx index 80e9fb30..ea7f48a0 100644 --- a/src-web/components/ConfirmLargeResponseRequest.tsx +++ b/src-web/components/ConfirmLargeResponseRequest.tsx @@ -1,15 +1,15 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { type ReactNode, useMemo } from 'react'; -import { getRequestBodyText as getHttpResponseRequestBodyText } from '../hooks/useHttpRequestBody'; -import { useToggle } from '../hooks/useToggle'; -import { isProbablyTextContentType } from '../lib/contentType'; -import { getContentTypeFromHeaders } from '../lib/model_util'; -import { CopyButton } from './CopyButton'; -import { Banner } from './core/Banner'; -import { Button } from './core/Button'; -import { InlineCode } from './core/InlineCode'; -import { SizeTag } from './core/SizeTag'; -import { HStack } from './core/Stacks'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { type ReactNode, useMemo } from "react"; +import { getRequestBodyText as getHttpResponseRequestBodyText } from "../hooks/useHttpRequestBody"; +import { useToggle } from "../hooks/useToggle"; +import { isProbablyTextContentType } from "../lib/contentType"; +import { getContentTypeFromHeaders } from "../lib/model_util"; +import { CopyButton } from "./CopyButton"; +import { Banner } from "./core/Banner"; +import { Button } from "./core/Button"; +import { InlineCode } from "./core/InlineCode"; +import { SizeTag } from "./core/SizeTag"; +import { HStack } from "./core/Stacks"; interface Props { children: ReactNode; @@ -31,10 +31,10 @@ export function ConfirmLargeResponseRequest({ children, response }: Props) { return (

- Showing content over{' '} + Showing content over{" "} - {' '} + {" "} may impact performance

@@ -46,7 +46,7 @@ export function ConfirmLargeResponseRequest({ children, response }: Props) { color="secondary" variant="border" size="xs" - text={() => getHttpResponseRequestBodyText(response).then((d) => d?.bodyText ?? '')} + text={() => getHttpResponseRequestBodyText(response).then((d) => d?.bodyText ?? "")} /> )} diff --git a/src-web/components/CookieDialog.tsx b/src-web/components/CookieDialog.tsx index 624ee49e..34329188 100644 --- a/src-web/components/CookieDialog.tsx +++ b/src-web/components/CookieDialog.tsx @@ -1,10 +1,10 @@ -import type { Cookie } from '@yaakapp-internal/models'; -import { cookieJarsAtom, patchModel } from '@yaakapp-internal/models'; -import { useAtomValue } from 'jotai'; -import { cookieDomain } from '../lib/model_util'; -import { Banner } from './core/Banner'; -import { IconButton } from './core/IconButton'; -import { InlineCode } from './core/InlineCode'; +import type { Cookie } from "@yaakapp-internal/models"; +import { cookieJarsAtom, patchModel } from "@yaakapp-internal/models"; +import { useAtomValue } from "jotai"; +import { cookieDomain } from "../lib/model_util"; +import { Banner } from "./core/Banner"; +import { IconButton } from "./core/IconButton"; +import { InlineCode } from "./core/InlineCode"; interface Props { cookieJarId: string | null; diff --git a/src-web/components/CookieDropdown.tsx b/src-web/components/CookieDropdown.tsx index 1f6708b6..4e590497 100644 --- a/src-web/components/CookieDropdown.tsx +++ b/src-web/components/CookieDropdown.tsx @@ -1,17 +1,17 @@ -import { cookieJarsAtom, patchModel } from '@yaakapp-internal/models'; -import { useAtomValue } from 'jotai'; -import { memo, useMemo } from 'react'; -import { useActiveCookieJar } from '../hooks/useActiveCookieJar'; -import { useCreateCookieJar } from '../hooks/useCreateCookieJar'; -import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm'; -import { showDialog } from '../lib/dialog'; -import { showPrompt } from '../lib/prompt'; -import { setWorkspaceSearchParams } from '../lib/setWorkspaceSearchParams'; -import { CookieDialog } from './CookieDialog'; -import { Dropdown, type DropdownItem } from './core/Dropdown'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { InlineCode } from './core/InlineCode'; +import { cookieJarsAtom, patchModel } from "@yaakapp-internal/models"; +import { useAtomValue } from "jotai"; +import { memo, useMemo } from "react"; +import { useActiveCookieJar } from "../hooks/useActiveCookieJar"; +import { useCreateCookieJar } from "../hooks/useCreateCookieJar"; +import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm"; +import { showDialog } from "../lib/dialog"; +import { showPrompt } from "../lib/prompt"; +import { setWorkspaceSearchParams } from "../lib/setWorkspaceSearchParams"; +import { CookieDialog } from "./CookieDialog"; +import { Dropdown, type DropdownItem } from "./core/Dropdown"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { InlineCode } from "./core/InlineCode"; export const CookieDropdown = memo(function CookieDropdown() { const activeCookieJar = useActiveCookieJar(); @@ -23,44 +23,44 @@ export const CookieDropdown = memo(function CookieDropdown() { ...(cookieJars ?? []).map((j) => ({ key: j.id, label: j.name, - leftSlot: , + leftSlot: , onSelect: () => { setWorkspaceSearchParams({ cookie_jar_id: j.id }); }, })), ...(((cookieJars ?? []).length > 0 && activeCookieJar != null ? [ - { type: 'separator', label: activeCookieJar.name }, + { type: "separator", label: activeCookieJar.name }, { - key: 'manage', - label: 'Manage Cookies', + key: "manage", + label: "Manage Cookies", leftSlot: , onSelect: () => { if (activeCookieJar == null) return; showDialog({ - id: 'cookies', - title: 'Manage Cookies', - size: 'full', + id: "cookies", + title: "Manage Cookies", + size: "full", render: () => , }); }, }, { - key: 'rename', - label: 'Rename', + key: "rename", + label: "Rename", leftSlot: , onSelect: async () => { const name = await showPrompt({ - id: 'rename-cookie-jar', - title: 'Rename Cookie Jar', + id: "rename-cookie-jar", + title: "Rename Cookie Jar", description: ( <> Enter a new name for {activeCookieJar?.name} ), - label: 'Name', - confirmText: 'Save', - placeholder: 'New name', + label: "Name", + confirmText: "Save", + placeholder: "New name", defaultValue: activeCookieJar?.name, }); if (name == null) return; @@ -70,9 +70,9 @@ export const CookieDropdown = memo(function CookieDropdown() { ...(((cookieJars ?? []).length > 1 // Never delete the last one ? [ { - label: 'Delete', + label: "Delete", leftSlot: , - color: 'danger', + color: "danger", onSelect: async () => { await deleteModelWithConfirm(activeCookieJar); }, @@ -81,10 +81,10 @@ export const CookieDropdown = memo(function CookieDropdown() { : []) as DropdownItem[]), ] : []) as DropdownItem[]), - { type: 'separator' }, + { type: "separator" }, { - key: 'create-cookie-jar', - label: 'New Cookie Jar', + key: "create-cookie-jar", + label: "New Cookie Jar", leftSlot: , onSelect: () => createCookieJar.mutate(), }, diff --git a/src-web/components/CopyButton.tsx b/src-web/components/CopyButton.tsx index 2c262429..93f0c1f7 100644 --- a/src-web/components/CopyButton.tsx +++ b/src-web/components/CopyButton.tsx @@ -1,10 +1,10 @@ -import { useTimedBoolean } from '../hooks/useTimedBoolean'; -import { copyToClipboard } from '../lib/copy'; -import { showToast } from '../lib/toast'; -import type { ButtonProps } from './core/Button'; -import { Button } from './core/Button'; +import { useTimedBoolean } from "../hooks/useTimedBoolean"; +import { copyToClipboard } from "../lib/copy"; +import { showToast } from "../lib/toast"; +import type { ButtonProps } from "./core/Button"; +import { Button } from "./core/Button"; -interface Props extends Omit { +interface Props extends Omit { text: string | (() => Promise); } @@ -14,12 +14,12 @@ export function CopyButton({ text, ...props }: Props) { ); } diff --git a/src-web/components/CopyIconButton.tsx b/src-web/components/CopyIconButton.tsx index 78c3c679..a828103a 100644 --- a/src-web/components/CopyIconButton.tsx +++ b/src-web/components/CopyIconButton.tsx @@ -1,10 +1,10 @@ -import { useTimedBoolean } from '../hooks/useTimedBoolean'; -import { copyToClipboard } from '../lib/copy'; -import { showToast } from '../lib/toast'; -import type { IconButtonProps } from './core/IconButton'; -import { IconButton } from './core/IconButton'; +import { useTimedBoolean } from "../hooks/useTimedBoolean"; +import { copyToClipboard } from "../lib/copy"; +import { showToast } from "../lib/toast"; +import type { IconButtonProps } from "./core/IconButton"; +import { IconButton } from "./core/IconButton"; -interface Props extends Omit { +interface Props extends Omit { text: string | (() => Promise); } @@ -13,15 +13,15 @@ export function CopyIconButton({ text, ...props }: Props) { return ( { - const content = typeof text === 'function' ? await text() : text; + const content = typeof text === "function" ? await text() : text; if (content == null) { showToast({ - id: 'failed-to-copy', - color: 'danger', - message: 'Failed to copy', + id: "failed-to-copy", + color: "danger", + message: "Failed to copy", }); } else { copyToClipboard(content, { disableToast: true }); diff --git a/src-web/components/CreateDropdown.tsx b/src-web/components/CreateDropdown.tsx index 7241381d..9fa82376 100644 --- a/src-web/components/CreateDropdown.tsx +++ b/src-web/components/CreateDropdown.tsx @@ -1,8 +1,8 @@ -import { useCreateDropdownItems } from '../hooks/useCreateDropdownItems'; -import type { DropdownProps } from './core/Dropdown'; -import { Dropdown } from './core/Dropdown'; +import { useCreateDropdownItems } from "../hooks/useCreateDropdownItems"; +import type { DropdownProps } from "./core/Dropdown"; +import { Dropdown } from "./core/Dropdown"; -interface Props extends Omit { +interface Props extends Omit { hideFolder?: boolean; } @@ -10,7 +10,7 @@ export function CreateDropdown({ hideFolder, children, ...props }: Props) { const getItems = useCreateDropdownItems({ hideFolder, hideIcons: true, - folderId: 'active-folder', + folderId: "active-folder", }); return ( diff --git a/src-web/components/CreateEnvironmentDialog.tsx b/src-web/components/CreateEnvironmentDialog.tsx index 7a0534b3..1436617a 100644 --- a/src-web/components/CreateEnvironmentDialog.tsx +++ b/src-web/components/CreateEnvironmentDialog.tsx @@ -1,12 +1,12 @@ -import { createWorkspaceModel } from '@yaakapp-internal/models'; -import { useState } from 'react'; -import { useToggle } from '../hooks/useToggle'; -import { ColorIndicator } from './ColorIndicator'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { ColorPickerWithThemeColors } from './core/ColorPicker'; -import { Label } from './core/Label'; -import { PlainInput } from './core/PlainInput'; +import { createWorkspaceModel } from "@yaakapp-internal/models"; +import { useState } from "react"; +import { useToggle } from "../hooks/useToggle"; +import { ColorIndicator } from "./ColorIndicator"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { ColorPickerWithThemeColors } from "./core/ColorPicker"; +import { Label } from "./core/Label"; +import { PlainInput } from "./core/PlainInput"; interface Props { onCreate: (id: string) => void; @@ -15,7 +15,7 @@ interface Props { } export function CreateEnvironmentDialog({ workspaceId, hide, onCreate }: Props) { - const [name, setName] = useState(''); + const [name, setName] = useState(""); const [color, setColor] = useState(null); const [sharable, toggleSharable] = useToggle(false); return ( @@ -24,13 +24,13 @@ export function CreateEnvironmentDialog({ workspaceId, hide, onCreate }: Props) onSubmit={async (e) => { e.preventDefault(); const id = await createWorkspaceModel({ - model: 'environment', + model: "environment", name, color, variables: [], public: sharable, workspaceId, - parentModel: 'environment', + parentModel: "environment", }); hide(); onCreate(id); diff --git a/src-web/components/CreateWorkspaceDialog.tsx b/src-web/components/CreateWorkspaceDialog.tsx index 0076412d..86eb8503 100644 --- a/src-web/components/CreateWorkspaceDialog.tsx +++ b/src-web/components/CreateWorkspaceDialog.tsx @@ -1,26 +1,26 @@ -import { gitMutations } from '@yaakapp-internal/git'; -import type { WorkspaceMeta } from '@yaakapp-internal/models'; -import { createGlobalModel, updateModel } from '@yaakapp-internal/models'; -import { useState } from 'react'; -import { router } from '../lib/router'; -import { setupOrConfigureEncryption } from '../lib/setupOrConfigureEncryption'; -import { invokeCmd } from '../lib/tauri'; -import { showErrorToast } from '../lib/toast'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { Label } from './core/Label'; -import { PlainInput } from './core/PlainInput'; -import { VStack } from './core/Stacks'; -import { EncryptionHelp } from './EncryptionHelp'; -import { gitCallbacks } from './git/callbacks'; -import { SyncToFilesystemSetting } from './SyncToFilesystemSetting'; +import { gitMutations } from "@yaakapp-internal/git"; +import type { WorkspaceMeta } from "@yaakapp-internal/models"; +import { createGlobalModel, updateModel } from "@yaakapp-internal/models"; +import { useState } from "react"; +import { router } from "../lib/router"; +import { setupOrConfigureEncryption } from "../lib/setupOrConfigureEncryption"; +import { invokeCmd } from "../lib/tauri"; +import { showErrorToast } from "../lib/toast"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { Label } from "./core/Label"; +import { PlainInput } from "./core/PlainInput"; +import { VStack } from "./core/Stacks"; +import { EncryptionHelp } from "./EncryptionHelp"; +import { gitCallbacks } from "./git/callbacks"; +import { SyncToFilesystemSetting } from "./SyncToFilesystemSetting"; interface Props { hide: () => void; } export function CreateWorkspaceDialog({ hide }: Props) { - const [name, setName] = useState(''); + const [name, setName] = useState(""); const [syncConfig, setSyncConfig] = useState<{ filePath: string | null; initGit?: boolean; @@ -34,12 +34,12 @@ export function CreateWorkspaceDialog({ hide }: Props) { className="pb-3" onSubmit={async (e) => { e.preventDefault(); - const workspaceId = await createGlobalModel({ model: 'workspace', name }); + const workspaceId = await createGlobalModel({ model: "workspace", name }); if (workspaceId == null) return; // Do getWorkspaceMeta instead of naively creating one because it might have // been created already when the store refreshes the workspace meta after - const workspaceMeta = await invokeCmd('cmd_get_workspace_meta', { + const workspaceMeta = await invokeCmd("cmd_get_workspace_meta", { workspaceId, }); await updateModel({ @@ -52,8 +52,8 @@ export function CreateWorkspaceDialog({ hide }: Props) { .init.mutateAsync() .catch((err) => { showErrorToast({ - id: 'git-init-error', - title: 'Error initializing Git', + id: "git-init-error", + title: "Error initializing Git", message: String(err), }); }); @@ -61,7 +61,7 @@ export function CreateWorkspaceDialog({ hide }: Props) { // Navigate to workspace await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId }, }); diff --git a/src-web/components/Dialogs.tsx b/src-web/components/Dialogs.tsx index bf8b72ce..297cf2f2 100644 --- a/src-web/components/Dialogs.tsx +++ b/src-web/components/Dialogs.tsx @@ -1,14 +1,14 @@ -import { useAtomValue } from 'jotai'; -import type { ComponentType } from 'react'; -import { useCallback } from 'react'; -import { dialogsAtom, hideDialog } from '../lib/dialog'; -import { Dialog, type DialogProps } from './core/Dialog'; -import { ErrorBoundary } from './ErrorBoundary'; +import { useAtomValue } from "jotai"; +import type { ComponentType } from "react"; +import { useCallback } from "react"; +import { dialogsAtom, hideDialog } from "../lib/dialog"; +import { Dialog, type DialogProps } from "./core/Dialog"; +import { ErrorBoundary } from "./ErrorBoundary"; export type DialogInstance = { id: string; render: ComponentType<{ hide: () => void }>; -} & Omit; +} & Omit; export function Dialogs() { const dialogs = useAtomValue(dialogsAtom); diff --git a/src-web/components/DnsOverridesEditor.tsx b/src-web/components/DnsOverridesEditor.tsx index 41530ece..60948de3 100644 --- a/src-web/components/DnsOverridesEditor.tsx +++ b/src-web/components/DnsOverridesEditor.tsx @@ -1,13 +1,13 @@ -import type { DnsOverride, Workspace } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import { useCallback, useId, useMemo } from 'react'; -import { fireAndForget } from '../lib/fireAndForget'; -import { Button } from './core/Button'; -import { Checkbox } from './core/Checkbox'; -import { IconButton } from './core/IconButton'; -import { PlainInput } from './core/PlainInput'; -import { HStack, VStack } from './core/Stacks'; -import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from './core/Table'; +import type { DnsOverride, Workspace } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import { useCallback, useId, useMemo } from "react"; +import { fireAndForget } from "../lib/fireAndForget"; +import { Button } from "./core/Button"; +import { Checkbox } from "./core/Checkbox"; +import { IconButton } from "./core/IconButton"; +import { PlainInput } from "./core/PlainInput"; +import { HStack, VStack } from "./core/Stacks"; +import { Table, TableBody, TableCell, TableHead, TableHeaderCell, TableRow } from "./core/Table"; interface Props { workspace: Workspace; @@ -37,8 +37,8 @@ export function DnsOverridesEditor({ workspace }: Props) { const handleAdd = useCallback(() => { const newOverride: DnsOverride = { - hostname: '', - ipv4: [''], + hostname: "", + ipv4: [""], ipv6: [], enabled: true, }; @@ -66,9 +66,9 @@ export function DnsOverridesEditor({ workspace }: Props) { return (
- Override DNS resolution for specific hostnames. This works like{' '} - /etc/hosts{' '} - but only for requests made from this workspace. + Override DNS resolution for specific hostnames. This works like{" "} + /etc/hosts but + only for requests made from this workspace.
{overridesWithIds.length > 0 && ( @@ -111,15 +111,15 @@ interface DnsOverrideRowProps { } function DnsOverrideRow({ override, onUpdate, onDelete }: DnsOverrideRowProps) { - const ipv4Value = override.ipv4.join(', '); - const ipv6Value = override.ipv6.join(', '); + const ipv4Value = override.ipv4.join(", "); + const ipv6Value = override.ipv6.join(", "); return ( onUpdate({ enabled })} /> @@ -144,7 +144,7 @@ function DnsOverrideRow({ override, onUpdate, onDelete }: DnsOverrideRowProps) { onChange={(value) => onUpdate({ ipv4: value - .split(',') + .split(",") .map((s) => s.trim()) .filter(Boolean), }) @@ -161,7 +161,7 @@ function DnsOverrideRow({ override, onUpdate, onDelete }: DnsOverrideRowProps) { onChange={(value) => onUpdate({ ipv6: value - .split(',') + .split(",") .map((s) => s.trim()) .filter(Boolean), }) diff --git a/src-web/components/DropMarker.tsx b/src-web/components/DropMarker.tsx index 18547781..f3a1f8dd 100644 --- a/src-web/components/DropMarker.tsx +++ b/src-web/components/DropMarker.tsx @@ -1,30 +1,30 @@ -import classNames from 'classnames'; -import type { CSSProperties } from 'react'; -import { memo } from 'react'; +import classNames from "classnames"; +import type { CSSProperties } from "react"; +import { memo } from "react"; interface Props { className?: string; style?: CSSProperties; - orientation?: 'horizontal' | 'vertical'; + orientation?: "horizontal" | "vertical"; } export const DropMarker = memo( - function DropMarker({ className, style, orientation = 'horizontal' }: Props) { + function DropMarker({ className, style, orientation = "horizontal" }: Props) { return (
diff --git a/src-web/components/DynamicForm.tsx b/src-web/components/DynamicForm.tsx index 72d5fc34..91d49890 100644 --- a/src-web/components/DynamicForm.tsx +++ b/src-web/components/DynamicForm.tsx @@ -1,5 +1,5 @@ -import type { Folder, HttpRequest } from '@yaakapp-internal/models'; -import { foldersAtom, httpRequestsAtom } from '@yaakapp-internal/models'; +import type { Folder, HttpRequest } from "@yaakapp-internal/models"; +import { foldersAtom, httpRequestsAtom } from "@yaakapp-internal/models"; import type { FormInput, FormInputCheckbox, @@ -10,33 +10,33 @@ import type { FormInputSelect, FormInputText, JsonPrimitive, -} from '@yaakapp-internal/plugins'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import { useCallback, useEffect, useMemo } from 'react'; -import { useActiveRequest } from '../hooks/useActiveRequest'; -import { useRandomKey } from '../hooks/useRandomKey'; -import { capitalize } from '../lib/capitalize'; -import { showDialog } from '../lib/dialog'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { Banner } from './core/Banner'; -import { Checkbox } from './core/Checkbox'; -import { DetailsBanner } from './core/DetailsBanner'; -import { Editor } from './core/Editor/LazyEditor'; -import { IconButton } from './core/IconButton'; -import type { InputProps } from './core/Input'; -import { Input } from './core/Input'; -import { Label } from './core/Label'; -import type { Pair } from './core/PairEditor'; -import { PairEditor } from './core/PairEditor'; -import { PlainInput } from './core/PlainInput'; -import { Select } from './core/Select'; -import { VStack } from './core/Stacks'; -import { Markdown } from './Markdown'; -import { SelectFile } from './SelectFile'; +} from "@yaakapp-internal/plugins"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import { useCallback, useEffect, useMemo } from "react"; +import { useActiveRequest } from "../hooks/useActiveRequest"; +import { useRandomKey } from "../hooks/useRandomKey"; +import { capitalize } from "../lib/capitalize"; +import { showDialog } from "../lib/dialog"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { Banner } from "./core/Banner"; +import { Checkbox } from "./core/Checkbox"; +import { DetailsBanner } from "./core/DetailsBanner"; +import { Editor } from "./core/Editor/LazyEditor"; +import { IconButton } from "./core/IconButton"; +import type { InputProps } from "./core/Input"; +import { Input } from "./core/Input"; +import { Label } from "./core/Label"; +import type { Pair } from "./core/PairEditor"; +import { PairEditor } from "./core/PairEditor"; +import { PlainInput } from "./core/PlainInput"; +import { Select } from "./core/Select"; +import { VStack } from "./core/Stacks"; +import { Markdown } from "./Markdown"; +import { SelectFile } from "./SelectFile"; -export const DYNAMIC_FORM_NULL_ARG = '__NULL__'; -const INPUT_SIZE = 'sm'; +export const DYNAMIC_FORM_NULL_ARG = "__NULL__"; +const INPUT_SIZE = "sm"; interface Props { inputs: FormInput[] | undefined | null; @@ -75,7 +75,7 @@ export function DynamicForm>({ autocompleteFunctions={autocompleteFunctions} autocompleteVariables={autocompleteVariables} data={data} - className={classNames(className, 'pb-4')} // Pad the bottom to look nice + className={classNames(className, "pb-4")} // Pad the bottom to look nice /> ); } @@ -89,8 +89,8 @@ function FormInputsStack>({ space={3} className={classNames( className, - 'h-full overflow-auto', - 'pr-1', // A bit of space between inputs and scrollbar + "h-full overflow-auto", + "pr-1", // A bit of space between inputs and scrollbar )} > @@ -100,7 +100,7 @@ function FormInputsStack>({ type FormInputsProps = Pick< Props, - 'inputs' | 'autocompleteFunctions' | 'autocompleteVariables' | 'stateKey' | 'data' + "inputs" | "autocompleteFunctions" | "autocompleteVariables" | "stateKey" | "data" > & { setDataAttr: (name: string, value: JsonPrimitive) => void; disabled?: boolean; @@ -118,16 +118,16 @@ function FormInputs>({ return ( <> {inputs?.map((input, i) => { - if ('hidden' in input && input.hidden) { + if ("hidden" in input && input.hidden) { return null; } - if ('disabled' in input && disabled != null) { + if ("disabled" in input && disabled != null) { input.disabled = disabled; } switch (input.type) { - case 'select': + case "select": return ( >({ } /> ); - case 'text': + case "text": return ( >({ autocompleteVariables={autocompleteVariables || false} onChange={(v) => setDataAttr(input.name, v)} value={ - data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '') + data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? "") } /> ); - case 'editor': + case "editor": return ( >({ autocompleteVariables={autocompleteVariables || false} onChange={(v) => setDataAttr(input.name, v)} value={ - data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '') + data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? "") } /> ); - case 'checkbox': + case "checkbox": return ( >({ value={data[input.name] != null ? data[input.name] === true : false} /> ); - case 'http_request': + case "http_request": return ( >({ value={data[input.name] != null ? String(data[input.name]) : DYNAMIC_FORM_NULL_ARG} /> ); - case 'file': + case "file": return ( >({ } /> ); - case 'accordion': + case "accordion": if (!hasVisibleInputs(input.inputs)) { return null; } @@ -205,7 +205,7 @@ function FormInputs>({
>({
); - case 'h_stack': + case "h_stack": if (!hasVisibleInputs(input.inputs)) { return null; } @@ -238,7 +238,7 @@ function FormInputs>({ />
); - case 'banner': + case "banner": if (!hasVisibleInputs(input.inputs)) { return null; } @@ -246,7 +246,7 @@ function FormInputs>({ >({ /> ); - case 'markdown': + case "markdown": return {input.content}; - case 'key_value': + case "key_value": return ( >({ stateKey={stateKey} onChange={(v) => setDataAttr(input.name, v)} value={ - data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '[]') + data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? "[]") } /> ); @@ -301,12 +301,12 @@ function TextArg({ onChange, name: arg.name, multiLine: arg.multiLine, - className: arg.multiLine ? 'min-h-[4rem]' : undefined, + className: arg.multiLine ? "min-h-[4rem]" : undefined, defaultValue: value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value, required: !arg.optional, disabled: arg.disabled, help: arg.description, - type: arg.password ? 'password' : 'text', + type: arg.password ? "password" : "text", label: arg.label ?? arg.name, size: INPUT_SIZE, hideLabel: arg.hideLabel ?? arg.label == null, @@ -358,9 +358,9 @@ function EditorArg({
@@ -390,10 +390,10 @@ function EditorArg({ title="Pop out to large editor" onClick={() => { showDialog({ - id: 'id', - size: 'full', - title: arg.readOnly ? 'View Value' : 'Edit Value', - className: '!max-w-[50rem] !max-h-[60rem]', + id: "id", + size: "full", + title: arg.readOnly ? "View Value" : "Edit Value", + className: "!max-w-[50rem] !max-h-[60rem]", description: arg.label && (
))} @@ -155,8 +155,8 @@ function ExportDataDialogContent({ disabled={noneSelected} onClick={() => handleExport()} > - Export{' '} - {pluralizeCount('Workspace', numSelected, { omitSingle: true, noneWord: 'Nothing' })} + Export{" "} + {pluralizeCount("Workspace", numSelected, { omitSingle: true, noneWord: "Nothing" })} diff --git a/src-web/components/FolderLayout.tsx b/src-web/components/FolderLayout.tsx index 8609d2a8..ffc1f0f7 100644 --- a/src-web/components/FolderLayout.tsx +++ b/src-web/components/FolderLayout.tsx @@ -1,28 +1,28 @@ -import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; -import { foldersAtom } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import type { CSSProperties, ReactNode } from 'react'; -import { useCallback, useMemo } from 'react'; -import { allRequestsAtom } from '../hooks/useAllRequests'; -import { useFolderActions } from '../hooks/useFolderActions'; -import { useLatestHttpResponse } from '../hooks/useLatestHttpResponse'; -import { sendAnyHttpRequest } from '../hooks/useSendAnyHttpRequest'; -import { fireAndForget } from '../lib/fireAndForget'; -import { showDialog } from '../lib/dialog'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { router } from '../lib/router'; -import { Button } from './core/Button'; -import { Heading } from './core/Heading'; -import { HttpResponseDurationTag } from './core/HttpResponseDurationTag'; -import { HttpStatusTag } from './core/HttpStatusTag'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { LoadingIcon } from './core/LoadingIcon'; -import { Separator } from './core/Separator'; -import { SizeTag } from './core/SizeTag'; -import { HStack } from './core/Stacks'; -import { HttpResponsePane } from './HttpResponsePane'; +import type { Folder, GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; +import { foldersAtom } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import type { CSSProperties, ReactNode } from "react"; +import { useCallback, useMemo } from "react"; +import { allRequestsAtom } from "../hooks/useAllRequests"; +import { useFolderActions } from "../hooks/useFolderActions"; +import { useLatestHttpResponse } from "../hooks/useLatestHttpResponse"; +import { sendAnyHttpRequest } from "../hooks/useSendAnyHttpRequest"; +import { fireAndForget } from "../lib/fireAndForget"; +import { showDialog } from "../lib/dialog"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { router } from "../lib/router"; +import { Button } from "./core/Button"; +import { Heading } from "./core/Heading"; +import { HttpResponseDurationTag } from "./core/HttpResponseDurationTag"; +import { HttpStatusTag } from "./core/HttpStatusTag"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { LoadingIcon } from "./core/LoadingIcon"; +import { Separator } from "./core/Separator"; +import { SizeTag } from "./core/SizeTag"; +import { HStack } from "./core/Stacks"; +import { HttpResponsePane } from "./HttpResponsePane"; interface Props { folder: Folder; @@ -34,7 +34,7 @@ export function FolderLayout({ folder, style }: Props) { const requests = useAtomValue(allRequestsAtom); const folderActions = useFolderActions(); const sendAllAction = useMemo( - () => folderActions.find((a) => a.label === 'Send All'), + () => folderActions.find((a) => a.label === "Send All"), [folderActions], ); @@ -79,13 +79,13 @@ export function FolderLayout({ folder, style }: Props) { function ChildCard({ child }: { child: Folder | HttpRequest | GrpcRequest | WebsocketRequest }) { let card: ReactNode; - if (child.model === 'folder') { + if (child.model === "folder") { card = ; - } else if (child.model === 'http_request') { + } else if (child.model === "http_request") { card = ; - } else if (child.model === 'grpc_request') { + } else if (child.model === "grpc_request") { card = ; - } else if (child.model === 'websocket_request') { + } else if (child.model === "websocket_request") { card = ; } else { card =
Unknown model
; @@ -93,7 +93,7 @@ function ChildCard({ child }: { child: Folder | HttpRequest | GrpcRequest | Webs const navigate = useCallback(async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: child.workspaceId }, search: (prev) => ({ ...prev, request_id: child.id }), }); @@ -102,12 +102,12 @@ function ChildCard({ child }: { child: Folder | HttpRequest | GrpcRequest | Webs return (
- {child.model === 'folder' && } + {child.model === "folder" && } {resolvedModelName(child)} @@ -144,7 +144,7 @@ function FolderCard({ folder }: { folder: Folder }) { color="primary" onClick={async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: folder.workspaceId }, search: (prev) => { return { ...prev, request_id: null, folder_id: folder.id }; @@ -178,10 +178,10 @@ function HttpRequestCard({ request }: { request: HttpRequest }) { onClick={(e) => { e.stopPropagation(); showDialog({ - id: 'response-preview', - title: 'Response Preview', - size: 'md', - className: 'h-full', + id: "response-preview", + title: "Response Preview", + size: "md", + className: "h-full", render: () => { return ; }, @@ -192,12 +192,12 @@ function HttpRequestCard({ request }: { request: HttpRequest }) { space={2} alignItems="center" className={classNames( - 'cursor-default select-none', - 'whitespace-nowrap w-full pl-3 overflow-x-auto font-mono text-sm hide-scrollbars', - 'font-mono text-editor border rounded px-1.5 py-0.5 truncate w-full', + "cursor-default select-none", + "whitespace-nowrap w-full pl-3 overflow-x-auto font-mono text-sm hide-scrollbars", + "font-mono text-editor border rounded px-1.5 py-0.5 truncate w-full", )} > - {latestResponse.state !== 'closed' && } + {latestResponse.state !== "closed" && } diff --git a/src-web/components/FolderSettingsDialog.tsx b/src-web/components/FolderSettingsDialog.tsx index 8b2c0bc7..50d96dd3 100644 --- a/src-web/components/FolderSettingsDialog.tsx +++ b/src-web/components/FolderSettingsDialog.tsx @@ -1,42 +1,38 @@ -import { - createWorkspaceModel, - foldersAtom, - patchModel, -} from '@yaakapp-internal/models'; -import { useAtomValue } from 'jotai'; -import { Fragment, useMemo } from 'react'; -import { useAuthTab } from '../hooks/useAuthTab'; -import { useEnvironmentsBreakdown } from '../hooks/useEnvironmentsBreakdown'; -import { useHeadersTab } from '../hooks/useHeadersTab'; -import { useInheritedHeaders } from '../hooks/useInheritedHeaders'; -import { useModelAncestors } from '../hooks/useModelAncestors'; -import { deleteModelWithConfirm } from '../lib/deleteModelWithConfirm'; -import { hideDialog } from '../lib/dialog'; -import { CopyIconButton } from './CopyIconButton'; -import { Button } from './core/Button'; -import { CountBadge } from './core/CountBadge'; -import { Icon } from './core/Icon'; -import { InlineCode } from './core/InlineCode'; -import { Input } from './core/Input'; -import { Link } from './core/Link'; -import { HStack, VStack } from './core/Stacks'; -import type { TabItem } from './core/Tabs/Tabs'; -import { TabContent, Tabs } from './core/Tabs/Tabs'; -import { EmptyStateText } from './EmptyStateText'; -import { EnvironmentEditor } from './EnvironmentEditor'; -import { HeadersEditor } from './HeadersEditor'; -import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; -import { MarkdownEditor } from './MarkdownEditor'; +import { createWorkspaceModel, foldersAtom, patchModel } from "@yaakapp-internal/models"; +import { useAtomValue } from "jotai"; +import { Fragment, useMemo } from "react"; +import { useAuthTab } from "../hooks/useAuthTab"; +import { useEnvironmentsBreakdown } from "../hooks/useEnvironmentsBreakdown"; +import { useHeadersTab } from "../hooks/useHeadersTab"; +import { useInheritedHeaders } from "../hooks/useInheritedHeaders"; +import { useModelAncestors } from "../hooks/useModelAncestors"; +import { deleteModelWithConfirm } from "../lib/deleteModelWithConfirm"; +import { hideDialog } from "../lib/dialog"; +import { CopyIconButton } from "./CopyIconButton"; +import { Button } from "./core/Button"; +import { CountBadge } from "./core/CountBadge"; +import { Icon } from "./core/Icon"; +import { InlineCode } from "./core/InlineCode"; +import { Input } from "./core/Input"; +import { Link } from "./core/Link"; +import { HStack, VStack } from "./core/Stacks"; +import type { TabItem } from "./core/Tabs/Tabs"; +import { TabContent, Tabs } from "./core/Tabs/Tabs"; +import { EmptyStateText } from "./EmptyStateText"; +import { EnvironmentEditor } from "./EnvironmentEditor"; +import { HeadersEditor } from "./HeadersEditor"; +import { HttpAuthenticationEditor } from "./HttpAuthenticationEditor"; +import { MarkdownEditor } from "./MarkdownEditor"; interface Props { folderId: string | null; tab?: FolderSettingsTab; } -const TAB_AUTH = 'auth'; -const TAB_HEADERS = 'headers'; -const TAB_VARIABLES = 'variables'; -const TAB_GENERAL = 'general'; +const TAB_AUTH = "auth"; +const TAB_HEADERS = "headers"; +const TAB_VARIABLES = "variables"; +const TAB_GENERAL = "general"; export type FolderSettingsTab = | typeof TAB_AUTH @@ -54,7 +50,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { const inheritedHeaders = useInheritedHeaders(folder); const environments = useEnvironmentsBreakdown(); const folderEnvironment = environments.allEnvironments.find( - (e) => e.parentModel === 'folder' && e.parentId === folderId, + (e) => e.parentModel === "folder" && e.parentId === folderId, ); const numVars = (folderEnvironment?.variables ?? []).filter((v) => v.name).length; @@ -64,13 +60,13 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { return [ { value: TAB_GENERAL, - label: 'General', + label: "General", }, ...headersTab, ...authTab, { value: TAB_VARIABLES, - label: 'Variables', + label: "Variables", rightSlot: numVars > 0 ? : null, }, ]; @@ -86,11 +82,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { {breadcrumbs.map((item, index) => ( {index > 0 && ( - + )} {item.name} @@ -100,10 +92,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { {breadcrumbs.length > 0 && ( )} - + {folder.name}
@@ -141,7 +130,7 @@ export function FolderSettingsDialog({ folderId, tab }: Props) { onClick={async () => { const didDelete = await deleteModelWithConfirm(folder); if (didDelete) { - hideDialog('folder-settings'); + hideDialog("folder-settings"); } }} color="danger" @@ -177,10 +166,10 @@ export function FolderSettingsDialog({ folderId, tab }: Props) {

- Override{' '} + Override{" "} Variables - {' '} + {" "} for requests within this folder.

, ], diff --git a/src-web/components/GrpcProtoSelectionDialog.tsx b/src-web/components/GrpcProtoSelectionDialog.tsx index 83b4d27c..6da5ff15 100644 --- a/src-web/components/GrpcProtoSelectionDialog.tsx +++ b/src-web/components/GrpcProtoSelectionDialog.tsx @@ -1,16 +1,16 @@ -import { open } from '@tauri-apps/plugin-dialog'; -import type { GrpcRequest } from '@yaakapp-internal/models'; -import { useActiveRequest } from '../hooks/useActiveRequest'; -import { useGrpc } from '../hooks/useGrpc'; -import { useGrpcProtoFiles } from '../hooks/useGrpcProtoFiles'; -import { pluralizeCount } from '../lib/pluralize'; -import { Banner } from './core/Banner'; -import { Button } from './core/Button'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { InlineCode } from './core/InlineCode'; -import { Link } from './core/Link'; -import { HStack, VStack } from './core/Stacks'; +import { open } from "@tauri-apps/plugin-dialog"; +import type { GrpcRequest } from "@yaakapp-internal/models"; +import { useActiveRequest } from "../hooks/useActiveRequest"; +import { useGrpc } from "../hooks/useGrpc"; +import { useGrpcProtoFiles } from "../hooks/useGrpcProtoFiles"; +import { pluralizeCount } from "../lib/pluralize"; +import { Banner } from "./core/Banner"; +import { Button } from "./core/Button"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { InlineCode } from "./core/InlineCode"; +import { Link } from "./core/Link"; +import { HStack, VStack } from "./core/Stacks"; interface Props { onDone: () => void; @@ -18,7 +18,7 @@ interface Props { export function GrpcProtoSelectionDialog(props: Props) { const request = useActiveRequest(); - if (request?.model !== 'grpc_request') return null; + if (request?.model !== "grpc_request") return null; return ; } @@ -49,9 +49,9 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp variant="border" onClick={async () => { const selected = await open({ - title: 'Select Proto Files', + title: "Select Proto Files", multiple: true, - filters: [{ name: 'Proto Files', extensions: ['proto'] }], + filters: [{ name: "Proto Files", extensions: ["proto"] }], }); if (selected == null) return; @@ -67,7 +67,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp color="primary" onClick={async () => { const selected = await open({ - title: 'Select Proto Directory', + title: "Select Proto Directory", directory: true, }); if (selected == null) return; @@ -92,7 +92,7 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp {reflectError && (

- Reflection failed on URL {request.url || 'n/a'} + Reflection failed on URL {request.url || "n/a"}

{reflectError.trim()}

@@ -100,16 +100,16 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp {!serverReflection && services != null && services.length > 0 && (

- Found services{' '} + Found services{" "} {services?.slice(0, 5).map((s, i) => { return ( - m.name).join(',')}> + m.name).join(",")}> {s.name} - {i === services.length - 1 ? '' : i === services.length - 2 ? ' and ' : ', '} + {i === services.length - 1 ? "" : i === services.length - 2 ? " and " : ", "} ); })} - {services?.length > 5 && pluralizeCount('other', services?.length - 5)} + {services?.length > 5 && pluralizeCount("other", services?.length - 5)}

)} @@ -119,13 +119,13 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp Server reflection found services {services?.map((s, i) => { return ( - m.name).join(',')}> + m.name).join(",")}> {s.name} - {i === services.length - 1 ? '' : i === services.length - 2 ? ' and ' : ', '} + {i === services.length - 1 ? "" : i === services.length - 2 ? " and " : ", "} ); })} - . You can override this schema by manually selecting *.proto{' '} + . You can override this schema by manually selecting *.proto{" "} files.

@@ -142,16 +142,16 @@ function GrpcProtoSelectionDialogWithRequest({ request }: Props & { request: Grp
{protoFiles.map((f, i) => { - const parts = f.split('/'); + const parts = f.split("/"); return ( // oxlint-disable-next-line react/no-array-index-key
({ ...prev, [w.id]: !prev[w.id] })) } > - {w.name} {w.id === activeWorkspace.id ? '(current workspace)' : ''} + {w.name} {w.id === activeWorkspace.id ? "(current workspace)" : ""}
- + - {parts.length > 3 && '.../'} - {parts.slice(-3).join('/')} + {parts.length > 3 && ".../"} + {parts.slice(-3).join("/")} - {request.url} doesn't implement{' '} + {request.url} doesn't implement{" "} Server Reflection - {' '} + {" "} . Please manually add the .proto file to get started. )} diff --git a/src-web/components/GrpcRequestPane.tsx b/src-web/components/GrpcRequestPane.tsx index 3db03ecf..cae4ec48 100644 --- a/src-web/components/GrpcRequestPane.tsx +++ b/src-web/components/GrpcRequestPane.tsx @@ -1,28 +1,28 @@ -import { type GrpcRequest, type HttpRequestHeader, patchModel } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import type { CSSProperties } from 'react'; -import { useCallback, useMemo, useRef } from 'react'; -import { useAuthTab } from '../hooks/useAuthTab'; -import { useContainerSize } from '../hooks/useContainerQuery'; -import type { ReflectResponseService } from '../hooks/useGrpc'; -import { useHeadersTab } from '../hooks/useHeadersTab'; -import { useInheritedHeaders } from '../hooks/useInheritedHeaders'; -import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { Button } from './core/Button'; -import { CountBadge } from './core/CountBadge'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { PlainInput } from './core/PlainInput'; -import { RadioDropdown } from './core/RadioDropdown'; -import { HStack, VStack } from './core/Stacks'; -import type { TabItem } from './core/Tabs/Tabs'; -import { TabContent, Tabs } from './core/Tabs/Tabs'; -import { GrpcEditor } from './GrpcEditor'; -import { HeadersEditor } from './HeadersEditor'; -import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; -import { MarkdownEditor } from './MarkdownEditor'; -import { UrlBar } from './UrlBar'; +import { type GrpcRequest, type HttpRequestHeader, patchModel } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import type { CSSProperties } from "react"; +import { useCallback, useMemo, useRef } from "react"; +import { useAuthTab } from "../hooks/useAuthTab"; +import { useContainerSize } from "../hooks/useContainerQuery"; +import type { ReflectResponseService } from "../hooks/useGrpc"; +import { useHeadersTab } from "../hooks/useHeadersTab"; +import { useInheritedHeaders } from "../hooks/useInheritedHeaders"; +import { useRequestUpdateKey } from "../hooks/useRequestUpdateKey"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { Button } from "./core/Button"; +import { CountBadge } from "./core/CountBadge"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { PlainInput } from "./core/PlainInput"; +import { RadioDropdown } from "./core/RadioDropdown"; +import { HStack, VStack } from "./core/Stacks"; +import type { TabItem } from "./core/Tabs/Tabs"; +import { TabContent, Tabs } from "./core/Tabs/Tabs"; +import { GrpcEditor } from "./GrpcEditor"; +import { HeadersEditor } from "./HeadersEditor"; +import { HttpAuthenticationEditor } from "./HttpAuthenticationEditor"; +import { MarkdownEditor } from "./MarkdownEditor"; +import { UrlBar } from "./UrlBar"; interface Props { style?: CSSProperties; @@ -32,12 +32,12 @@ interface Props { reflectionError?: string; reflectionLoading?: boolean; methodType: - | 'unary' - | 'client_streaming' - | 'server_streaming' - | 'streaming' - | 'no-schema' - | 'no-method'; + | "unary" + | "client_streaming" + | "server_streaming" + | "streaming" + | "no-schema" + | "no-method"; isStreaming: boolean; onCommit: () => void; onCancel: () => void; @@ -46,10 +46,10 @@ interface Props { services: ReflectResponseService[] | null; } -const TAB_MESSAGE = 'message'; -const TAB_METADATA = 'metadata'; -const TAB_AUTH = 'auth'; -const TAB_DESCRIPTION = 'description'; +const TAB_MESSAGE = "message"; +const TAB_METADATA = "metadata"; +const TAB_AUTH = "auth"; +const TAB_DESCRIPTION = "description"; export function GrpcRequestPane({ style, @@ -66,7 +66,7 @@ export function GrpcRequestPane({ onSend, }: Props) { const authTab = useAuthTab(TAB_AUTH, activeRequest); - const metadataTab = useHeadersTab(TAB_METADATA, activeRequest, 'Metadata'); + const metadataTab = useHeadersTab(TAB_METADATA, activeRequest, "Metadata"); const inheritedHeaders = useInheritedHeaders(activeRequest); const forceUpdateKey = useRequestUpdateKey(activeRequest.id ?? null); @@ -87,18 +87,18 @@ export function GrpcRequestPane({ const options = services?.flatMap((s) => s.methods.map((m) => ({ - label: `${s.name.split('.').pop() ?? s.name}/${m.name}`, + label: `${s.name.split(".").pop() ?? s.name}/${m.name}`, value: `${s.name}/${m.name}`, })), ) ?? []; - const value = `${activeRequest?.service ?? ''}/${activeRequest?.method ?? ''}`; + const value = `${activeRequest?.service ?? ""}/${activeRequest?.method ?? ""}`; return { value, options }; }, [activeRequest?.method, activeRequest?.service, services]); const handleChangeService = useCallback( async (v: string) => { - const [serviceName, methodName] = v.split('/', 2); - if (serviceName == null || methodName == null) throw new Error('Should never happen'); + const [serviceName, methodName] = v.split("/", 2); + if (serviceName == null || methodName == null) throw new Error("Should never happen"); await patchModel(activeRequest, { service: serviceName, method: methodName, @@ -112,9 +112,9 @@ export function GrpcRequestPane({ if (activeRequest.service == null || activeRequest.method == null) { alert({ - id: 'grpc-invalid-service-method', - title: 'Error', - body: 'Service or method not selected', + id: "grpc-invalid-service-method", + title: "Error", + body: "Service or method not selected", }); } onGo(); @@ -127,12 +127,12 @@ export function GrpcRequestPane({ const tabs: TabItem[] = useMemo( () => [ - { value: TAB_MESSAGE, label: 'Message' }, + { value: TAB_MESSAGE, label: "Message" }, ...metadataTab, ...authTab, { value: TAB_DESCRIPTION, - label: 'Info', + label: "Info", rightSlot: activeRequest.description && , }, ], @@ -154,14 +154,14 @@ export function GrpcRequestPane({
0 && paneWidth < 400 && '!grid-cols-1', + "grid grid-cols-[minmax(0,1fr)_auto] gap-1.5", + paneWidth === 0 && "opacity-0", + paneWidth > 0 && paneWidth < 400 && "!grid-cols-1", )} > ({ label: o.label, value: o.value, - type: 'default', + type: "default", shortLabel: o.label, }))} itemsAfter={[ { - label: 'Refresh', - type: 'default', + label: "Refresh", + type: "default", leftSlot: , }, ]} @@ -195,14 +195,14 @@ export function GrpcRequestPane({ rightSlot={} disabled={isStreaming || services == null} className={classNames( - 'font-mono text-editor min-w-[5rem] !ring-0', - paneWidth < 400 && 'flex-1', + "font-mono text-editor min-w-[5rem] !ring-0", + paneWidth < 400 && "flex-1", )} > - {select.options.find((o) => o.value === select.value)?.label ?? 'No Schema'} + {select.options.find((o) => o.value === select.value)?.label ?? "No Schema"} - {methodType === 'client_streaming' || methodType === 'streaming' ? ( + {methodType === "client_streaming" || methodType === "streaming" ? ( <> {isStreaming && ( <> @@ -225,26 +225,26 @@ export function GrpcRequestPane({ ) : ( )} diff --git a/src-web/components/GrpcResponsePane.tsx b/src-web/components/GrpcResponsePane.tsx index ae01dd8f..5d2099d3 100644 --- a/src-web/components/GrpcResponsePane.tsx +++ b/src-web/components/GrpcResponsePane.tsx @@ -1,38 +1,38 @@ -import type { GrpcEvent, GrpcRequest } from '@yaakapp-internal/models'; -import { useAtomValue, useSetAtom } from 'jotai'; -import type { CSSProperties } from 'react'; -import { useEffect, useMemo, useState } from 'react'; +import type { GrpcEvent, GrpcRequest } from "@yaakapp-internal/models"; +import { useAtomValue, useSetAtom } from "jotai"; +import type { CSSProperties } from "react"; +import { useEffect, useMemo, useState } from "react"; import { activeGrpcConnectionAtom, activeGrpcConnections, pinnedGrpcConnectionIdAtom, useGrpcEvents, -} from '../hooks/usePinnedGrpcConnection'; -import { useStateWithDeps } from '../hooks/useStateWithDeps'; -import { Button } from './core/Button'; -import { Editor } from './core/Editor/LazyEditor'; -import { EventDetailHeader, EventViewer } from './core/EventViewer'; -import { EventViewerRow } from './core/EventViewerRow'; -import { HotkeyList } from './core/HotkeyList'; -import { Icon, type IconProps } from './core/Icon'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; -import { LoadingIcon } from './core/LoadingIcon'; -import { HStack, VStack } from './core/Stacks'; -import { EmptyStateText } from './EmptyStateText'; -import { ErrorBoundary } from './ErrorBoundary'; -import { RecentGrpcConnectionsDropdown } from './RecentGrpcConnectionsDropdown'; +} from "../hooks/usePinnedGrpcConnection"; +import { useStateWithDeps } from "../hooks/useStateWithDeps"; +import { Button } from "./core/Button"; +import { Editor } from "./core/Editor/LazyEditor"; +import { EventDetailHeader, EventViewer } from "./core/EventViewer"; +import { EventViewerRow } from "./core/EventViewerRow"; +import { HotkeyList } from "./core/HotkeyList"; +import { Icon, type IconProps } from "./core/Icon"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; +import { LoadingIcon } from "./core/LoadingIcon"; +import { HStack, VStack } from "./core/Stacks"; +import { EmptyStateText } from "./EmptyStateText"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { RecentGrpcConnectionsDropdown } from "./RecentGrpcConnectionsDropdown"; interface Props { style?: CSSProperties; className?: string; activeRequest: GrpcRequest; methodType: - | 'unary' - | 'client_streaming' - | 'server_streaming' - | 'streaming' - | 'no-schema' - | 'no-method'; + | "unary" + | "client_streaming" + | "server_streaming" + | "streaming" + | "no-schema" + | "no-method"; } export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { @@ -52,10 +52,10 @@ export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { // Set the active message to the first message received if unary // oxlint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { - if (events.length === 0 || activeEvent != null || methodType !== 'unary') { + if (events.length === 0 || activeEvent != null || methodType !== "unary") { return; } - const firstServerMessageIndex = events.findIndex((m) => m.eventType === 'server_message'); + const firstServerMessageIndex = events.findIndex((m) => m.eventType === "server_message"); if (firstServerMessageIndex !== -1) { setActiveEventIndex(firstServerMessageIndex); } @@ -63,7 +63,7 @@ export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { if (activeConnection == null) { return ( - + ); } @@ -71,7 +71,7 @@ export function GrpcResponsePane({ style, methodType, activeRequest }: Props) { {events.length} Messages - {activeConnection.state !== 'closed' && ( + {activeConnection.state !== "closed" && ( )} @@ -157,8 +157,8 @@ function GrpcEventDetail({ setShowingLarge: (v: boolean) => void; onClose: () => void; }) { - if (event.eventType === 'client_message' || event.eventType === 'server_message') { - const title = `Message ${event.eventType === 'client_message' ? 'Sent' : 'Received'}`; + if (event.eventType === "client_message" || event.eventType === "server_message") { + const title = `Message ${event.eventType === "client_message" ? "Sent" : "Received"}`; return (
@@ -192,7 +192,7 @@ function GrpcEventDetail({ ) : ( {Object.keys(event.metadata).length === 0 ? ( - No {event.eventType === 'connection_end' ? 'trailers' : 'metadata'} + No {event.eventType === "connection_end" ? "trailers" : "metadata"} ) : ( @@ -231,20 +231,20 @@ function GrpcEventDetail({ } function getEventDisplay( - eventType: GrpcEvent['eventType'], - status: GrpcEvent['status'], -): { icon: IconProps['icon']; color: IconProps['color']; title: string } { - if (eventType === 'server_message') { - return { icon: 'arrow_big_down_dash', color: 'info', title: 'Server message' }; + eventType: GrpcEvent["eventType"], + status: GrpcEvent["status"], +): { icon: IconProps["icon"]; color: IconProps["color"]; title: string } { + if (eventType === "server_message") { + return { icon: "arrow_big_down_dash", color: "info", title: "Server message" }; } - if (eventType === 'client_message') { - return { icon: 'arrow_big_up_dash', color: 'primary', title: 'Client message' }; + if (eventType === "client_message") { + return { icon: "arrow_big_up_dash", color: "primary", title: "Client message" }; } - if (eventType === 'error' || (status != null && status > 0)) { - return { icon: 'alert_triangle', color: 'danger', title: 'Error' }; + if (eventType === "error" || (status != null && status > 0)) { + return { icon: "alert_triangle", color: "danger", title: "Error" }; } - if (eventType === 'connection_end') { - return { icon: 'check', color: 'success', title: 'Connection response' }; + if (eventType === "connection_end") { + return { icon: "check", color: "success", title: "Connection response" }; } - return { icon: 'info', color: undefined, title: 'Event' }; + return { icon: "info", color: undefined, title: "Event" }; } diff --git a/src-web/components/HeaderSize.tsx b/src-web/components/HeaderSize.tsx index 5944e011..200502e5 100644 --- a/src-web/components/HeaderSize.tsx +++ b/src-web/components/HeaderSize.tsx @@ -1,16 +1,16 @@ -import { type } from '@tauri-apps/plugin-os'; -import { settingsAtom } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import type { CSSProperties, HTMLAttributes, ReactNode } from 'react'; -import { useMemo } from 'react'; -import { useIsFullscreen } from '../hooks/useIsFullscreen'; -import { HEADER_SIZE_LG, HEADER_SIZE_MD, WINDOW_CONTROLS_WIDTH } from '../lib/constants'; -import { WindowControls } from './WindowControls'; +import { type } from "@tauri-apps/plugin-os"; +import { settingsAtom } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import type { CSSProperties, HTMLAttributes, ReactNode } from "react"; +import { useMemo } from "react"; +import { useIsFullscreen } from "../hooks/useIsFullscreen"; +import { HEADER_SIZE_LG, HEADER_SIZE_MD, WINDOW_CONTROLS_WIDTH } from "../lib/constants"; +import { WindowControls } from "./WindowControls"; interface HeaderSizeProps extends HTMLAttributes { children?: ReactNode; - size: 'md' | 'lg'; + size: "md" | "lg"; ignoreControlsSpacing?: boolean; onlyXWindowControl?: boolean; hideControls?: boolean; @@ -32,12 +32,12 @@ export function HeaderSize({ const s = { ...style }; // Set the height (use min-height because scaling font size may make it larger - if (size === 'md') s.minHeight = HEADER_SIZE_MD; - if (size === 'lg') s.minHeight = HEADER_SIZE_LG; + if (size === "md") s.minHeight = HEADER_SIZE_MD; + if (size === "lg") s.minHeight = HEADER_SIZE_LG; if (nativeTitlebar) { // No style updates when using native titlebar - } else if (type() === 'macos') { + } else if (type() === "macos") { if (!isFullscreen) { // Add large padding for window controls s.paddingLeft = 76 / settings.interfaceScale; @@ -63,16 +63,16 @@ export function HeaderSize({ style={finalStyle} className={classNames( className, - 'pt-[1px]', // Make up for bottom border - 'select-none relative', - 'w-full border-b border-border-subtle min-w-0', + "pt-[1px]", // Make up for bottom border + "select-none relative", + "w-full border-b border-border-subtle min-w-0", )} > {/* NOTE: This needs display:grid or else the element shrinks (even though scrollable) */}
{children} diff --git a/src-web/components/HeadersEditor.tsx b/src-web/components/HeadersEditor.tsx index 05d08ac5..2f43525f 100644 --- a/src-web/components/HeadersEditor.tsx +++ b/src-web/components/HeadersEditor.tsx @@ -1,19 +1,19 @@ -import type { HttpRequestHeader } from '@yaakapp-internal/models'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import { charsets } from '../lib/data/charsets'; -import { connections } from '../lib/data/connections'; -import { encodings } from '../lib/data/encodings'; -import { headerNames } from '../lib/data/headerNames'; -import { mimeTypes } from '../lib/data/mimetypes'; -import { CountBadge } from './core/CountBadge'; -import { DetailsBanner } from './core/DetailsBanner'; -import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; -import type { InputProps } from './core/Input'; -import type { Pair, PairEditorProps } from './core/PairEditor'; -import { PairEditorRow } from './core/PairEditor'; -import { ensurePairId } from './core/PairEditor.util'; -import { PairOrBulkEditor } from './core/PairOrBulkEditor'; -import { HStack } from './core/Stacks'; +import type { HttpRequestHeader } from "@yaakapp-internal/models"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import { charsets } from "../lib/data/charsets"; +import { connections } from "../lib/data/connections"; +import { encodings } from "../lib/data/encodings"; +import { headerNames } from "../lib/data/headerNames"; +import { mimeTypes } from "../lib/data/mimetypes"; +import { CountBadge } from "./core/CountBadge"; +import { DetailsBanner } from "./core/DetailsBanner"; +import type { GenericCompletionConfig } from "./core/Editor/genericCompletion"; +import type { InputProps } from "./core/Input"; +import type { Pair, PairEditorProps } from "./core/PairEditor"; +import { PairEditorRow } from "./core/PairEditor"; +import { ensurePairId } from "./core/PairEditor.util"; +import { PairOrBulkEditor } from "./core/PairOrBulkEditor"; +import { HStack } from "./core/Stacks"; type Props = { forceUpdateKey: string; @@ -29,7 +29,7 @@ export function HeadersEditor({ stateKey, headers, inheritedHeaders, - inheritedHeadersLabel = 'Inherited', + inheritedHeadersLabel = "Inherited", onChange, forceUpdateKey, }: Props) { @@ -41,15 +41,17 @@ export function HeadersEditor({ const validInheritedHeaders = inheritedHeaders?.filter( (pair) => - pair.enabled && (pair.name || pair.value) && !currentHeaderNames.has(pair.name.toLowerCase()), + pair.enabled && + (pair.name || pair.value) && + !currentHeaderNames.has(pair.name.toLowerCase()), ) ?? []; const hasInheritedHeaders = validInheritedHeaders.length > 0; return (
{hasInheritedHeaders && ( @@ -104,28 +106,28 @@ export function HeadersEditor({ const MIN_MATCH = 3; const headerOptionsMap: Record = { - 'content-type': mimeTypes, - accept: ['*/*', ...mimeTypes], - 'accept-encoding': encodings, + "content-type": mimeTypes, + accept: ["*/*", ...mimeTypes], + "accept-encoding": encodings, connection: connections, - 'accept-charset': charsets, + "accept-charset": charsets, }; -const valueType = (pair: Pair): InputProps['type'] => { +const valueType = (pair: Pair): InputProps["type"] => { const name = pair.name.toLowerCase().trim(); if ( - name.includes('authorization') || - name.includes('api-key') || - name.includes('access-token') || - name.includes('auth') || - name.includes('secret') || - name.includes('token') || - name === 'cookie' || - name === 'set-cookie' + name.includes("authorization") || + name.includes("api-key") || + name.includes("access-token") || + name.includes("auth") || + name.includes("secret") || + name.includes("token") || + name === "cookie" || + name === "set-cookie" ) { - return 'password'; + return "password"; } - return 'text'; + return "text"; }; const valueAutocomplete = (headerName: string): GenericCompletionConfig | undefined => { @@ -133,19 +135,19 @@ const valueAutocomplete = (headerName: string): GenericCompletionConfig | undefi const options: GenericCompletionOption[] = headerOptionsMap[name]?.map((o) => ({ label: o, - type: 'constant', + type: "constant", boost: 1, // Put above other completions })) ?? []; return { minMatch: MIN_MATCH, options }; }; -const nameAutocomplete: PairEditorProps['nameAutocomplete'] = { +const nameAutocomplete: PairEditorProps["nameAutocomplete"] = { minMatch: MIN_MATCH, options: headerNames.map((t) => - typeof t === 'string' + typeof t === "string" ? { label: t, - type: 'constant', + type: "constant", boost: 1, // Put above other completions } : { @@ -156,11 +158,11 @@ const nameAutocomplete: PairEditorProps['nameAutocomplete'] = { }; const validateHttpHeader = (v: string) => { - if (v === '') { + if (v === "") { return true; } // Template strings are not allowed so we replace them with a valid example string - const withoutTemplateStrings = v.replace(/\$\{\[\s*[^\]\s]+\s*]}/gi, '123'); + const withoutTemplateStrings = v.replace(/\$\{\[\s*[^\]\s]+\s*]}/gi, "123"); return withoutTemplateStrings.match(/^[a-zA-Z0-9-_]+$/) !== null; }; diff --git a/src-web/components/HttpAuthenticationEditor.tsx b/src-web/components/HttpAuthenticationEditor.tsx index 56e11068..8a7a2031 100644 --- a/src-web/components/HttpAuthenticationEditor.tsx +++ b/src-web/components/HttpAuthenticationEditor.tsx @@ -4,25 +4,25 @@ import type { HttpRequest, WebsocketRequest, Workspace, -} from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import { useCallback } from 'react'; -import { openFolderSettings } from '../commands/openFolderSettings'; -import { openWorkspaceSettings } from '../commands/openWorkspaceSettings'; -import { useHttpAuthenticationConfig } from '../hooks/useHttpAuthenticationConfig'; -import { useInheritedAuthentication } from '../hooks/useInheritedAuthentication'; -import { useRenderTemplate } from '../hooks/useRenderTemplate'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { Dropdown, type DropdownItem } from './core/Dropdown'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { InlineCode } from './core/InlineCode'; -import { Input, type InputProps } from './core/Input'; -import { Link } from './core/Link'; -import { SegmentedControl } from './core/SegmentedControl'; -import { HStack } from './core/Stacks'; -import { DynamicForm } from './DynamicForm'; -import { EmptyStateText } from './EmptyStateText'; +} from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import { useCallback } from "react"; +import { openFolderSettings } from "../commands/openFolderSettings"; +import { openWorkspaceSettings } from "../commands/openWorkspaceSettings"; +import { useHttpAuthenticationConfig } from "../hooks/useHttpAuthenticationConfig"; +import { useInheritedAuthentication } from "../hooks/useInheritedAuthentication"; +import { useRenderTemplate } from "../hooks/useRenderTemplate"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { Dropdown, type DropdownItem } from "./core/Dropdown"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { InlineCode } from "./core/InlineCode"; +import { Input, type InputProps } from "./core/Input"; +import { Link } from "./core/Link"; +import { SegmentedControl } from "./core/SegmentedControl"; +import { HStack } from "./core/Stacks"; +import { DynamicForm } from "./DynamicForm"; +import { EmptyStateText } from "./EmptyStateText"; interface Props { model: HttpRequest | GrpcRequest | WebsocketRequest | Folder | Workspace; @@ -41,7 +41,7 @@ export function HttpAuthenticationEditor({ model }: Props) { [model], ); - if (model.authenticationType === 'none') { + if (model.authenticationType === "none") { return No authentication; } @@ -56,7 +56,7 @@ export function HttpAuthenticationEditor({ model }: Props) { } if (inheritedAuth == null) { - if (model.model === 'workspace' || model.model === 'folder') { + if (model.model === "workspace" || model.model === "folder") { return (

@@ -69,24 +69,24 @@ export function HttpAuthenticationEditor({ model }: Props) { return No authentication; } - if (inheritedAuth.authenticationType === 'none') { + if (inheritedAuth.authenticationType === "none") { return No authentication; } const wasAuthInherited = inheritedAuth?.id !== model.id; if (wasAuthInherited) { const name = resolvedModelName(inheritedAuth); - const cta = inheritedAuth.model === 'workspace' ? 'Workspace' : name; + const cta = inheritedAuth.model === "workspace" ? "Workspace" : name; return (

- Inherited from{' '} + Inherited from{" "} - ) : activeResponse.state === 'closed' && + ) : activeResponse.state === "closed" && (activeResponse.contentLength ?? 0) === 0 ? ( Empty - ) : mimeType?.match(/^text\/event-stream/i) && viewMode === 'pretty' ? ( + ) : mimeType?.match(/^text\/event-stream/i) && viewMode === "pretty" ? ( ) : mimeType?.match(/^image\/svg/) ? ( @@ -294,17 +294,17 @@ export function HttpResponsePane({ style, className, activeRequestId }: Props) { ) : mimeType?.match(/^video/i) ? ( - ) : mimeType?.match(/^multipart/i) && viewMode === 'pretty' ? ( + ) : mimeType?.match(/^multipart/i) && viewMode === "pretty" ? ( ) : mimeType?.match(/pdf/i) ? ( - ) : mimeType?.match(/csv|tab-separated/i) && viewMode === 'pretty' ? ( + ) : mimeType?.match(/csv|tab-separated/i) && viewMode === "pretty" ? ( ) : ( )} @@ -342,7 +342,7 @@ function getRedirectDropWarning( const droppedHeaders = new Set(); for (const e of events) { const event = e.event; - if (event.type !== 'redirect') { + if (event.type !== "redirect") { continue; } @@ -373,12 +373,12 @@ function pushHeaderName(headers: Set, headerName: string): void { function getRedirectWarningLabel(warning: RedirectDropWarning): string { if (warning.droppedBodyCount > 0 && warning.droppedHeaders.length > 0) { - return 'Dropped body and headers'; + return "Dropped body and headers"; } if (warning.droppedBodyCount > 0) { - return 'Dropped body'; + return "Dropped body"; } - return 'Dropped headers'; + return "Dropped headers"; } function EnsureCompleteResponse({ @@ -393,7 +393,7 @@ function EnsureCompleteResponse({ } // Wait until the response has been fully-downloaded - if (response.state !== 'closed') { + if (response.state !== "closed") { return ( @@ -424,7 +424,7 @@ function HttpMultipartViewer({ response }: { response: HttpResponse }) { if (body.data == null) return null; const contentTypeHeader = getContentTypeFromHeaders(response.headers); - const boundary = contentTypeHeader?.split('boundary=')[1] ?? 'unknown'; + const boundary = contentTypeHeader?.split("boundary=")[1] ?? "unknown"; return ; } diff --git a/src-web/components/HttpResponseTimeline.tsx b/src-web/components/HttpResponseTimeline.tsx index 22840853..050ca9df 100644 --- a/src-web/components/HttpResponseTimeline.tsx +++ b/src-web/components/HttpResponseTimeline.tsx @@ -2,16 +2,16 @@ import type { HttpResponse, HttpResponseEvent, HttpResponseEventData, -} from '@yaakapp-internal/models'; -import { type ReactNode, useMemo, useState } from 'react'; -import { useHttpResponseEvents } from '../hooks/useHttpResponseEvents'; -import { Editor } from './core/Editor/LazyEditor'; -import { type EventDetailAction, EventDetailHeader, EventViewer } from './core/EventViewer'; -import { EventViewerRow } from './core/EventViewerRow'; -import { HttpStatusTagRaw } from './core/HttpStatusTag'; -import { Icon, type IconProps } from './core/Icon'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; -import type { TimelineViewMode } from './HttpResponsePane'; +} from "@yaakapp-internal/models"; +import { type ReactNode, useMemo, useState } from "react"; +import { useHttpResponseEvents } from "../hooks/useHttpResponseEvents"; +import { Editor } from "./core/Editor/LazyEditor"; +import { type EventDetailAction, EventDetailHeader, EventViewer } from "./core/EventViewer"; +import { EventViewerRow } from "./core/EventViewerRow"; +import { HttpStatusTagRaw } from "./core/HttpStatusTag"; +import { Icon, type IconProps } from "./core/Icon"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; +import type { TimelineViewMode } from "./HttpResponsePane"; interface Props { response: HttpResponse; @@ -28,12 +28,12 @@ function Inner({ response, viewMode }: Props) { // Generate plain text representation of all events (with prefixes for timeline view) const plainText = useMemo(() => { - if (!events || events.length === 0) return ''; - return events.map((event) => formatEventText(event.event, true)).join('\n'); + if (!events || events.length === 0) return ""; + return events.map((event) => formatEventText(event.event, true)).join("\n"); }, [events]); // Plain text view - show all events as text in an editor - if (viewMode === 'text') { + if (viewMode === "text") { if (isLoading) { return

Loading events...
; } else if (error) { @@ -98,8 +98,8 @@ function EventDetails({ const actions: EventDetailAction[] = [ { - key: 'toggle-raw', - label: showRaw ? 'Formatted' : 'Text', + key: "toggle-raw", + label: showRaw ? "Formatted" : "Text", onClick: () => setShowRaw(!showRaw), }, ]; @@ -107,24 +107,24 @@ function EventDetails({ // Determine the title based on event type const title = (() => { switch (e.type) { - case 'header_up': - return 'Header Sent'; - case 'header_down': - return 'Header Received'; - case 'send_url': - return 'Request'; - case 'receive_url': - return 'Response'; - case 'redirect': - return 'Redirect'; - case 'setting': - return 'Apply Setting'; - case 'chunk_sent': - return 'Data Sent'; - case 'chunk_received': - return 'Data Received'; - case 'dns_resolved': - return e.overridden ? 'DNS Override' : 'DNS Resolution'; + case "header_up": + return "Header Sent"; + case "header_down": + return "Header Received"; + case "send_url": + return "Request"; + case "receive_url": + return "Response"; + case "redirect": + return "Redirect"; + case "setting": + return "Apply Setting"; + case "chunk_sent": + return "Data Sent"; + case "chunk_received": + return "Data Received"; + case "dns_resolved": + return e.overridden ? "DNS Override" : "DNS Resolution"; default: return label; } @@ -139,7 +139,7 @@ function EventDetails({ } // Headers - show name and value - if (e.type === 'header_up' || e.type === 'header_down') { + if (e.type === "header_up" || e.type === "header_down") { return ( {e.name} @@ -149,13 +149,13 @@ function EventDetails({ } // Request URL - show all URL parts separately - if (e.type === 'send_url') { - const auth = e.username || e.password ? `${e.username}:${e.password}@` : ''; + if (e.type === "send_url") { + const auth = e.username || e.password ? `${e.username}:${e.password}@` : ""; const isDefaultPort = - (e.scheme === 'http' && e.port === 80) || (e.scheme === 'https' && e.port === 443); - const portStr = isDefaultPort ? '' : `:${e.port}`; - const query = e.query ? `?${e.query}` : ''; - const fragment = e.fragment ? `#${e.fragment}` : ''; + (e.scheme === "http" && e.port === 80) || (e.scheme === "https" && e.port === 443); + const portStr = isDefaultPort ? "" : `:${e.port}`; + const query = e.query ? `?${e.query}` : ""; + const fragment = e.fragment ? `#${e.fragment}` : ""; const fullUrl = `${e.scheme}://${auth}${e.host}${portStr}${e.path}${query}${fragment}`; return ( @@ -174,7 +174,7 @@ function EventDetails({ } // Response status - show version and status separately - if (e.type === 'receive_url') { + if (e.type === "receive_url") { return ( {e.version} @@ -186,7 +186,7 @@ function EventDetails({ } // Redirect - show status, URL, and behavior - if (e.type === 'redirect') { + if (e.type === "redirect") { const droppedHeaders = e.dropped_headers ?? []; return ( @@ -195,18 +195,18 @@ function EventDetails({ {e.url} - {e.behavior === 'drop_body' ? 'Drop body, change to GET' : 'Preserve method and body'} + {e.behavior === "drop_body" ? "Drop body, change to GET" : "Preserve method and body"} - {e.dropped_body ? 'Yes' : 'No'} + {e.dropped_body ? "Yes" : "No"} - {droppedHeaders.length > 0 ? droppedHeaders.join(', ') : '--'} + {droppedHeaders.length > 0 ? droppedHeaders.join(", ") : "--"} ); } // Settings - show as key/value - if (e.type === 'setting') { + if (e.type === "setting") { return ( {e.name} @@ -216,16 +216,16 @@ function EventDetails({ } // Chunks - show formatted bytes - if (e.type === 'chunk_sent' || e.type === 'chunk_received') { + if (e.type === "chunk_sent" || e.type === "chunk_received") { return
{formatBytes(e.bytes)}
; } // DNS Resolution - show hostname, addresses, and timing - if (e.type === 'dns_resolved') { + if (e.type === "dns_resolved") { return ( {e.hostname} - {e.addresses.join(', ')} + {e.addresses.join(", ")} {e.overridden ? ( -- @@ -255,57 +255,57 @@ function EventDetails({ ); } -type EventTextParts = { prefix: '>' | '<' | '*'; text: string }; +type EventTextParts = { prefix: ">" | "<" | "*"; text: string }; /** Get the prefix and text for an event */ function getEventTextParts(event: HttpResponseEventData): EventTextParts { switch (event.type) { - case 'send_url': + case "send_url": return { - prefix: '>', - text: `${event.method} ${event.path}${event.query ? `?${event.query}` : ''}${event.fragment ? `#${event.fragment}` : ''}`, + prefix: ">", + text: `${event.method} ${event.path}${event.query ? `?${event.query}` : ""}${event.fragment ? `#${event.fragment}` : ""}`, }; - case 'receive_url': - return { prefix: '<', text: `${event.version} ${event.status}` }; - case 'header_up': - return { prefix: '>', text: `${event.name}: ${event.value}` }; - case 'header_down': - return { prefix: '<', text: `${event.name}: ${event.value}` }; - case 'redirect': { - const behavior = event.behavior === 'drop_body' ? 'drop body' : 'preserve'; + case "receive_url": + return { prefix: "<", text: `${event.version} ${event.status}` }; + case "header_up": + return { prefix: ">", text: `${event.name}: ${event.value}` }; + case "header_down": + return { prefix: "<", text: `${event.name}: ${event.value}` }; + case "redirect": { + const behavior = event.behavior === "drop_body" ? "drop body" : "preserve"; const droppedHeaders = event.dropped_headers ?? []; const dropped = [ - event.dropped_body ? 'body dropped' : null, - droppedHeaders.length > 0 ? `headers dropped: ${droppedHeaders.join(', ')}` : null, + event.dropped_body ? "body dropped" : null, + droppedHeaders.length > 0 ? `headers dropped: ${droppedHeaders.join(", ")}` : null, ] .filter(Boolean) - .join(', '); + .join(", "); return { - prefix: '*', - text: `Redirect ${event.status} -> ${event.url} (${behavior}${dropped ? `, ${dropped}` : ''})`, + prefix: "*", + text: `Redirect ${event.status} -> ${event.url} (${behavior}${dropped ? `, ${dropped}` : ""})`, }; } - case 'setting': - return { prefix: '*', text: `Setting ${event.name}=${event.value}` }; - case 'info': - return { prefix: '*', text: event.message }; - case 'chunk_sent': - return { prefix: '*', text: `[${formatBytes(event.bytes)} sent]` }; - case 'chunk_received': - return { prefix: '*', text: `[${formatBytes(event.bytes)} received]` }; - case 'dns_resolved': + case "setting": + return { prefix: "*", text: `Setting ${event.name}=${event.value}` }; + case "info": + return { prefix: "*", text: event.message }; + case "chunk_sent": + return { prefix: "*", text: `[${formatBytes(event.bytes)} sent]` }; + case "chunk_received": + return { prefix: "*", text: `[${formatBytes(event.bytes)} received]` }; + case "dns_resolved": if (event.overridden) { return { - prefix: '*', - text: `DNS override ${event.hostname} -> ${event.addresses.join(', ')}`, + prefix: "*", + text: `DNS override ${event.hostname} -> ${event.addresses.join(", ")}`, }; } return { - prefix: '*', - text: `DNS resolved ${event.hostname} to ${event.addresses.join(', ')} (${event.duration}ms)`, + prefix: "*", + text: `DNS resolved ${event.hostname} to ${event.addresses.join(", ")} (${event.duration}ms)`, }; default: - return { prefix: '*', text: '[unknown event]' }; + return { prefix: "*", text: "[unknown event]" }; } } @@ -316,103 +316,103 @@ function formatEventText(event: HttpResponseEventData, includePrefix: boolean): } type EventDisplay = { - icon: IconProps['icon']; - color: IconProps['color']; + icon: IconProps["icon"]; + color: IconProps["color"]; label: string; summary: ReactNode; }; function getEventDisplay(event: HttpResponseEventData): EventDisplay { switch (event.type) { - case 'setting': + case "setting": return { - icon: 'settings', - color: 'secondary', - label: 'Setting', + icon: "settings", + color: "secondary", + label: "Setting", summary: `${event.name} = ${event.value}`, }; - case 'info': + case "info": return { - icon: 'info', - color: 'secondary', - label: 'Info', + icon: "info", + color: "secondary", + label: "Info", summary: event.message, }; - case 'redirect': { + case "redirect": { const droppedHeaders = event.dropped_headers ?? []; const dropped = [ - event.dropped_body ? 'drop body' : null, + event.dropped_body ? "drop body" : null, droppedHeaders.length > 0 - ? `drop ${droppedHeaders.length} ${droppedHeaders.length === 1 ? 'header' : 'headers'}` + ? `drop ${droppedHeaders.length} ${droppedHeaders.length === 1 ? "header" : "headers"}` : null, ] .filter(Boolean) - .join(', '); + .join(", "); return { - icon: 'arrow_big_right_dash', - color: 'success', - label: 'Redirect', - summary: `Redirecting ${event.status} ${event.url}${dropped ? ` (${dropped})` : ''}`, + icon: "arrow_big_right_dash", + color: "success", + label: "Redirect", + summary: `Redirecting ${event.status} ${event.url}${dropped ? ` (${dropped})` : ""}`, }; } - case 'send_url': + case "send_url": return { - icon: 'arrow_big_up_dash', - color: 'primary', - label: 'Request', - summary: `${event.method} ${event.path}${event.query ? `?${event.query}` : ''}${event.fragment ? `#${event.fragment}` : ''}`, + icon: "arrow_big_up_dash", + color: "primary", + label: "Request", + summary: `${event.method} ${event.path}${event.query ? `?${event.query}` : ""}${event.fragment ? `#${event.fragment}` : ""}`, }; - case 'receive_url': + case "receive_url": return { - icon: 'arrow_big_down_dash', - color: 'info', - label: 'Response', + icon: "arrow_big_down_dash", + color: "info", + label: "Response", summary: `${event.version} ${event.status}`, }; - case 'header_up': + case "header_up": return { - icon: 'arrow_big_up_dash', - color: 'primary', - label: 'Header', + icon: "arrow_big_up_dash", + color: "primary", + label: "Header", summary: `${event.name}: ${event.value}`, }; - case 'header_down': + case "header_down": return { - icon: 'arrow_big_down_dash', - color: 'info', - label: 'Header', + icon: "arrow_big_down_dash", + color: "info", + label: "Header", summary: `${event.name}: ${event.value}`, }; - case 'chunk_sent': + case "chunk_sent": return { - icon: 'info', - color: 'secondary', - label: 'Chunk', + icon: "info", + color: "secondary", + label: "Chunk", summary: `${formatBytes(event.bytes)} chunk sent`, }; - case 'chunk_received': + case "chunk_received": return { - icon: 'info', - color: 'secondary', - label: 'Chunk', + icon: "info", + color: "secondary", + label: "Chunk", summary: `${formatBytes(event.bytes)} chunk received`, }; - case 'dns_resolved': + case "dns_resolved": return { - icon: 'globe', - color: event.overridden ? 'success' : 'secondary', - label: event.overridden ? 'DNS Override' : 'DNS', + icon: "globe", + color: event.overridden ? "success" : "secondary", + label: event.overridden ? "DNS Override" : "DNS", summary: event.overridden - ? `${event.hostname} → ${event.addresses.join(', ')} (overridden)` - : `${event.hostname} → ${event.addresses.join(', ')} (${event.duration}ms)`, + ? `${event.hostname} → ${event.addresses.join(", ")} (overridden)` + : `${event.hostname} → ${event.addresses.join(", ")} (${event.duration}ms)`, }; default: return { - icon: 'info', - color: 'secondary', - label: 'Unknown', - summary: 'Unknown event', + icon: "info", + color: "secondary", + label: "Unknown", + summary: "Unknown event", }; } } diff --git a/src-web/components/ImportCurlButton.tsx b/src-web/components/ImportCurlButton.tsx index 6335cd84..0f522d1d 100644 --- a/src-web/components/ImportCurlButton.tsx +++ b/src-web/components/ImportCurlButton.tsx @@ -1,24 +1,26 @@ -import { clear, readText } from '@tauri-apps/plugin-clipboard-manager'; -import * as m from 'motion/react-m'; -import { useEffect, useState } from 'react'; -import { useImportCurl } from '../hooks/useImportCurl'; -import { useWindowFocus } from '../hooks/useWindowFocus'; -import { Button } from './core/Button'; -import { Icon } from './core/Icon'; +import { clear, readText } from "@tauri-apps/plugin-clipboard-manager"; +import * as m from "motion/react-m"; +import { useEffect, useState } from "react"; +import { useImportCurl } from "../hooks/useImportCurl"; +import { useWindowFocus } from "../hooks/useWindowFocus"; +import { Button } from "./core/Button"; +import { Icon } from "./core/Icon"; export function ImportCurlButton() { const focused = useWindowFocus(); - const [clipboardText, setClipboardText] = useState(''); + const [clipboardText, setClipboardText] = useState(""); const importCurl = useImportCurl(); const [isLoading, setIsLoading] = useState(false); // oxlint-disable-next-line react-hooks/exhaustive-deps useEffect(() => { - readText().then(setClipboardText).catch(() => {}); + readText() + .then(setClipboardText) + .catch(() => {}); }, [focused]); - if (!clipboardText?.trim().startsWith('curl ')) { + if (!clipboardText?.trim().startsWith("curl ")) { return null; } @@ -41,9 +43,9 @@ export function ImportCurlButton() { try { await importCurl.mutateAsync({ command: clipboardText }); await clear(); // Clear the clipboard so the button goes away - setClipboardText(''); + setClipboardText(""); } catch (e) { - console.log('Failed to import curl', e); + console.log("Failed to import curl", e); } finally { setIsLoading(false); } diff --git a/src-web/components/ImportDataDialog.tsx b/src-web/components/ImportDataDialog.tsx index 35931843..8833e646 100644 --- a/src-web/components/ImportDataDialog.tsx +++ b/src-web/components/ImportDataDialog.tsx @@ -1,8 +1,8 @@ -import { useState } from 'react'; -import { useLocalStorage } from 'react-use'; -import { Button } from './core/Button'; -import { VStack } from './core/Stacks'; -import { SelectFile } from './SelectFile'; +import { useState } from "react"; +import { useLocalStorage } from "react-use"; +import { Button } from "./core/Button"; +import { VStack } from "./core/Stacks"; +import { SelectFile } from "./SelectFile"; interface Props { importData: (filePath: string) => Promise; @@ -10,7 +10,7 @@ interface Props { export function ImportDataDialog({ importData }: Props) { const [isLoading, setIsLoading] = useState(false); - const [filePath, setFilePath] = useLocalStorage('importFilePath', null); + const [filePath, setFilePath] = useLocalStorage("importFilePath", null); return ( @@ -45,7 +45,7 @@ export function ImportDataDialog({ importData }: Props) { } }} > - {isLoading ? 'Importing' : 'Import'} + {isLoading ? "Importing" : "Import"} )} diff --git a/src-web/components/IsDev.tsx b/src-web/components/IsDev.tsx index d850c5e3..6cc9ad6c 100644 --- a/src-web/components/IsDev.tsx +++ b/src-web/components/IsDev.tsx @@ -1,5 +1,5 @@ -import type { ReactNode } from 'react'; -import { appInfo } from '../lib/appInfo'; +import type { ReactNode } from "react"; +import { appInfo } from "../lib/appInfo"; interface Props { children: ReactNode; diff --git a/src-web/components/JsonBodyEditor.tsx b/src-web/components/JsonBodyEditor.tsx index e23b0f7c..65ad6fe0 100644 --- a/src-web/components/JsonBodyEditor.tsx +++ b/src-web/components/JsonBodyEditor.tsx @@ -1,23 +1,23 @@ -import { linter } from '@codemirror/lint'; -import type { HttpRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import { useCallback, useMemo } from 'react'; -import { fireAndForget } from '../lib/fireAndForget'; -import { useKeyValue } from '../hooks/useKeyValue'; -import { textLikelyContainsJsonComments } from '../lib/jsonComments'; -import { Banner } from './core/Banner'; -import type { DropdownItem } from './core/Dropdown'; -import { Dropdown } from './core/Dropdown'; -import type { EditorProps } from './core/Editor/Editor'; -import { jsonParseLinter } from './core/Editor/json-lint'; -import { Editor } from './core/Editor/LazyEditor'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { IconTooltip } from './core/IconTooltip'; +import { linter } from "@codemirror/lint"; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import { useCallback, useMemo } from "react"; +import { fireAndForget } from "../lib/fireAndForget"; +import { useKeyValue } from "../hooks/useKeyValue"; +import { textLikelyContainsJsonComments } from "../lib/jsonComments"; +import { Banner } from "./core/Banner"; +import type { DropdownItem } from "./core/Dropdown"; +import { Dropdown } from "./core/Dropdown"; +import type { EditorProps } from "./core/Editor/Editor"; +import { jsonParseLinter } from "./core/Editor/json-lint"; +import { Editor } from "./core/Editor/LazyEditor"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { IconTooltip } from "./core/IconTooltip"; interface Props { forceUpdateKey: string; - heightMode: EditorProps['heightMode']; + heightMode: EditorProps["heightMode"]; request: HttpRequest; } @@ -42,13 +42,13 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { ); const hasComments = useMemo( - () => textLikelyContainsJsonComments(request.body?.text ?? ''), + () => textLikelyContainsJsonComments(request.body?.text ?? ""), [request.body?.text], ); const { value: bannerDismissed, set: setBannerDismissed } = useKeyValue({ - namespace: 'no_sync', - key: ['json-fix-3', request.workspaceId], + namespace: "no_sync", + key: ["json-fix-3", request.workspaceId], fallback: false, }); @@ -70,8 +70,8 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { const showBanner = hasComments && autoFix && !bannerDismissed; - const stripMessage = 'Automatically strip comments and trailing commas before sending'; - const actions = useMemo( + const stripMessage = "Automatically strip comments and trailing commas before sending"; + const actions = useMemo( () => [ showBanner && ( @@ -87,12 +87,12 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { items={ [ { - label: 'Automatically Fix JSON', + label: "Automatically Fix JSON", keepOpenOnSelect: true, onSelect: handleToggleAutoFix, rightSlot: , leftSlot: ( - + ), }, ] satisfies DropdownItem[] @@ -112,7 +112,7 @@ export function JsonBodyEditor({ forceUpdateKey, heightMode, request }: Props) { autocompleteVariables placeholder="..." heightMode={heightMode} - defaultValue={`${request.body?.text ?? ''}`} + defaultValue={`${request.body?.text ?? ""}`} language="json" onChange={handleChange} stateKey={`json.${request.id}`} diff --git a/src-web/components/KeyboardShortcutsDialog.tsx b/src-web/components/KeyboardShortcutsDialog.tsx index e6a245ef..194437ef 100644 --- a/src-web/components/KeyboardShortcutsDialog.tsx +++ b/src-web/components/KeyboardShortcutsDialog.tsx @@ -1,5 +1,5 @@ -import { hotkeyActions } from '../hooks/useHotKey'; -import { HotkeyList } from './core/HotkeyList'; +import { hotkeyActions } from "../hooks/useHotKey"; +import { HotkeyList } from "./core/HotkeyList"; export function KeyboardShortcutsDialog() { return ( diff --git a/src-web/components/LicenseBadge.tsx b/src-web/components/LicenseBadge.tsx index 78cfe33e..10ff2f9b 100644 --- a/src-web/components/LicenseBadge.tsx +++ b/src-web/components/LicenseBadge.tsx @@ -1,62 +1,62 @@ -import { openUrl } from '@tauri-apps/plugin-opener'; -import type { LicenseCheckStatus } from '@yaakapp-internal/license'; -import { useLicense } from '@yaakapp-internal/license'; -import { settingsAtom } from '@yaakapp-internal/models'; -import { differenceInCalendarDays } from 'date-fns'; -import { formatDate } from 'date-fns/format'; -import { useAtomValue } from 'jotai'; -import type { ReactNode } from 'react'; -import { openSettings } from '../commands/openSettings'; -import { atomWithKVStorage } from '../lib/atoms/atomWithKVStorage'; -import { jotaiStore } from '../lib/jotai'; -import { CargoFeature } from './CargoFeature'; -import type { ButtonProps } from './core/Button'; -import { Dropdown, type DropdownItem } from './core/Dropdown'; -import { Icon } from './core/Icon'; -import { PillButton } from './core/PillButton'; +import { openUrl } from "@tauri-apps/plugin-opener"; +import type { LicenseCheckStatus } from "@yaakapp-internal/license"; +import { useLicense } from "@yaakapp-internal/license"; +import { settingsAtom } from "@yaakapp-internal/models"; +import { differenceInCalendarDays } from "date-fns"; +import { formatDate } from "date-fns/format"; +import { useAtomValue } from "jotai"; +import type { ReactNode } from "react"; +import { openSettings } from "../commands/openSettings"; +import { atomWithKVStorage } from "../lib/atoms/atomWithKVStorage"; +import { jotaiStore } from "../lib/jotai"; +import { CargoFeature } from "./CargoFeature"; +import type { ButtonProps } from "./core/Button"; +import { Dropdown, type DropdownItem } from "./core/Dropdown"; +import { Icon } from "./core/Icon"; +import { PillButton } from "./core/PillButton"; -const dismissedAtom = atomWithKVStorage('dismissed_license_expired', null); +const dismissedAtom = atomWithKVStorage("dismissed_license_expired", null); function getDetail( data: LicenseCheckStatus, dismissedExpired: string | null, -): { label: ReactNode; color: ButtonProps['color']; options?: DropdownItem[] } | null | undefined { +): { label: ReactNode; color: ButtonProps["color"]; options?: DropdownItem[] } | null | undefined { const dismissedAt = dismissedExpired ? new Date(dismissedExpired).getTime() : null; switch (data.status) { - case 'active': + case "active": return null; - case 'personal_use': - return { label: 'Personal Use', color: 'notice' }; - case 'trialing': - return { label: 'Commercial Trial', color: 'secondary' }; - case 'error': - return { label: 'Error', color: 'danger' }; - case 'inactive': - return { label: 'Personal Use', color: 'notice' }; - case 'past_due': - return { label: 'Past Due', color: 'danger' }; - case 'expired': + case "personal_use": + return { label: "Personal Use", color: "notice" }; + case "trialing": + return { label: "Commercial Trial", color: "secondary" }; + case "error": + return { label: "Error", color: "danger" }; + case "inactive": + return { label: "Personal Use", color: "notice" }; + case "past_due": + return { label: "Past Due", color: "danger" }; + case "expired": // Don't show the expired message if it's been less than 14 days since the last dismissal if (dismissedAt && differenceInCalendarDays(new Date(), dismissedAt) < 14) { return null; } return { - color: 'notice', - label: data.data.changes > 0 ? 'Updates Paused' : 'License Expired', + color: "notice", + label: data.data.changes > 0 ? "Updates Paused" : "License Expired", options: [ { label: `${data.data.changes} New Updates`, - color: 'success', + color: "success", leftSlot: , rightSlot: , hidden: data.data.changes === 0 || data.data.changesUrl == null, - onSelect: () => openUrl(data.data.changesUrl ?? ''), + onSelect: () => openUrl(data.data.changesUrl ?? ""), }, { - type: 'separator', - label: `License expired ${formatDate(data.data.periodEnd, 'MMM dd, yyyy')}`, + type: "separator", + label: `License expired ${formatDate(data.data.periodEnd, "MMM dd, yyyy")}`, }, { label:
Renew License
, @@ -66,12 +66,12 @@ function getDetail( onSelect: () => openUrl(data.data.billingUrl), }, { - label: 'Enter License Key', + label: "Enter License Key", leftSlot: , hidden: data.data.changesUrl == null, onSelect: openLicenseDialog, }, - { type: 'separator' }, + { type: "separator" }, { label: Remind me Later, leftSlot: , @@ -135,5 +135,5 @@ function LicenseBadgeCmp() { } function openLicenseDialog() { - openSettings.mutate('license'); + openSettings.mutate("license"); } diff --git a/src-web/components/LocalImage.tsx b/src-web/components/LocalImage.tsx index 340218f5..5c4f2719 100644 --- a/src-web/components/LocalImage.tsx +++ b/src-web/components/LocalImage.tsx @@ -1,7 +1,7 @@ -import { useQuery } from '@tanstack/react-query'; -import { convertFileSrc } from '@tauri-apps/api/core'; -import { resolveResource } from '@tauri-apps/api/path'; -import classNames from 'classnames'; +import { useQuery } from "@tanstack/react-query"; +import { convertFileSrc } from "@tauri-apps/api/core"; +import { resolveResource } from "@tauri-apps/api/path"; +import classNames from "classnames"; interface Props { src: string; @@ -10,7 +10,7 @@ interface Props { export function LocalImage({ src: srcPath, className }: Props) { const src = useQuery({ - queryKey: ['local-image', srcPath], + queryKey: ["local-image", srcPath], queryFn: async () => { const p = await resolveResource(srcPath); return convertFileSrc(p); @@ -23,8 +23,8 @@ export function LocalImage({ src: srcPath, className }: Props) { alt="Response preview" className={classNames( className, - 'transition-opacity', - src.data == null ? 'opacity-0' : 'opacity-100', + "transition-opacity", + src.data == null ? "opacity-0" : "opacity-100", )} /> ); diff --git a/src-web/components/Markdown.tsx b/src-web/components/Markdown.tsx index 687f9f87..1bc0834d 100644 --- a/src-web/components/Markdown.tsx +++ b/src-web/components/Markdown.tsx @@ -1,9 +1,9 @@ -import type { CSSProperties } from 'react'; -import ReactMarkdown, { type Components } from 'react-markdown'; -import { PrismLight as SyntaxHighlighter } from 'react-syntax-highlighter'; -import remarkGfm from 'remark-gfm'; -import { ErrorBoundary } from './ErrorBoundary'; -import { Prose } from './Prose'; +import type { CSSProperties } from "react"; +import ReactMarkdown, { type Components } from "react-markdown"; +import { PrismLight as SyntaxHighlighter } from "react-syntax-highlighter"; +import remarkGfm from "remark-gfm"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { Prose } from "./Prose"; interface Props { children: string | null; @@ -30,48 +30,48 @@ const prismTheme = { }, // Syntax tokens - comment: { color: 'var(--textSubtle)' }, - prolog: { color: 'var(--textSubtle)' }, - doctype: { color: 'var(--textSubtle)' }, - cdata: { color: 'var(--textSubtle)' }, + comment: { color: "var(--textSubtle)" }, + prolog: { color: "var(--textSubtle)" }, + doctype: { color: "var(--textSubtle)" }, + cdata: { color: "var(--textSubtle)" }, - punctuation: { color: 'var(--textSubtle)' }, + punctuation: { color: "var(--textSubtle)" }, - property: { color: 'var(--primary)' }, - 'attr-name': { color: 'var(--primary)' }, + property: { color: "var(--primary)" }, + "attr-name": { color: "var(--primary)" }, - string: { color: 'var(--notice)' }, - char: { color: 'var(--notice)' }, + string: { color: "var(--notice)" }, + char: { color: "var(--notice)" }, - number: { color: 'var(--info)' }, - constant: { color: 'var(--info)' }, - symbol: { color: 'var(--info)' }, + number: { color: "var(--info)" }, + constant: { color: "var(--info)" }, + symbol: { color: "var(--info)" }, - boolean: { color: 'var(--warning)' }, - 'attr-value': { color: 'var(--warning)' }, + boolean: { color: "var(--warning)" }, + "attr-value": { color: "var(--warning)" }, - variable: { color: 'var(--success)' }, + variable: { color: "var(--success)" }, - tag: { color: 'var(--info)' }, - operator: { color: 'var(--danger)' }, - keyword: { color: 'var(--danger)' }, - function: { color: 'var(--success)' }, - 'class-name': { color: 'var(--primary)' }, - builtin: { color: 'var(--danger)' }, - selector: { color: 'var(--danger)' }, - inserted: { color: 'var(--success)' }, - deleted: { color: 'var(--danger)' }, - regex: { color: 'var(--warning)' }, + tag: { color: "var(--info)" }, + operator: { color: "var(--danger)" }, + keyword: { color: "var(--danger)" }, + function: { color: "var(--success)" }, + "class-name": { color: "var(--primary)" }, + builtin: { color: "var(--danger)" }, + selector: { color: "var(--danger)" }, + inserted: { color: "var(--success)" }, + deleted: { color: "var(--danger)" }, + regex: { color: "var(--warning)" }, - important: { color: 'var(--danger)', fontWeight: 'bold' }, - italic: { fontStyle: 'italic' }, - bold: { fontWeight: 'bold' }, - entity: { cursor: 'help' }, + important: { color: "var(--danger)", fontWeight: "bold" }, + italic: { fontStyle: "italic" }, + bold: { fontWeight: "bold" }, + entity: { cursor: "help" }, }; const lineStyle: CSSProperties = { - paddingRight: '1.5em', - paddingLeft: '0', + paddingRight: "1.5em", + paddingLeft: "0", opacity: 0.5, }; @@ -91,7 +91,7 @@ const markdownComponents: Partial = { const { children, className, ref, ...extraProps } = props; extraProps.node = undefined; - const match = /language-(\w+)/.exec(className || ''); + const match = /language-(\w+)/.exec(className || ""); return match ? ( = { language={match[1]} style={prismTheme} > - {String(children as string).replace(/\n$/, '')} + {String(children as string).replace(/\n$/, "")} ) : ( diff --git a/src-web/components/MarkdownEditor.tsx b/src-web/components/MarkdownEditor.tsx index e51acd0a..5da836a2 100644 --- a/src-web/components/MarkdownEditor.tsx +++ b/src-web/components/MarkdownEditor.tsx @@ -1,13 +1,13 @@ -import classNames from 'classnames'; -import { useRef, useState } from 'react'; -import type { EditorProps } from './core/Editor/Editor'; -import { Editor } from './core/Editor/LazyEditor'; -import { SegmentedControl } from './core/SegmentedControl'; -import { Markdown } from './Markdown'; +import classNames from "classnames"; +import { useRef, useState } from "react"; +import type { EditorProps } from "./core/Editor/Editor"; +import { Editor } from "./core/Editor/LazyEditor"; +import { SegmentedControl } from "./core/SegmentedControl"; +import { Markdown } from "./Markdown"; -type ViewMode = 'edit' | 'preview'; +type ViewMode = "edit" | "preview"; -interface Props extends Pick { +interface Props extends Pick { placeholder: string; className?: string; editorClassName?: string; @@ -25,7 +25,7 @@ export function MarkdownEditor({ forceUpdateKey, ...editorProps }: Props) { - const [viewMode, setViewMode] = useState(defaultValue ? 'preview' : 'edit'); + const [viewMode, setViewMode] = useState(defaultValue ? "preview" : "edit"); const containerRef = useRef(null); @@ -33,7 +33,7 @@ export function MarkdownEditor({ ); - const contents = viewMode === 'preview' ? preview : editor; + const contents = viewMode === "preview" ? preview : editor; return (
@@ -73,8 +73,8 @@ export function MarkdownEditor({ value={viewMode} className="opacity-0 group-focus-within/markdown:opacity-100 group-hover/markdown:opacity-100" options={[ - { icon: 'eye', label: 'Preview mode', value: 'preview' }, - { icon: 'pencil', label: 'Edit mode', value: 'edit' }, + { icon: "eye", label: "Preview mode", value: "preview" }, + { icon: "pencil", label: "Edit mode", value: "edit" }, ]} />
diff --git a/src-web/components/MoveToWorkspaceDialog.tsx b/src-web/components/MoveToWorkspaceDialog.tsx index d6f405ec..e8118ac9 100644 --- a/src-web/components/MoveToWorkspaceDialog.tsx +++ b/src-web/components/MoveToWorkspaceDialog.tsx @@ -1,15 +1,15 @@ -import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; -import { patchModel, workspacesAtom } from '@yaakapp-internal/models'; -import { useAtomValue } from 'jotai'; -import { useState } from 'react'; -import { pluralizeCount } from '../lib/pluralize'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { router } from '../lib/router'; -import { showToast } from '../lib/toast'; -import { Button } from './core/Button'; -import { InlineCode } from './core/InlineCode'; -import { Select } from './core/Select'; -import { VStack } from './core/Stacks'; +import type { GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; +import { patchModel, workspacesAtom } from "@yaakapp-internal/models"; +import { useAtomValue } from "jotai"; +import { useState } from "react"; +import { pluralizeCount } from "../lib/pluralize"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { router } from "../lib/router"; +import { showToast } from "../lib/toast"; +import { Button } from "./core/Button"; +import { InlineCode } from "./core/InlineCode"; +import { Select } from "./core/Select"; +import { VStack } from "./core/Stacks"; interface Props { activeWorkspaceId: string; @@ -50,17 +50,17 @@ export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: P // Hide after a moment, to give time for requests to disappear setTimeout(onDone, 100); showToast({ - id: 'workspace-moved', + id: "workspace-moved", message: requests.length === 1 && requests[0] != null ? ( <> - {resolvedModelName(requests[0])} moved to{' '} - {targetWorkspace?.name ?? 'unknown'} + {resolvedModelName(requests[0])} moved to{" "} + {targetWorkspace?.name ?? "unknown"} ) : ( <> - {pluralizeCount('request', requests.length)} moved to{' '} - {targetWorkspace?.name ?? 'unknown'} + {pluralizeCount("request", requests.length)} moved to{" "} + {targetWorkspace?.name ?? "unknown"} ), action: ({ hide }) => ( @@ -70,7 +70,7 @@ export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: P className="mr-auto min-w-[5rem]" onClick={async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: selectedWorkspaceId }, }); hide(); @@ -82,7 +82,7 @@ export function MoveToWorkspaceDialog({ onDone, requests, activeWorkspaceId }: P }); }} > - {requests.length === 1 ? 'Move' : `Move ${pluralizeCount('Request', requests.length)}`} + {requests.length === 1 ? "Move" : `Move ${pluralizeCount("Request", requests.length)}`} ); diff --git a/src-web/components/Overlay.tsx b/src-web/components/Overlay.tsx index 89c31af0..ca349b75 100644 --- a/src-web/components/Overlay.tsx +++ b/src-web/components/Overlay.tsx @@ -1,9 +1,9 @@ -import classNames from 'classnames'; -import { FocusTrap } from 'focus-trap-react'; -import * as m from 'motion/react-m'; -import type { ReactNode } from 'react'; -import { useRef } from 'react'; -import { Portal } from './Portal'; +import classNames from "classnames"; +import { FocusTrap } from "focus-trap-react"; +import * as m from "motion/react-m"; +import type { ReactNode } from "react"; +import { useRef } from "react"; +import { Portal } from "./Portal"; interface Props { children: ReactNode; @@ -11,20 +11,20 @@ interface Props { open: boolean; onClose?: () => void; zIndex?: keyof typeof zIndexes; - variant?: 'default' | 'transparent'; + variant?: "default" | "transparent"; noBackdrop?: boolean; } const zIndexes: Record = { - 10: 'z-10', - 20: 'z-20', - 30: 'z-30', - 40: 'z-40', - 50: 'z-50', + 10: "z-10", + 20: "z-20", + 30: "z-30", + 40: "z-40", + 50: "z-50", }; export function Overlay({ - variant = 'default', + variant = "default", zIndex = 30, open, onClose, @@ -63,7 +63,7 @@ export function Overlay({ > @@ -71,14 +71,14 @@ export function Overlay({ aria-hidden onClick={onClose} className={classNames( - 'absolute inset-0', - variant === 'default' && 'bg-backdrop backdrop-blur-sm', + "absolute inset-0", + variant === "default" && "bg-backdrop backdrop-blur-sm", )} /> {/* Show the draggable region at the top */} {/* TODO: Figure out tauri drag region and also make clickable still */} - {variant === 'default' && ( + {variant === "default" && (
)} {children} diff --git a/src-web/components/Portal.tsx b/src-web/components/Portal.tsx index 25184dfd..2a302197 100644 --- a/src-web/components/Portal.tsx +++ b/src-web/components/Portal.tsx @@ -1,6 +1,6 @@ -import type { ReactNode } from 'react'; -import { createPortal } from 'react-dom'; -import { usePortal } from '../hooks/usePortal'; +import type { ReactNode } from "react"; +import { createPortal } from "react-dom"; +import { usePortal } from "../hooks/usePortal"; interface Props { children: ReactNode; diff --git a/src-web/components/Prose.tsx b/src-web/components/Prose.tsx index ab8be45b..883aef46 100644 --- a/src-web/components/Prose.tsx +++ b/src-web/components/Prose.tsx @@ -1,6 +1,6 @@ -import classNames from 'classnames'; -import type { ReactNode } from 'react'; -import './Prose.css'; +import classNames from "classnames"; +import type { ReactNode } from "react"; +import "./Prose.css"; interface Props { children: ReactNode; @@ -8,5 +8,5 @@ interface Props { } export function Prose({ className, ...props }: Props) { - return
; + return
; } diff --git a/src-web/components/RecentGrpcConnectionsDropdown.tsx b/src-web/components/RecentGrpcConnectionsDropdown.tsx index 606d7177..e2ba1bf8 100644 --- a/src-web/components/RecentGrpcConnectionsDropdown.tsx +++ b/src-web/components/RecentGrpcConnectionsDropdown.tsx @@ -1,12 +1,12 @@ -import type { GrpcConnection } from '@yaakapp-internal/models'; -import { deleteModel } from '@yaakapp-internal/models'; -import { formatDistanceToNowStrict } from 'date-fns'; -import { useDeleteGrpcConnections } from '../hooks/useDeleteGrpcConnections'; -import { pluralizeCount } from '../lib/pluralize'; -import { Dropdown } from './core/Dropdown'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { HStack } from './core/Stacks'; +import type { GrpcConnection } from "@yaakapp-internal/models"; +import { deleteModel } from "@yaakapp-internal/models"; +import { formatDistanceToNowStrict } from "date-fns"; +import { useDeleteGrpcConnections } from "../hooks/useDeleteGrpcConnections"; +import { pluralizeCount } from "../lib/pluralize"; +import { Dropdown } from "./core/Dropdown"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { HStack } from "./core/Stacks"; interface Props { connections: GrpcConnection[]; @@ -20,27 +20,27 @@ export function RecentGrpcConnectionsDropdown({ onPinnedConnectionId, }: Props) { const deleteAllConnections = useDeleteGrpcConnections(activeConnection?.requestId); - const latestConnectionId = connections[0]?.id ?? 'n/a'; + const latestConnectionId = connections[0]?.id ?? "n/a"; return ( deleteModel(activeConnection), disabled: connections.length === 0, }, { - label: `Clear ${pluralizeCount('Connection', connections.length)}`, + label: `Clear ${pluralizeCount("Connection", connections.length)}`, onSelect: deleteAllConnections.mutate, hidden: connections.length <= 1, disabled: connections.length === 0, }, - { type: 'separator', label: 'History' }, + { type: "separator", label: "History" }, ...connections.map((c) => ({ label: ( - {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{' '} + {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{" "} {c.elapsed}ms ), @@ -51,7 +51,7 @@ export function RecentGrpcConnectionsDropdown({ > , hidden: responses.length === 0 || !!activeResponse.error, - disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100, + disabled: activeResponse.state !== "closed" && activeResponse.status >= 100, }, { - label: 'Copy Body', + label: "Copy Body", onSelect: copyResponse.mutate, leftSlot: , hidden: responses.length === 0 || !!activeResponse.error, - disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100, + disabled: activeResponse.state !== "closed" && activeResponse.status >= 100, }, { - label: 'Delete', + label: "Delete", leftSlot: , onSelect: () => deleteModel(activeResponse), }, { - label: 'Unpin Response', + label: "Unpin Response", onSelect: () => onPinnedResponseId(activeResponse.id), leftSlot: , hidden: latestResponseId === activeResponse.id, disabled: responses.length === 0, }, - { type: 'separator', label: 'History' }, + { type: "separator", label: "History" }, { - label: `Delete ${responses.length} ${pluralize('Response', responses.length)}`, + label: `Delete ${responses.length} ${pluralize("Response", responses.length)}`, onSelect: deleteAllResponses.mutate, hidden: responses.length === 0, disabled: responses.length === 0, }, - { type: 'separator' }, + { type: "separator" }, ...responses.map((r: HttpResponse) => ({ label: ( - {' '} - {r.elapsed >= 0 ? `${r.elapsed}ms` : 'n/a'} + {" "} + {r.elapsed >= 0 ? `${r.elapsed}ms` : "n/a"} ), leftSlot: activeResponse?.id === r.id ? : , @@ -79,7 +79,7 @@ export const RecentHttpResponsesDropdown = function ResponsePane({ > { + useKeyboardEvent("keyup", "Control", () => { if (dropdownRef.current?.isOpen) { dropdownRef.current?.select?.(); } }); - useHotKey('switcher.prev', () => { + useHotKey("switcher.prev", () => { if (!dropdownRef.current?.isOpen) { // Select the second because the first is the current request dropdownRef.current?.open(1); @@ -41,7 +41,7 @@ export function RecentRequestsDropdown({ className }: Props) { } }); - useHotKey('switcher.next', () => { + useHotKey("switcher.next", () => { if (!dropdownRef.current?.isOpen) dropdownRef.current?.open(); dropdownRef.current?.prev?.(); }); @@ -61,7 +61,7 @@ export function RecentRequestsDropdown({ className }: Props) { leftSlot: , onSelect: async () => { await router.navigate({ - to: '/workspaces/$workspaceId', + to: "/workspaces/$workspaceId", params: { workspaceId: activeWorkspaceId }, search: (prev) => ({ ...prev, request_id: request.id }), }); @@ -73,8 +73,8 @@ export function RecentRequestsDropdown({ className }: Props) { if (recentRequestItems.length === 0) { return [ { - key: 'no-recent-requests', - label: 'No recent requests', + key: "no-recent-requests", + label: "No recent requests", disabled: true, }, ]; @@ -90,8 +90,8 @@ export function RecentRequestsDropdown({ className }: Props) { hotkeyAction="switcher.toggle" className={classNames( className, - 'truncate pointer-events-auto', - activeRequest == null && 'text-text-subtlest italic', + "truncate pointer-events-auto", + activeRequest == null && "text-text-subtlest italic", )} > {resolvedModelName(activeRequest)} diff --git a/src-web/components/RecentWebsocketConnectionsDropdown.tsx b/src-web/components/RecentWebsocketConnectionsDropdown.tsx index db79c125..d04375f6 100644 --- a/src-web/components/RecentWebsocketConnectionsDropdown.tsx +++ b/src-web/components/RecentWebsocketConnectionsDropdown.tsx @@ -1,12 +1,12 @@ -import type { WebsocketConnection } from '@yaakapp-internal/models'; -import { deleteModel, getModel } from '@yaakapp-internal/models'; -import { formatDistanceToNowStrict } from 'date-fns'; -import { deleteWebsocketConnections } from '../commands/deleteWebsocketConnections'; -import { pluralizeCount } from '../lib/pluralize'; -import { Dropdown } from './core/Dropdown'; -import { Icon } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import { HStack } from './core/Stacks'; +import type { WebsocketConnection } from "@yaakapp-internal/models"; +import { deleteModel, getModel } from "@yaakapp-internal/models"; +import { formatDistanceToNowStrict } from "date-fns"; +import { deleteWebsocketConnections } from "../commands/deleteWebsocketConnections"; +import { pluralizeCount } from "../lib/pluralize"; +import { Dropdown } from "./core/Dropdown"; +import { Icon } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import { HStack } from "./core/Stacks"; interface Props { connections: WebsocketConnection[]; @@ -19,20 +19,20 @@ export function RecentWebsocketConnectionsDropdown({ connections, onPinnedConnectionId, }: Props) { - const latestConnectionId = connections[0]?.id ?? 'n/a'; + const latestConnectionId = connections[0]?.id ?? "n/a"; return ( deleteModel(activeConnection), disabled: connections.length === 0, }, { - label: `Clear ${pluralizeCount('Connection', connections.length)}`, + label: `Clear ${pluralizeCount("Connection", connections.length)}`, onSelect: () => { - const request = getModel('websocket_request', activeConnection.requestId); + const request = getModel("websocket_request", activeConnection.requestId); if (request != null) { deleteWebsocketConnections.mutate(request); } @@ -40,11 +40,11 @@ export function RecentWebsocketConnectionsDropdown({ hidden: connections.length <= 1, disabled: connections.length === 0, }, - { type: 'separator', label: 'History' }, + { type: "separator", label: "History" }, ...connections.map((c) => ({ label: ( - {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{' '} + {formatDistanceToNowStrict(`${c.createdAt}Z`)} ago •{" "} {c.elapsed}ms ), @@ -55,7 +55,7 @@ export function RecentWebsocketConnectionsDropdown({ > { if (workspaces.length === 0 || recentWorkspaces == null) { - console.log('No workspaces found to redirect to. Skipping.', { + console.log("No workspaces found to redirect to. Skipping.", { workspaces, recentWorkspaces, }); return; } - fireAndForget((async () => { - const workspaceId = recentWorkspaces[0] ?? workspaces[0]?.id ?? 'n/a'; - const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? null; - const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? null; - const requestId = (await getRecentRequests(workspaceId))[0] ?? null; - const params = { workspaceId }; - const search = { - cookie_jar_id: cookieJarId, - environment_id: environmentId, - request_id: requestId, - }; + fireAndForget( + (async () => { + const workspaceId = recentWorkspaces[0] ?? workspaces[0]?.id ?? "n/a"; + const environmentId = (await getRecentEnvironments(workspaceId))[0] ?? null; + const cookieJarId = (await getRecentCookieJars(workspaceId))[0] ?? null; + const requestId = (await getRecentRequests(workspaceId))[0] ?? null; + const params = { workspaceId }; + const search = { + cookie_jar_id: cookieJarId, + environment_id: environmentId, + request_id: requestId, + }; - console.log('Redirecting to workspace', params, search); - await router.navigate({ to: '/workspaces/$workspaceId', params, search }); - })()); + console.log("Redirecting to workspace", params, search); + await router.navigate({ to: "/workspaces/$workspaceId", params, search }); + })(), + ); }, [recentWorkspaces, workspaces, workspaces.length]); return null; diff --git a/src-web/components/RequestBodyViewer.tsx b/src-web/components/RequestBodyViewer.tsx index 5c108d84..7deca539 100644 --- a/src-web/components/RequestBodyViewer.tsx +++ b/src-web/components/RequestBodyViewer.tsx @@ -1,20 +1,20 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { lazy, Suspense } from 'react'; -import { useHttpRequestBody } from '../hooks/useHttpRequestBody'; -import { getMimeTypeFromContentType, languageFromContentType } from '../lib/contentType'; -import { LoadingIcon } from './core/LoadingIcon'; -import { EmptyStateText } from './EmptyStateText'; -import { AudioViewer } from './responseViewers/AudioViewer'; -import { CsvViewer } from './responseViewers/CsvViewer'; -import { ImageViewer } from './responseViewers/ImageViewer'; -import { MultipartViewer } from './responseViewers/MultipartViewer'; -import { SvgViewer } from './responseViewers/SvgViewer'; -import { TextViewer } from './responseViewers/TextViewer'; -import { VideoViewer } from './responseViewers/VideoViewer'; -import { WebPageViewer } from './responseViewers/WebPageViewer'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { lazy, Suspense } from "react"; +import { useHttpRequestBody } from "../hooks/useHttpRequestBody"; +import { getMimeTypeFromContentType, languageFromContentType } from "../lib/contentType"; +import { LoadingIcon } from "./core/LoadingIcon"; +import { EmptyStateText } from "./EmptyStateText"; +import { AudioViewer } from "./responseViewers/AudioViewer"; +import { CsvViewer } from "./responseViewers/CsvViewer"; +import { ImageViewer } from "./responseViewers/ImageViewer"; +import { MultipartViewer } from "./responseViewers/MultipartViewer"; +import { SvgViewer } from "./responseViewers/SvgViewer"; +import { TextViewer } from "./responseViewers/TextViewer"; +import { VideoViewer } from "./responseViewers/VideoViewer"; +import { WebPageViewer } from "./responseViewers/WebPageViewer"; const PdfViewer = lazy(() => - import('./responseViewers/PdfViewer').then((m) => ({ default: m.PdfViewer })), + import("./responseViewers/PdfViewer").then((m) => ({ default: m.PdfViewer })), ); interface Props { @@ -48,7 +48,7 @@ function RequestBodyViewerInner({ response }: Props) { // Try to detect language from content-type header that was sent const contentTypeHeader = response.requestHeaders.find( - (h) => h.name.toLowerCase() === 'content-type', + (h) => h.name.toLowerCase() === "content-type", ); const contentType = contentTypeHeader?.value ?? null; const mimeType = contentType ? getMimeTypeFromContentType(contentType).essence : null; @@ -56,7 +56,7 @@ function RequestBodyViewerInner({ response }: Props) { // Route to appropriate viewer based on content type if (mimeType?.match(/^multipart/i)) { - const boundary = contentType?.split('boundary=')[1] ?? 'unknown'; + const boundary = contentType?.split("boundary=")[1] ?? "unknown"; // Create a copy because parseMultipart may detach the buffer const bodyCopy = new Uint8Array(body); return ( diff --git a/src-web/components/RequestMethodDropdown.tsx b/src-web/components/RequestMethodDropdown.tsx index 4bfaf534..0b7e4a7d 100644 --- a/src-web/components/RequestMethodDropdown.tsx +++ b/src-web/components/RequestMethodDropdown.tsx @@ -1,14 +1,14 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { memo, useCallback, useMemo } from 'react'; -import { showPrompt } from '../lib/prompt'; -import { Button } from './core/Button'; -import type { DropdownItem } from './core/Dropdown'; -import { HttpMethodTag, HttpMethodTagRaw } from './core/HttpMethodTag'; -import { Icon } from './core/Icon'; -import type { RadioDropdownItem } from './core/RadioDropdown'; -import { RadioDropdown } from './core/RadioDropdown'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { memo, useCallback, useMemo } from "react"; +import { showPrompt } from "../lib/prompt"; +import { Button } from "./core/Button"; +import type { DropdownItem } from "./core/Dropdown"; +import { HttpMethodTag, HttpMethodTagRaw } from "./core/HttpMethodTag"; +import { Icon } from "./core/Icon"; +import type { RadioDropdownItem } from "./core/RadioDropdown"; +import { RadioDropdown } from "./core/RadioDropdown"; type Props = { request: HttpRequest; @@ -16,14 +16,14 @@ type Props = { }; const radioItems: RadioDropdownItem[] = [ - 'GET', - 'PUT', - 'POST', - 'PATCH', - 'DELETE', - 'OPTIONS', - 'QUERY', - 'HEAD', + "GET", + "PUT", + "POST", + "PATCH", + "DELETE", + "OPTIONS", + "QUERY", + "HEAD", ].map((m) => ({ value: m, label: , @@ -43,17 +43,17 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({ const itemsAfter = useMemo( () => [ { - key: 'custom', - label: 'CUSTOM', + key: "custom", + label: "CUSTOM", leftSlot: , onSelect: async () => { const newMethod = await showPrompt({ - id: 'custom-method', - label: 'Http Method', - title: 'Custom Method', - confirmText: 'Save', - description: 'Enter a custom method name', - placeholder: 'CUSTOM', + id: "custom-method", + label: "Http Method", + title: "Custom Method", + confirmText: "Save", + description: "Enter a custom method name", + placeholder: "CUSTOM", }); if (newMethod == null) return; await handleChange(newMethod); @@ -70,7 +70,7 @@ export const RequestMethodDropdown = memo(function RequestMethodDropdown({ itemsAfter={itemsAfter} onChange={handleChange} > - diff --git a/src-web/components/ResizeHandle.tsx b/src-web/components/ResizeHandle.tsx index bdf55209..dc4a2de1 100644 --- a/src-web/components/ResizeHandle.tsx +++ b/src-web/components/ResizeHandle.tsx @@ -1,6 +1,6 @@ -import classNames from 'classnames'; -import type { CSSProperties, MouseEvent as ReactMouseEvent } from 'react'; -import { useCallback, useRef, useState } from 'react'; +import classNames from "classnames"; +import type { CSSProperties, MouseEvent as ReactMouseEvent } from "react"; +import { useCallback, useRef, useState } from "react"; const START_DISTANCE = 7; @@ -18,8 +18,8 @@ interface Props { onResizeEnd?: () => void; onResizeMove?: (e: ResizeHandleEvent) => void; onReset?: () => void; - side: 'left' | 'right' | 'top'; - justify: 'center' | 'end' | 'start'; + side: "left" | "right" | "top"; + justify: "center" | "end" | "start"; } export function ResizeHandle({ @@ -32,7 +32,7 @@ export function ResizeHandle({ onReset, side, }: Props) { - const vertical = side === 'top'; + const vertical = side === "top"; const [isResizing, setIsResizing] = useState(false); const moveState = useRef<{ move: (e: MouseEvent) => void; @@ -67,15 +67,15 @@ export function ResizeHandle({ function up() { setIsResizing(false); moveState.current = null; - document.documentElement.removeEventListener('mousemove', move); - document.documentElement.removeEventListener('mouseup', up); + document.documentElement.removeEventListener("mousemove", move); + document.documentElement.removeEventListener("mouseup", up); onResizeEnd?.(); } moveState.current = { calledStart: false, xStart: e.clientX, yStart: e.clientY, move, up }; - document.documentElement.addEventListener('mousemove', move); - document.documentElement.addEventListener('mouseup', up); + document.documentElement.addEventListener("mousemove", move); + document.documentElement.addEventListener("mouseup", up); }, [onResizeEnd, onResizeMove, onResizeStart, vertical], ); @@ -88,15 +88,15 @@ export function ResizeHandle({ onPointerDown={handlePointerDown} className={classNames( className, - 'group z-10 flex select-none transition-colors hover:bg-surface-active rounded-full', + "group z-10 flex select-none transition-colors hover:bg-surface-active rounded-full", // 'bg-info', // For debugging - vertical ? 'w-full h-1.5 cursor-row-resize' : 'h-full w-1.5 cursor-col-resize', - justify === 'center' && 'justify-center', - justify === 'end' && 'justify-end', - justify === 'start' && 'justify-start', - side === 'right' && 'right-0', - side === 'left' && 'left-0', - side === 'top' && 'top-0', + vertical ? "w-full h-1.5 cursor-row-resize" : "h-full w-1.5 cursor-col-resize", + justify === "center" && "justify-center", + justify === "end" && "justify-end", + justify === "start" && "justify-start", + side === "right" && "right-0", + side === "left" && "left-0", + side === "top" && "top-0", )} > {/* Show global overlay with cursor style to ensure cursor remains the same when moving quickly */} @@ -104,9 +104,9 @@ export function ResizeHandle({
)} diff --git a/src-web/components/ResponseCookies.tsx b/src-web/components/ResponseCookies.tsx index a7013961..65ecad04 100644 --- a/src-web/components/ResponseCookies.tsx +++ b/src-web/components/ResponseCookies.tsx @@ -1,11 +1,11 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useMemo } from 'react'; -import type { JSX } from 'react/jsx-runtime'; -import { useHttpResponseEvents } from '../hooks/useHttpResponseEvents'; -import { CountBadge } from './core/CountBadge'; -import { DetailsBanner } from './core/DetailsBanner'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useMemo } from "react"; +import type { JSX } from "react/jsx-runtime"; +import { useHttpResponseEvents } from "../hooks/useHttpResponseEvents"; +import { CountBadge } from "./core/CountBadge"; +import { DetailsBanner } from "./core/DetailsBanner"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; interface Props { response: HttpResponse; @@ -26,37 +26,37 @@ interface ParsedCookie { function parseCookieHeader(cookieHeader: string): Array<{ name: string; value: string }> { // Parse "Cookie: name=value; name2=value2" format - return cookieHeader.split(';').map((pair) => { - const [name = '', ...valueParts] = pair.split('='); + return cookieHeader.split(";").map((pair) => { + const [name = "", ...valueParts] = pair.split("="); return { name: name.trim(), - value: valueParts.join('=').trim(), + value: valueParts.join("=").trim(), }; }); } function parseSetCookieHeader(setCookieHeader: string): ParsedCookie { // Parse "Set-Cookie: name=value; Domain=...; Path=..." format - const parts = setCookieHeader.split(';').map((p) => p.trim()); - const [nameValue = '', ...attributes] = parts; - const [name = '', ...valueParts] = nameValue.split('='); + const parts = setCookieHeader.split(";").map((p) => p.trim()); + const [nameValue = "", ...attributes] = parts; + const [name = "", ...valueParts] = nameValue.split("="); const cookie: ParsedCookie = { name: name.trim(), - value: valueParts.join('=').trim(), + value: valueParts.join("=").trim(), }; for (const attr of attributes) { - const [key = '', val] = attr.split('=').map((s) => s.trim()); + const [key = "", val] = attr.split("=").map((s) => s.trim()); const lowerKey = key.toLowerCase(); - if (lowerKey === 'domain') cookie.domain = val; - else if (lowerKey === 'path') cookie.path = val; - else if (lowerKey === 'expires') cookie.expires = val; - else if (lowerKey === 'max-age') cookie.maxAge = val; - else if (lowerKey === 'secure') cookie.secure = true; - else if (lowerKey === 'httponly') cookie.httpOnly = true; - else if (lowerKey === 'samesite') cookie.sameSite = val; + if (lowerKey === "domain") cookie.domain = val; + else if (lowerKey === "path") cookie.path = val; + else if (lowerKey === "expires") cookie.expires = val; + else if (lowerKey === "max-age") cookie.maxAge = val; + else if (lowerKey === "secure") cookie.secure = true; + else if (lowerKey === "httponly") cookie.httpOnly = true; + else if (lowerKey === "samesite") cookie.sameSite = val; } // Detect if cookie is being deleted @@ -94,7 +94,7 @@ export function ResponseCookies({ response }: Props) { const e = event.event; // Cookie headers sent (header_up with name=cookie) - if (e.type === 'header_up' && e.name.toLowerCase() === 'cookie') { + if (e.type === "header_up" && e.name.toLowerCase() === "cookie") { const cookies = parseCookieHeader(e.value); for (const cookie of cookies) { sentMap.set(cookie.name, cookie); @@ -102,7 +102,7 @@ export function ResponseCookies({ response }: Props) { } // Set-Cookie headers received (header_down with name=set-cookie) - if (e.type === 'header_down' && e.name.toLowerCase() === 'set-cookie') { + if (e.type === "header_down" && e.name.toLowerCase() === "set-cookie") { const cookie = parseSetCookieHeader(e.value); receivedMap.set(cookie.name, cookie); } @@ -158,8 +158,8 @@ export function ResponseCookies({ response }: Props) {
{cookie.name} diff --git a/src-web/components/ResponseHeaders.tsx b/src-web/components/ResponseHeaders.tsx index c4222947..4f0b64c6 100644 --- a/src-web/components/ResponseHeaders.tsx +++ b/src-web/components/ResponseHeaders.tsx @@ -1,10 +1,10 @@ -import { openUrl } from '@tauri-apps/plugin-opener'; -import type { HttpResponse } from '@yaakapp-internal/models'; -import { useMemo } from 'react'; -import { CountBadge } from './core/CountBadge'; -import { DetailsBanner } from './core/DetailsBanner'; -import { IconButton } from './core/IconButton'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; +import { openUrl } from "@tauri-apps/plugin-opener"; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { useMemo } from "react"; +import { CountBadge } from "./core/CountBadge"; +import { DetailsBanner } from "./core/DetailsBanner"; +import { IconButton } from "./core/IconButton"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; interface Props { response: HttpResponse; diff --git a/src-web/components/ResponseInfo.tsx b/src-web/components/ResponseInfo.tsx index dcb68816..865ce6fa 100644 --- a/src-web/components/ResponseInfo.tsx +++ b/src-web/components/ResponseInfo.tsx @@ -1,7 +1,7 @@ -import { openUrl } from '@tauri-apps/plugin-opener'; -import type { HttpResponse } from '@yaakapp-internal/models'; -import { IconButton } from './core/IconButton'; -import { KeyValueRow, KeyValueRows } from './core/KeyValueRow'; +import { openUrl } from "@tauri-apps/plugin-opener"; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { IconButton } from "./core/IconButton"; +import { KeyValueRow, KeyValueRows } from "./core/KeyValueRow"; interface Props { response: HttpResponse; diff --git a/src-web/components/RouteError.tsx b/src-web/components/RouteError.tsx index 3299fcec..8d17cf9e 100644 --- a/src-web/components/RouteError.tsx +++ b/src-web/components/RouteError.tsx @@ -1,16 +1,16 @@ -import { Button } from './core/Button'; -import { DetailsBanner } from './core/DetailsBanner'; -import { FormattedError } from './core/FormattedError'; -import { Heading } from './core/Heading'; -import { VStack } from './core/Stacks'; +import { Button } from "./core/Button"; +import { DetailsBanner } from "./core/DetailsBanner"; +import { FormattedError } from "./core/FormattedError"; +import { Heading } from "./core/Heading"; +import { VStack } from "./core/Stacks"; export default function RouteError({ error }: { error: unknown }) { - console.log('Error', error); + console.log("Error", error); const stringified = JSON.stringify(error); // oxlint-disable-next-line no-explicit-any const message = (error as any).message ?? stringified; const stack = - typeof error === 'object' && error != null && 'stack' in error ? String(error.stack) : null; + typeof error === "object" && error != null && "stack" in error ? String(error.stack) : null; return (
@@ -31,7 +31,7 @@ export default function RouteError({ error }: { error: unknown }) { @@ -273,8 +273,8 @@ TemplateFunctionDialog.show = ( const initialTokens = parseTemplate(tagValue); showDialog({ id: `template-function-${Math.random()}`, // Allow multiple at once - size: 'md', - className: 'h-[60rem]', + size: "md", + className: "h-[60rem]", noPadding: true, title: {fn.name}(…), description: fn.description, diff --git a/src-web/components/Toasts.tsx b/src-web/components/Toasts.tsx index 65cef54b..8050abbc 100644 --- a/src-web/components/Toasts.tsx +++ b/src-web/components/Toasts.tsx @@ -1,18 +1,18 @@ -import { useAtomValue } from 'jotai'; -import { AnimatePresence } from 'motion/react'; -import type { ReactNode } from 'react'; -import { hideToast, toastsAtom } from '../lib/toast'; -import { Toast, type ToastProps } from './core/Toast'; -import { ErrorBoundary } from './ErrorBoundary'; -import { Portal } from './Portal'; +import { useAtomValue } from "jotai"; +import { AnimatePresence } from "motion/react"; +import type { ReactNode } from "react"; +import { hideToast, toastsAtom } from "../lib/toast"; +import { Toast, type ToastProps } from "./core/Toast"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { Portal } from "./Portal"; export type ToastInstance = { id: string; uniqueKey: string; message: ReactNode; timeout: 3000 | 5000 | 8000 | (number & {}) | null; - onClose?: ToastProps['onClose']; -} & Omit; + onClose?: ToastProps["onClose"]; +} & Omit; export const Toasts = () => { const toasts = useAtomValue(toastsAtom); diff --git a/src-web/components/UrlBar.tsx b/src-web/components/UrlBar.tsx index c9fafbe2..9687b37f 100644 --- a/src-web/components/UrlBar.tsx +++ b/src-web/components/UrlBar.tsx @@ -1,29 +1,29 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import type { FormEvent, ReactNode } from 'react'; -import { memo, useCallback, useRef, useState } from 'react'; -import { useHotKey } from '../hooks/useHotKey'; -import type { IconProps } from './core/Icon'; -import { IconButton } from './core/IconButton'; -import type { InputHandle, InputProps } from './core/Input'; -import { Input } from './core/Input'; -import { HStack } from './core/Stacks'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import type { FormEvent, ReactNode } from "react"; +import { memo, useCallback, useRef, useState } from "react"; +import { useHotKey } from "../hooks/useHotKey"; +import type { IconProps } from "./core/Icon"; +import { IconButton } from "./core/IconButton"; +import type { InputHandle, InputProps } from "./core/Input"; +import { Input } from "./core/Input"; +import { HStack } from "./core/Stacks"; -type Props = Pick & { +type Props = Pick & { className?: string; placeholder: string; onSend: () => void; onUrlChange: (url: string) => void; onPaste?: (v: string) => void; - onPasteOverwrite?: InputProps['onPasteOverwrite']; + onPasteOverwrite?: InputProps["onPasteOverwrite"]; onCancel: () => void; - submitIcon?: IconProps['icon'] | null; + submitIcon?: IconProps["icon"] | null; isLoading: boolean; forceUpdateKey: string; rightSlot?: ReactNode; leftSlot?: ReactNode; - autocomplete?: InputProps['autocomplete']; - stateKey: InputProps['stateKey']; + autocomplete?: InputProps["autocomplete"]; + stateKey: InputProps["stateKey"]; }; export const UrlBar = memo(function UrlBar({ @@ -36,7 +36,7 @@ export const UrlBar = memo(function UrlBar({ onCancel, onPaste, onPasteOverwrite, - submitIcon = 'send_horizontal', + submitIcon = "send_horizontal", autocomplete, leftSlot, rightSlot, @@ -50,7 +50,7 @@ export const UrlBar = memo(function UrlBar({ inputRef.current = h; }, []); - useHotKey('url_bar.focus', () => { + useHotKey("url_bar.focus", () => { inputRef.current?.selectAll(); }); @@ -61,7 +61,7 @@ export const UrlBar = memo(function UrlBar({ }; return ( - + { // Prevent the button from taking focus diff --git a/src-web/components/UrlParameterEditor.tsx b/src-web/components/UrlParameterEditor.tsx index b436526f..bce2d579 100644 --- a/src-web/components/UrlParameterEditor.tsx +++ b/src-web/components/UrlParameterEditor.tsx @@ -1,15 +1,15 @@ -import type { HttpRequest } from '@yaakapp-internal/models'; -import { useCallback, useRef } from 'react'; -import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor'; -import type { PairEditorHandle, PairEditorProps } from './core/PairEditor'; -import { PairOrBulkEditor } from './core/PairOrBulkEditor'; -import { VStack } from './core/Stacks'; +import type { HttpRequest } from "@yaakapp-internal/models"; +import { useCallback, useRef } from "react"; +import { useRequestEditor, useRequestEditorEvent } from "../hooks/useRequestEditor"; +import type { PairEditorHandle, PairEditorProps } from "./core/PairEditor"; +import { PairOrBulkEditor } from "./core/PairOrBulkEditor"; +import { VStack } from "./core/Stacks"; type Props = { forceUpdateKey: string; - pairs: HttpRequest['headers']; - stateKey: PairEditorProps['stateKey']; - onChange: (headers: HttpRequest['urlParameters']) => void; + pairs: HttpRequest["headers"]; + stateKey: PairEditorProps["stateKey"]; + onChange: (headers: HttpRequest["urlParameters"]) => void; }; export function UrlParametersEditor({ pairs, forceUpdateKey, onChange, stateKey }: Props) { @@ -21,7 +21,7 @@ export function UrlParametersEditor({ pairs, forceUpdateKey, onChange, stateKey const [{ urlParametersKey }] = useRequestEditor(); useRequestEditorEvent( - 'request_params.focus_value', + "request_params.focus_value", (name) => { const pair = pairs.find((p) => p.name === name); if (pair?.id != null) { diff --git a/src-web/components/WebsocketRequestLayout.tsx b/src-web/components/WebsocketRequestLayout.tsx index c26bb6c7..2b5fc8a9 100644 --- a/src-web/components/WebsocketRequestLayout.tsx +++ b/src-web/components/WebsocketRequestLayout.tsx @@ -1,12 +1,12 @@ -import type { WebsocketRequest } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import type { CSSProperties } from 'react'; +import type { WebsocketRequest } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import type { CSSProperties } from "react"; -import { workspaceLayoutAtom } from '../lib/atoms'; -import { SplitLayout } from './core/SplitLayout'; -import { WebsocketRequestPane } from './WebsocketRequestPane'; -import { WebsocketResponsePane } from './WebsocketResponsePane'; +import { workspaceLayoutAtom } from "../lib/atoms"; +import { SplitLayout } from "./core/SplitLayout"; +import { WebsocketRequestPane } from "./WebsocketRequestPane"; +import { WebsocketResponsePane } from "./WebsocketResponsePane"; interface Props { activeRequest: WebsocketRequest; @@ -25,17 +25,17 @@ export function WebsocketRequestLayout({ activeRequest, style }: Props) { )} secondSlot={({ style }) => (
diff --git a/src-web/components/WebsocketRequestPane.tsx b/src-web/components/WebsocketRequestPane.tsx index 3627c20c..9dd86e68 100644 --- a/src-web/components/WebsocketRequestPane.tsx +++ b/src-web/components/WebsocketRequestPane.tsx @@ -1,41 +1,41 @@ -import type { WebsocketRequest } from '@yaakapp-internal/models'; -import { patchModel } from '@yaakapp-internal/models'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import { closeWebsocket, connectWebsocket, sendWebsocket } from '@yaakapp-internal/ws'; -import classNames from 'classnames'; -import { atom, useAtomValue } from 'jotai'; -import type { CSSProperties } from 'react'; -import { useCallback, useMemo, useRef } from 'react'; -import { getActiveCookieJar } from '../hooks/useActiveCookieJar'; -import { getActiveEnvironment } from '../hooks/useActiveEnvironment'; -import { activeRequestIdAtom } from '../hooks/useActiveRequestId'; -import { allRequestsAtom } from '../hooks/useAllRequests'; -import { useAuthTab } from '../hooks/useAuthTab'; -import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse'; -import { useHeadersTab } from '../hooks/useHeadersTab'; -import { useInheritedHeaders } from '../hooks/useInheritedHeaders'; -import { usePinnedHttpResponse } from '../hooks/usePinnedHttpResponse'; -import { activeWebsocketConnectionAtom } from '../hooks/usePinnedWebsocketConnection'; -import { useRequestEditor, useRequestEditorEvent } from '../hooks/useRequestEditor'; -import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; -import { deepEqualAtom } from '../lib/atoms'; -import { languageFromContentType } from '../lib/contentType'; -import { generateId } from '../lib/generateId'; -import { prepareImportQuerystring } from '../lib/prepareImportQuerystring'; -import { resolvedModelName } from '../lib/resolvedModelName'; -import { CountBadge } from './core/CountBadge'; -import type { GenericCompletionConfig } from './core/Editor/genericCompletion'; -import { Editor } from './core/Editor/LazyEditor'; -import { IconButton } from './core/IconButton'; -import type { Pair } from './core/PairEditor'; -import { PlainInput } from './core/PlainInput'; -import type { TabItem, TabsRef } from './core/Tabs/Tabs'; -import { setActiveTab, TabContent, Tabs } from './core/Tabs/Tabs'; -import { HeadersEditor } from './HeadersEditor'; -import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; -import { MarkdownEditor } from './MarkdownEditor'; -import { UrlBar } from './UrlBar'; -import { UrlParametersEditor } from './UrlParameterEditor'; +import type { WebsocketRequest } from "@yaakapp-internal/models"; +import { patchModel } from "@yaakapp-internal/models"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import { closeWebsocket, connectWebsocket, sendWebsocket } from "@yaakapp-internal/ws"; +import classNames from "classnames"; +import { atom, useAtomValue } from "jotai"; +import type { CSSProperties } from "react"; +import { useCallback, useMemo, useRef } from "react"; +import { getActiveCookieJar } from "../hooks/useActiveCookieJar"; +import { getActiveEnvironment } from "../hooks/useActiveEnvironment"; +import { activeRequestIdAtom } from "../hooks/useActiveRequestId"; +import { allRequestsAtom } from "../hooks/useAllRequests"; +import { useAuthTab } from "../hooks/useAuthTab"; +import { useCancelHttpResponse } from "../hooks/useCancelHttpResponse"; +import { useHeadersTab } from "../hooks/useHeadersTab"; +import { useInheritedHeaders } from "../hooks/useInheritedHeaders"; +import { usePinnedHttpResponse } from "../hooks/usePinnedHttpResponse"; +import { activeWebsocketConnectionAtom } from "../hooks/usePinnedWebsocketConnection"; +import { useRequestEditor, useRequestEditorEvent } from "../hooks/useRequestEditor"; +import { useRequestUpdateKey } from "../hooks/useRequestUpdateKey"; +import { deepEqualAtom } from "../lib/atoms"; +import { languageFromContentType } from "../lib/contentType"; +import { generateId } from "../lib/generateId"; +import { prepareImportQuerystring } from "../lib/prepareImportQuerystring"; +import { resolvedModelName } from "../lib/resolvedModelName"; +import { CountBadge } from "./core/CountBadge"; +import type { GenericCompletionConfig } from "./core/Editor/genericCompletion"; +import { Editor } from "./core/Editor/LazyEditor"; +import { IconButton } from "./core/IconButton"; +import type { Pair } from "./core/PairEditor"; +import { PlainInput } from "./core/PlainInput"; +import type { TabItem, TabsRef } from "./core/Tabs/Tabs"; +import { setActiveTab, TabContent, Tabs } from "./core/Tabs/Tabs"; +import { HeadersEditor } from "./HeadersEditor"; +import { HttpAuthenticationEditor } from "./HttpAuthenticationEditor"; +import { MarkdownEditor } from "./MarkdownEditor"; +import { UrlBar } from "./UrlBar"; +import { UrlParametersEditor } from "./UrlParameterEditor"; interface Props { style: CSSProperties; @@ -44,19 +44,19 @@ interface Props { activeRequest: WebsocketRequest; } -const TAB_MESSAGE = 'message'; -const TAB_PARAMS = 'params'; -const TAB_HEADERS = 'headers'; -const TAB_AUTH = 'auth'; -const TAB_DESCRIPTION = 'description'; -const TABS_STORAGE_KEY = 'websocket_request_tabs'; +const TAB_MESSAGE = "message"; +const TAB_PARAMS = "params"; +const TAB_HEADERS = "headers"; +const TAB_AUTH = "auth"; +const TAB_DESCRIPTION = "description"; +const TABS_STORAGE_KEY = "websocket_request_tabs"; const nonActiveRequestUrlsAtom = atom((get) => { const activeRequestId = get(activeRequestIdAtom); const requests = get(allRequestsAtom); return requests .filter((r) => r.id !== activeRequestId) - .map((r): GenericCompletionOption => ({ type: 'constant', label: r.url })); + .map((r): GenericCompletionOption => ({ type: "constant", label: r.url })); }); const memoNotActiveRequestUrlsAtom = deepEqualAtom(nonActiveRequestUrlsAtom); @@ -71,13 +71,17 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque const inheritedHeaders = useInheritedHeaders(activeRequest); // Listen for event to focus the params tab (e.g., when clicking a :param in the URL) - useRequestEditorEvent('request_pane.focus_tab', () => { - tabsRef.current?.setActiveTab(TAB_PARAMS); - }, []); + useRequestEditorEvent( + "request_pane.focus_tab", + () => { + tabsRef.current?.setActiveTab(TAB_PARAMS); + }, + [], + ); const { urlParameterPairs, urlParametersKey } = useMemo(() => { const placeholderNames = Array.from(activeRequest.url.matchAll(/\/(:[^/]+)/g)).map( - (m) => m[1] ?? '', + (m) => m[1] ?? "", ); const nonEmptyParameters = activeRequest.urlParameters.filter((p) => p.name || p.value); const items: Pair[] = [...nonEmptyParameters]; @@ -86,28 +90,28 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque if (item) { item.readOnlyName = true; } else { - items.push({ name, value: '', enabled: true, readOnlyName: true, id: generateId() }); + items.push({ name, value: "", enabled: true, readOnlyName: true, id: generateId() }); } } - return { urlParameterPairs: items, urlParametersKey: placeholderNames.join(',') }; + return { urlParameterPairs: items, urlParametersKey: placeholderNames.join(",") }; }, [activeRequest.url, activeRequest.urlParameters]); const tabs = useMemo(() => { return [ { value: TAB_MESSAGE, - label: 'Message', + label: "Message", } as TabItem, { value: TAB_PARAMS, rightSlot: , - label: 'Params', + label: "Params", }, ...headersTab, ...authTab, { value: TAB_DESCRIPTION, - label: 'Info', + label: "Info", }, ]; }, [authTab, headersTab, urlParameterPairs.length]); @@ -125,8 +129,8 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque autocompleteUrls.length > 0 ? autocompleteUrls : [ - { label: 'http://', type: 'constant' }, - { label: 'https://', type: 'constant' }, + { label: "http://", type: "constant" }, + { label: "https://", type: "constant" }, ], }), [autocompleteUrls], @@ -184,12 +188,12 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque const messageLanguage = languageFromContentType(null, activeRequest.message); - const isLoading = connection !== null && connection.state !== 'closed'; + const isLoading = connection !== null && connection.state !== "closed"; return (
{activeRequest && ( <> @@ -198,7 +202,7 @@ export function WebsocketRequestPane({ style, fullHeight, className, activeReque stateKey={`url.${activeRequest.id}`} key={forceUpdateKey + urlKey} url={activeRequest.url} - submitIcon={isLoading ? 'send_horizontal' : 'arrow_up_down'} + submitIcon={isLoading ? "send_horizontal" : "arrow_up_down"} rightSlot={ isLoading && (
patchModel(activeRequest, { message })} diff --git a/src-web/components/WebsocketResponsePane.tsx b/src-web/components/WebsocketResponsePane.tsx index 043ab841..b65ebe46 100644 --- a/src-web/components/WebsocketResponsePane.tsx +++ b/src-web/components/WebsocketResponsePane.tsx @@ -1,28 +1,28 @@ -import type { WebsocketEvent, WebsocketRequest } from '@yaakapp-internal/models'; -import { hexy } from 'hexy'; -import { useAtomValue } from 'jotai'; -import { useMemo, useState } from 'react'; -import { useFormatText } from '../hooks/useFormatText'; +import type { WebsocketEvent, WebsocketRequest } from "@yaakapp-internal/models"; +import { hexy } from "hexy"; +import { useAtomValue } from "jotai"; +import { useMemo, useState } from "react"; +import { useFormatText } from "../hooks/useFormatText"; import { activeWebsocketConnectionAtom, activeWebsocketConnectionsAtom, setPinnedWebsocketConnectionId, useWebsocketEvents, -} from '../hooks/usePinnedWebsocketConnection'; -import { useStateWithDeps } from '../hooks/useStateWithDeps'; -import { languageFromContentType } from '../lib/contentType'; -import { Button } from './core/Button'; -import { Editor } from './core/Editor/LazyEditor'; -import { type EventDetailAction, EventDetailHeader, EventViewer } from './core/EventViewer'; -import { EventViewerRow } from './core/EventViewerRow'; -import { HotkeyList } from './core/HotkeyList'; -import { Icon } from './core/Icon'; -import { LoadingIcon } from './core/LoadingIcon'; -import { HStack, VStack } from './core/Stacks'; -import { WebsocketStatusTag } from './core/WebsocketStatusTag'; -import { EmptyStateText } from './EmptyStateText'; -import { ErrorBoundary } from './ErrorBoundary'; -import { RecentWebsocketConnectionsDropdown } from './RecentWebsocketConnectionsDropdown'; +} from "../hooks/usePinnedWebsocketConnection"; +import { useStateWithDeps } from "../hooks/useStateWithDeps"; +import { languageFromContentType } from "../lib/contentType"; +import { Button } from "./core/Button"; +import { Editor } from "./core/Editor/LazyEditor"; +import { type EventDetailAction, EventDetailHeader, EventViewer } from "./core/EventViewer"; +import { EventViewerRow } from "./core/EventViewerRow"; +import { HotkeyList } from "./core/HotkeyList"; +import { Icon } from "./core/Icon"; +import { LoadingIcon } from "./core/LoadingIcon"; +import { HStack, VStack } from "./core/Stacks"; +import { WebsocketStatusTag } from "./core/WebsocketStatusTag"; +import { EmptyStateText } from "./EmptyStateText"; +import { ErrorBoundary } from "./ErrorBoundary"; +import { RecentWebsocketConnectionsDropdown } from "./RecentWebsocketConnectionsDropdown"; interface Props { activeRequest: WebsocketRequest; @@ -39,14 +39,14 @@ export function WebsocketResponsePane({ activeRequest }: Props) { if (activeConnection == null) { return ( - + ); } const header = ( - {activeConnection.state !== 'closed' && ( + {activeConnection.state !== "closed" && ( )} @@ -78,7 +78,7 @@ export function WebsocketResponsePane({ activeRequest }: Props) { renderDetail={({ event, index, onClose }) => ( setHexDumps({ ...hexDumps, [index]: v })} showLarge={showLarge} showingLarge={showingLarge} @@ -103,25 +103,25 @@ function WebsocketEventRow({ }) { const { message: messageBytes, isServer, messageType } = event; const message = messageBytes - ? new TextDecoder('utf-8').decode(Uint8Array.from(messageBytes)) - : ''; + ? new TextDecoder("utf-8").decode(Uint8Array.from(messageBytes)) + : ""; const iconColor = - messageType === 'close' || messageType === 'open' ? 'secondary' : isServer ? 'info' : 'primary'; + messageType === "close" || messageType === "open" ? "secondary" : isServer ? "info" : "primary"; const icon = - messageType === 'close' || messageType === 'open' - ? 'info' + messageType === "close" || messageType === "open" + ? "info" : isServer - ? 'arrow_big_down_dash' - : 'arrow_big_up_dash'; + ? "arrow_big_down_dash" + : "arrow_big_up_dash"; const content = - messageType === 'close' ? ( - 'Disconnected from server' - ) : messageType === 'open' ? ( - 'Connected to server' - ) : message === '' ? ( + messageType === "close" ? ( + "Disconnected from server" + ) : messageType === "open" ? ( + "Connected to server" + ) : message === "" ? ( No content ) : ( {message.slice(0, 1000)} @@ -159,27 +159,27 @@ function WebsocketEventDetail({ }) { const message = useMemo(() => { if (hexDump) { - return event.message ? hexy(event.message) : ''; + return event.message ? hexy(event.message) : ""; } - return event.message ? new TextDecoder('utf-8').decode(Uint8Array.from(event.message)) : ''; + return event.message ? new TextDecoder("utf-8").decode(Uint8Array.from(event.message)) : ""; }, [event.message, hexDump]); const language = languageFromContentType(null, message); const formattedMessage = useFormatText({ language, text: message, pretty: true }); const title = - event.messageType === 'close' - ? 'Connection Closed' - : event.messageType === 'open' - ? 'Connection Open' - : `Message ${event.isServer ? 'Received' : 'Sent'}`; + event.messageType === "close" + ? "Connection Closed" + : event.messageType === "open" + ? "Connection Open" + : `Message ${event.isServer ? "Received" : "Sent"}`; const actions: EventDetailAction[] = - message !== '' + message !== "" ? [ { - key: 'toggle-hexdump', - label: hexDump ? 'Show Message' : 'Show Hexdump', + key: "toggle-hexdump", + label: hexDump ? "Show Message" : "Show Hexdump", onClick: () => setHexDump(!hexDump), }, ] @@ -220,7 +220,7 @@ function WebsocketEventDetail({ ) : ( (false); const settings = useAtomValue(settingsAtom); // Never show controls on macOS or if hideWindowControls is true - if (type() === 'macos' || settings.hideWindowControls || settings.useNativeTitlebar) { + if (type() === "macos" || settings.hideWindowControls || settings.useNativeTitlebar) { return null; } return ( {floating ? ( @@ -141,9 +141,9 @@ export function Workspace() { initial={{ opacity: 0, x: -20 }} animate={{ opacity: 1, x: 0 }} className={classNames( - 'x-theme-sidebar', - 'absolute top-0 left-0 bottom-0 bg-surface border-r border-border-subtle w-[20rem]', - 'grid grid-rows-[auto_1fr]', + "x-theme-sidebar", + "absolute top-0 left-0 bottom-0 bg-surface border-r border-border-subtle w-[20rem]", + "grid grid-rows-[auto_1fr]", )} > @@ -156,7 +156,7 @@ export function Workspace() { ) : ( <> -
+
@@ -220,13 +220,13 @@ function WorkspaceBody() { ); } - if (activeRequest?.model === 'grpc_request') { + if (activeRequest?.model === "grpc_request") { return ; } - if (activeRequest?.model === 'websocket_request') { + if (activeRequest?.model === "websocket_request") { return ; } - if (activeRequest?.model === 'http_request') { + if (activeRequest?.model === "http_request") { return ; } if (activeFolder != null) { @@ -235,7 +235,7 @@ function WorkspaceBody() { return ( ); diff --git a/src-web/components/WorkspaceEncryptionSetting.tsx b/src-web/components/WorkspaceEncryptionSetting.tsx index b9c6ebf5..4c510e53 100644 --- a/src-web/components/WorkspaceEncryptionSetting.tsx +++ b/src-web/components/WorkspaceEncryptionSetting.tsx @@ -3,28 +3,28 @@ import { enableEncryption, revealWorkspaceKey, setWorkspaceKey, -} from '@yaakapp-internal/crypto'; -import type { WorkspaceMeta } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import { useEffect, useState } from 'react'; -import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from '../hooks/useActiveWorkspace'; -import { createFastMutation } from '../hooks/useFastMutation'; -import { useStateWithDeps } from '../hooks/useStateWithDeps'; -import { showConfirm } from '../lib/confirm'; -import { CopyIconButton } from './CopyIconButton'; -import { Banner } from './core/Banner'; -import type { ButtonProps } from './core/Button'; -import { Button } from './core/Button'; -import { IconButton } from './core/IconButton'; -import { IconTooltip } from './core/IconTooltip'; -import { Label } from './core/Label'; -import { PlainInput } from './core/PlainInput'; -import { HStack, VStack } from './core/Stacks'; -import { EncryptionHelp } from './EncryptionHelp'; +} from "@yaakapp-internal/crypto"; +import type { WorkspaceMeta } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import { useEffect, useState } from "react"; +import { activeWorkspaceAtom, activeWorkspaceMetaAtom } from "../hooks/useActiveWorkspace"; +import { createFastMutation } from "../hooks/useFastMutation"; +import { useStateWithDeps } from "../hooks/useStateWithDeps"; +import { showConfirm } from "../lib/confirm"; +import { CopyIconButton } from "./CopyIconButton"; +import { Banner } from "./core/Banner"; +import type { ButtonProps } from "./core/Button"; +import { Button } from "./core/Button"; +import { IconButton } from "./core/IconButton"; +import { IconTooltip } from "./core/IconTooltip"; +import { Label } from "./core/Label"; +import { PlainInput } from "./core/PlainInput"; +import { HStack, VStack } from "./core/Stacks"; +import { EncryptionHelp } from "./EncryptionHelp"; interface Props { - size?: ButtonProps['size']; + size?: ButtonProps["size"]; expanded?: boolean; onDone?: () => void; onEnabledEncryption?: () => void; @@ -119,7 +119,7 @@ export function WorkspaceEncryptionSetting({ size, expanded, onDone, onEnabledEn
))}
- {selectedColor === 'custom' && ( + {selectedColor === "custom" && ( <> color.match(/#[0-9a-fA-F]{6}$/) !== null} /> diff --git a/src-web/components/core/Confirm.tsx b/src-web/components/core/Confirm.tsx index 938c9442..e292f087 100644 --- a/src-web/components/core/Confirm.tsx +++ b/src-web/components/core/Confirm.tsx @@ -1,10 +1,10 @@ -import type { Color } from '@yaakapp-internal/plugins'; -import type { FormEvent } from 'react'; -import { useState } from 'react'; -import { CopyIconButton } from '../CopyIconButton'; -import { Button } from './Button'; -import { PlainInput } from './PlainInput'; -import { HStack } from './Stacks'; +import type { Color } from "@yaakapp-internal/plugins"; +import type { FormEvent } from "react"; +import { useState } from "react"; +import { CopyIconButton } from "../CopyIconButton"; +import { Button } from "./Button"; +import { PlainInput } from "./PlainInput"; +import { HStack } from "./Stacks"; export interface ConfirmProps { onHide: () => void; @@ -19,9 +19,9 @@ export function Confirm({ onResult, confirmText, requireTyping, - color = 'primary', + color = "primary", }: ConfirmProps) { - const [confirm, setConfirm] = useState(''); + const [confirm, setConfirm] = useState(""); const handleHide = () => { onResult(false); onHide(); @@ -63,7 +63,7 @@ export function Confirm({ )} ); } interface MenuItemHotKeyProps { action: HotkeyAction | undefined; - onSelect: MenuItemProps['onSelect']; - item: MenuItemProps['item']; + onSelect: MenuItemProps["onSelect"]; + item: MenuItemProps["item"]; } function MenuItemHotKey({ action, onSelect, item }: MenuItemHotKeyProps) { diff --git a/src-web/components/core/Editor/BetterMatchDecorator.ts b/src-web/components/core/Editor/BetterMatchDecorator.ts index 5a6e963d..ff9ac078 100644 --- a/src-web/components/core/Editor/BetterMatchDecorator.ts +++ b/src-web/components/core/Editor/BetterMatchDecorator.ts @@ -1,4 +1,4 @@ -import { type DecorationSet, MatchDecorator, type ViewUpdate } from '@codemirror/view'; +import { type DecorationSet, MatchDecorator, type ViewUpdate } from "@codemirror/view"; /** * This is a custom MatchDecorator that will not decorate a match if the selection is inside it diff --git a/src-web/components/core/Editor/DiffViewer.tsx b/src-web/components/core/Editor/DiffViewer.tsx index d8d4dfb9..f36f28ee 100644 --- a/src-web/components/core/Editor/DiffViewer.tsx +++ b/src-web/components/core/Editor/DiffViewer.tsx @@ -1,11 +1,11 @@ -import { yaml } from '@codemirror/lang-yaml'; -import { syntaxHighlighting } from '@codemirror/language'; -import { MergeView } from '@codemirror/merge'; -import { EditorView } from '@codemirror/view'; -import classNames from 'classnames'; -import { useEffect, useRef } from 'react'; -import './DiffViewer.css'; -import { readonlyExtensions, syntaxHighlightStyle } from './extensions'; +import { yaml } from "@codemirror/lang-yaml"; +import { syntaxHighlighting } from "@codemirror/language"; +import { MergeView } from "@codemirror/merge"; +import { EditorView } from "@codemirror/view"; +import classNames from "classnames"; +import { useEffect, useRef } from "react"; +import "./DiffViewer.css"; +import { readonlyExtensions, syntaxHighlightStyle } from "./extensions"; interface Props { /** Original/previous version (left side) */ @@ -45,7 +45,7 @@ export function DiffViewer({ original, modified, className }: Props) { collapseUnchanged: { margin: 2, minSize: 3 }, highlightChanges: false, gutter: true, - orientation: 'a-b', + orientation: "a-b", revertControls: undefined, }); @@ -58,7 +58,7 @@ export function DiffViewer({ original, modified, className }: Props) { return (
); } diff --git a/src-web/components/core/Editor/Editor.css b/src-web/components/core/Editor/Editor.css index b7183b10..6b80aa03 100644 --- a/src-web/components/core/Editor/Editor.css +++ b/src-web/components/core/Editor/Editor.css @@ -438,7 +438,7 @@ } input.cm-textfield { - @apply cursor-text; + @apply cursor-text; } .cm-search label { diff --git a/src-web/components/core/Editor/Editor.tsx b/src-web/components/core/Editor/Editor.tsx index 64e58675..d73255ad 100644 --- a/src-web/components/core/Editor/Editor.tsx +++ b/src-web/components/core/Editor/Editor.tsx @@ -1,21 +1,21 @@ -import { startCompletion } from '@codemirror/autocomplete'; -import { defaultKeymap, historyField, indentWithTab } from '@codemirror/commands'; -import { foldState, forceParsing } from '@codemirror/language'; -import type { EditorStateConfig, Extension } from '@codemirror/state'; -import { Compartment, EditorState } from '@codemirror/state'; -import { EditorView, keymap, placeholder as placeholderExt, tooltips } from '@codemirror/view'; -import { emacs } from '@replit/codemirror-emacs'; -import { vim } from '@replit/codemirror-vim'; +import { startCompletion } from "@codemirror/autocomplete"; +import { defaultKeymap, historyField, indentWithTab } from "@codemirror/commands"; +import { foldState, forceParsing } from "@codemirror/language"; +import type { EditorStateConfig, Extension } from "@codemirror/state"; +import { Compartment, EditorState } from "@codemirror/state"; +import { EditorView, keymap, placeholder as placeholderExt, tooltips } from "@codemirror/view"; +import { emacs } from "@replit/codemirror-emacs"; +import { vim } from "@replit/codemirror-vim"; -import { vscodeKeymap } from '@replit/codemirror-vscode-keymap'; -import type { EditorKeymap } from '@yaakapp-internal/models'; -import { settingsAtom } from '@yaakapp-internal/models'; -import type { EditorLanguage, TemplateFunction } from '@yaakapp-internal/plugins'; -import classNames from 'classnames'; -import type { GraphQLSchema } from 'graphql'; -import { useAtomValue } from 'jotai'; -import { md5 } from 'js-md5'; -import type { ReactNode, RefObject } from 'react'; +import { vscodeKeymap } from "@replit/codemirror-vscode-keymap"; +import type { EditorKeymap } from "@yaakapp-internal/models"; +import { settingsAtom } from "@yaakapp-internal/models"; +import type { EditorLanguage, TemplateFunction } from "@yaakapp-internal/plugins"; +import classNames from "classnames"; +import type { GraphQLSchema } from "graphql"; +import { useAtomValue } from "jotai"; +import { md5 } from "js-md5"; +import type { ReactNode, RefObject } from "react"; import { Children, cloneElement, @@ -25,32 +25,32 @@ import { useLayoutEffect, useMemo, useRef, -} from 'react'; -import { activeEnvironmentAtom } from '../../../hooks/useActiveEnvironment'; -import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables'; -import { useEnvironmentVariables } from '../../../hooks/useEnvironmentVariables'; -import { eventMatchesHotkey } from '../../../hooks/useHotKey'; -import { useRequestEditor } from '../../../hooks/useRequestEditor'; -import { useTemplateFunctionCompletionOptions } from '../../../hooks/useTemplateFunctions'; -import { editEnvironment } from '../../../lib/editEnvironment'; -import { tryFormatJson, tryFormatXml } from '../../../lib/formatters'; -import { jotaiStore } from '../../../lib/jotai'; -import { withEncryptionEnabled } from '../../../lib/setupOrConfigureEncryption'; -import { TemplateFunctionDialog } from '../../TemplateFunctionDialog'; -import { IconButton } from '../IconButton'; -import { HStack } from '../Stacks'; -import './Editor.css'; +} from "react"; +import { activeEnvironmentAtom } from "../../../hooks/useActiveEnvironment"; +import type { WrappedEnvironmentVariable } from "../../../hooks/useEnvironmentVariables"; +import { useEnvironmentVariables } from "../../../hooks/useEnvironmentVariables"; +import { eventMatchesHotkey } from "../../../hooks/useHotKey"; +import { useRequestEditor } from "../../../hooks/useRequestEditor"; +import { useTemplateFunctionCompletionOptions } from "../../../hooks/useTemplateFunctions"; +import { editEnvironment } from "../../../lib/editEnvironment"; +import { tryFormatJson, tryFormatXml } from "../../../lib/formatters"; +import { jotaiStore } from "../../../lib/jotai"; +import { withEncryptionEnabled } from "../../../lib/setupOrConfigureEncryption"; +import { TemplateFunctionDialog } from "../../TemplateFunctionDialog"; +import { IconButton } from "../IconButton"; +import { HStack } from "../Stacks"; +import "./Editor.css"; import { baseExtensions, getLanguageExtension, multiLineExtensions, readonlyExtensions, -} from './extensions'; -import type { GenericCompletionConfig } from './genericCompletion'; -import { singleLineExtensions } from './singleLine'; +} from "./extensions"; +import type { GenericCompletionConfig } from "./genericCompletion"; +import { singleLineExtensions } from "./singleLine"; // VSCode's Tab actions mess with the single-line editor tab actions, so remove it. -const vsCodeWithoutTab = vscodeKeymap.filter((k) => k.key !== 'Tab'); +const vsCodeWithoutTab = vscodeKeymap.filter((k) => k.key !== "Tab"); const keymapExtensions: Record = { vim: vim(), @@ -74,10 +74,10 @@ export interface EditorProps { forcedEnvironmentId?: string; forceUpdateKey?: string | number; format?: (v: string) => Promise; - heightMode?: 'auto' | 'full'; + heightMode?: "auto" | "full"; hideGutter?: boolean; id?: string; - language?: EditorLanguage | 'pairs' | 'url' | 'timeline' | null; + language?: EditorLanguage | "pairs" | "url" | "timeline" | null; lintExtension?: Extension; graphQLSchema?: GraphQLSchema | null; onBlur?: () => void; @@ -92,7 +92,7 @@ export interface EditorProps { containerOnly?: boolean; stateKey: string | null; tooltipContainer?: HTMLElement; - type?: 'text' | 'password'; + type?: "text" | "password"; wrapLines?: boolean; setRef?: (view: EditorView | null) => void; } @@ -147,7 +147,7 @@ function EditorInner({ const useTemplating = !!(autocompleteFunctions || autocompleteVariables || autocomplete); const environmentVariables = useMemo(() => { if (!autocompleteVariables) return emptyVariables; - return typeof autocompleteVariables === 'function' + return typeof autocompleteVariables === "function" ? allEnvironmentVariables.filter(autocompleteVariables) : allEnvironmentVariables; }, [allEnvironmentVariables, autocompleteVariables]); @@ -163,18 +163,18 @@ function EditorInner({ if ( singleLine || language == null || - language === 'text' || - language === 'url' || - language === 'pairs' + language === "text" || + language === "url" || + language === "pairs" ) { disableTabIndent = true; } if (format == null && !readOnly) { format = - language === 'json' + language === "json" ? tryFormatJson - : language === 'xml' || language === 'html' + : language === "xml" || language === "html" ? tryFormatXml : undefined; } @@ -182,37 +182,37 @@ function EditorInner({ const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null); // Use ref so we can update the handler without re-initializing the editor - const handleChange = useRef(onChange); + const handleChange = useRef(onChange); useEffect(() => { handleChange.current = onChange; }, [onChange]); // Use ref so we can update the handler without re-initializing the editor - const handlePaste = useRef(onPaste); + const handlePaste = useRef(onPaste); useEffect(() => { handlePaste.current = onPaste; }, [onPaste]); // Use ref so we can update the handler without re-initializing the editor - const handlePasteOverwrite = useRef(onPasteOverwrite); + const handlePasteOverwrite = useRef(onPasteOverwrite); useEffect(() => { handlePasteOverwrite.current = onPasteOverwrite; }, [onPasteOverwrite]); // Use ref so we can update the handler without re-initializing the editor - const handleFocus = useRef(onFocus); + const handleFocus = useRef(onFocus); useEffect(() => { handleFocus.current = onFocus; }, [onFocus]); // Use ref so we can update the handler without re-initializing the editor - const handleBlur = useRef(onBlur); + const handleBlur = useRef(onBlur); useEffect(() => { handleBlur.current = onBlur; }, [onBlur]); // Use ref so we can update the handler without re-initializing the editor - const handleKeyDown = useRef(onKeyDown); + const handleKeyDown = useRef(onKeyDown); useEffect(() => { handleKeyDown.current = onKeyDown; }, [onKeyDown]); @@ -236,10 +236,10 @@ function EditorInner({ if (cm.current === null) return; const current = keymapCompartment.current.get(cm.current.view.state) ?? []; // PERF: This is expensive with hundreds of editors on screen, so only do it when necessary - if (settings.editorKeymap === 'default' && current === keymapExtensions.default) return; // Nothing to do - if (settings.editorKeymap === 'vim' && current === keymapExtensions.vim) return; // Nothing to do - if (settings.editorKeymap === 'vscode' && current === keymapExtensions.vscode) return; // Nothing to do - if (settings.editorKeymap === 'emacs' && current === keymapExtensions.emacs) return; // Nothing to do + if (settings.editorKeymap === "default" && current === keymapExtensions.default) return; // Nothing to do + if (settings.editorKeymap === "vim" && current === keymapExtensions.vim) return; // Nothing to do + if (settings.editorKeymap === "vscode" && current === keymapExtensions.vscode) return; // Nothing to do + if (settings.editorKeymap === "emacs" && current === keymapExtensions.emacs) return; // Nothing to do const ext = keymapExtensions[settings.editorKeymap] ?? keymapExtensions.default; const effects = keymapCompartment.current.reconfigure(ext); @@ -289,7 +289,7 @@ function EditorInner({ TemplateFunctionDialog.show(fn, tagValue, startPos, cm.current.view); }; - if (fn.name === 'secure') { + if (fn.name === "secure") { withEncryptionEnabled(show); } else { show(); @@ -309,7 +309,7 @@ function EditorInner({ const onClickMissingVariable = useCallback(async (name: string) => { const activeEnvironment = jotaiStore.get(activeEnvironmentAtom); await editEnvironment(activeEnvironment, { - addOrFocusVariable: { name, value: '', enabled: true }, + addOrFocusVariable: { name, value: "", enabled: true }, }); }, []); @@ -414,9 +414,9 @@ function EditorInner({ : []), ]; - const cachedJsonState = getCachedEditorState(defaultValue ?? '', stateKey); + const cachedJsonState = getCachedEditorState(defaultValue ?? "", stateKey); - const doc = `${defaultValue ?? ''}`; + const doc = `${defaultValue ?? ""}`; const config: EditorStateConfig = { extensions, doc }; const state = cachedJsonState @@ -439,7 +439,7 @@ function EditorInner({ } setRef?.(view); } catch (e) { - console.log('Failed to initialize Codemirror', e); + console.log("Failed to initialize Codemirror", e); } }, [forceUpdateKey], @@ -449,7 +449,7 @@ function EditorInner({ useEffect( function updateReadOnlyEditor() { if (readOnly && cm.current?.view != null) { - updateContents(cm.current.view, defaultValue || ''); + updateContents(cm.current.view, defaultValue || ""); } }, [defaultValue, readOnly], @@ -460,7 +460,7 @@ function EditorInner({ function updateNonFocusedEditor() { const notFocused = !cm.current?.view.hasFocus; if (notFocused && cm.current != null) { - updateContents(cm.current.view, defaultValue || ''); + updateContents(cm.current.view, defaultValue || ""); } }, [defaultValue], @@ -470,7 +470,7 @@ function EditorInner({ const decoratedActions = useMemo(() => { const results = []; const actionClassName = classNames( - 'bg-surface transition-opacity transform-gpu opacity-0 group-hover:opacity-100 hover:!opacity-100 shadow', + "bg-surface transition-opacity transform-gpu opacity-0 group-hover:opacity-100 hover:!opacity-100 shadow", ); if (format) { @@ -517,12 +517,12 @@ function EditorInner({ ref={initEditorRef} className={classNames( className, - 'cm-wrapper text-base', - disabled && 'opacity-disabled', - type === 'password' && 'cm-obscure-text', - heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height', - singleLine ? 'cm-singleline' : 'cm-multiline', - readOnly && 'cm-readonly', + "cm-wrapper text-base", + disabled && "opacity-disabled", + type === "password" && "cm-obscure-text", + heightMode === "auto" ? "cm-auto-height" : "cm-full-height", + singleLine ? "cm-singleline" : "cm-multiline", + readOnly && "cm-readonly", )} /> ); @@ -539,8 +539,8 @@ function EditorInner({ space={1} justifyContent="end" className={classNames( - 'absolute bottom-2 left-0 right-0', - 'pointer-events-none', // No pointer events, so we don't block the editor + "absolute bottom-2 left-0 right-0", + "pointer-events-none", // No pointer events, so we don't block the editor )} > {decoratedActions} @@ -562,20 +562,20 @@ function getExtensions({ onFocus, onBlur, onKeyDown, -}: Pick & { - stateKey: EditorProps['stateKey']; +}: Pick & { + stateKey: EditorProps["stateKey"]; container: HTMLDivElement | null; - onChange: RefObject; - onPaste: RefObject; - onPasteOverwrite: RefObject; - onFocus: RefObject; - onBlur: RefObject; - onKeyDown: RefObject; + onChange: RefObject; + onPaste: RefObject; + onPasteOverwrite: RefObject; + onFocus: RefObject; + onBlur: RefObject; + onKeyDown: RefObject; }) { // TODO: Ensure tooltips render inside the dialog if we are in one. const parent = container?.closest('[role="dialog"]') ?? - document.querySelector('#cm-portal') ?? + document.querySelector("#cm-portal") ?? undefined; return [ @@ -589,7 +589,7 @@ function getExtensions({ }, keydown: (e, view) => { // Check if the hotkey matches the editor.autocomplete action - if (eventMatchesHotkey(e, 'editor.autocomplete')) { + if (eventMatchesHotkey(e, "editor.autocomplete")) { e.preventDefault(); startCompletion(view); return true; @@ -597,7 +597,7 @@ function getExtensions({ onKeyDown.current?.(e); }, paste: (e, v) => { - const textData = e.clipboardData?.getData('text/plain') ?? ''; + const textData = e.clipboardData?.getData("text/plain") ?? ""; onPaste.current?.(textData); if (v.state.selection.main.from === 0 && v.state.selection.main.to === v.state.doc.length) { onPasteOverwrite.current?.(e, textData); @@ -605,7 +605,7 @@ function getExtensions({ }, }), tooltips({ parent }), - keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap), + keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== "Enter") : defaultKeymap), ...(singleLine ? [singleLineExtensions()] : []), ...(!singleLine ? multiLineExtensions({ hideGutter }) : []), ...(readOnly ? readonlyExtensions : []), @@ -627,10 +627,10 @@ function getExtensions({ } const placeholderElFromText = (text: string | undefined) => { - const el = document.createElement('div'); + const el = document.createElement("div"); // Default to because codemirror needs it for sizing. I'm not sure why, but probably something // to do with how Yaak "hacks" it with CSS for single line input. - el.innerHTML = text ? text.replaceAll('\n', '
') : ' '; + el.innerHTML = text ? text.replaceAll("\n", "
") : " "; return el; }; @@ -646,7 +646,7 @@ function saveCachedEditorState(stateKey: string | null, state: EditorState | nul try { sessionStorage.setItem(computeFullStateKey(stateKey), JSON.stringify(stateObj)); } catch (err) { - console.log('Failed to save to editor state', stateKey, err); + console.log("Failed to save to editor state", stateKey, err); } } @@ -667,7 +667,7 @@ function getCachedEditorState(doc: string, stateKey: string | null) { state.doc = doc; return state; } catch (err) { - console.log('Failed to restore editor storage', stateKey, err); + console.log("Failed to restore editor storage", stateKey, err); } return null; diff --git a/src-web/components/core/Editor/LazyEditor.tsx b/src-web/components/core/Editor/LazyEditor.tsx index c2ae1ab1..93a2706f 100644 --- a/src-web/components/core/Editor/LazyEditor.tsx +++ b/src-web/components/core/Editor/LazyEditor.tsx @@ -1,7 +1,7 @@ -import { lazy, Suspense } from 'react'; -import type { EditorProps } from './Editor'; +import { lazy, Suspense } from "react"; +import type { EditorProps } from "./Editor"; -const Editor_ = lazy(() => import('./Editor').then((m) => ({ default: m.Editor }))); +const Editor_ = lazy(() => import("./Editor").then((m) => ({ default: m.Editor }))); export function Editor(props: EditorProps) { return ( diff --git a/src-web/components/core/Editor/extensions.ts b/src-web/components/core/Editor/extensions.ts index 8db6750d..ec9d46ce 100644 --- a/src-web/components/core/Editor/extensions.ts +++ b/src-web/components/core/Editor/extensions.ts @@ -3,15 +3,15 @@ import { closeBrackets, closeBracketsKeymap, completionKeymap, -} from '@codemirror/autocomplete'; -import { history, historyKeymap } from '@codemirror/commands'; -import { go } from '@codemirror/lang-go'; -import { java } from '@codemirror/lang-java'; -import { javascript } from '@codemirror/lang-javascript'; -import { markdown } from '@codemirror/lang-markdown'; -import { php } from '@codemirror/lang-php'; -import { python } from '@codemirror/lang-python'; -import { xml } from '@codemirror/lang-xml'; +} from "@codemirror/autocomplete"; +import { history, historyKeymap } from "@codemirror/commands"; +import { go } from "@codemirror/lang-go"; +import { java } from "@codemirror/lang-java"; +import { javascript } from "@codemirror/lang-javascript"; +import { markdown } from "@codemirror/lang-markdown"; +import { php } from "@codemirror/lang-php"; +import { python } from "@codemirror/lang-python"; +import { xml } from "@codemirror/lang-xml"; import { bracketMatching, codeFolding, @@ -22,20 +22,20 @@ import { LanguageSupport, StreamLanguage, syntaxHighlighting, -} from '@codemirror/language'; -import { c, csharp, kotlin, objectiveC } from '@codemirror/legacy-modes/mode/clike'; -import { clojure } from '@codemirror/legacy-modes/mode/clojure'; -import { http } from '@codemirror/legacy-modes/mode/http'; -import { oCaml } from '@codemirror/legacy-modes/mode/mllike'; -import { powerShell } from '@codemirror/legacy-modes/mode/powershell'; -import { r } from '@codemirror/legacy-modes/mode/r'; -import { ruby } from '@codemirror/legacy-modes/mode/ruby'; -import { shell } from '@codemirror/legacy-modes/mode/shell'; -import { swift } from '@codemirror/legacy-modes/mode/swift'; -import { linter, lintGutter, lintKeymap } from '@codemirror/lint'; -import { search, searchKeymap } from '@codemirror/search'; -import type { Extension } from '@codemirror/state'; -import { EditorState } from '@codemirror/state'; +} from "@codemirror/language"; +import { c, csharp, kotlin, objectiveC } from "@codemirror/legacy-modes/mode/clike"; +import { clojure } from "@codemirror/legacy-modes/mode/clojure"; +import { http } from "@codemirror/legacy-modes/mode/http"; +import { oCaml } from "@codemirror/legacy-modes/mode/mllike"; +import { powerShell } from "@codemirror/legacy-modes/mode/powershell"; +import { r } from "@codemirror/legacy-modes/mode/r"; +import { ruby } from "@codemirror/legacy-modes/mode/ruby"; +import { shell } from "@codemirror/legacy-modes/mode/shell"; +import { swift } from "@codemirror/legacy-modes/mode/swift"; +import { linter, lintGutter, lintKeymap } from "@codemirror/lint"; +import { search, searchKeymap } from "@codemirror/search"; +import type { Extension } from "@codemirror/state"; +import { EditorState } from "@codemirror/state"; import { crosshairCursor, drawSelection, @@ -46,51 +46,51 @@ import { keymap, lineNumbers, rectangularSelection, -} from '@codemirror/view'; -import { tags as t } from '@lezer/highlight'; -import { jsonc, jsoncLanguage } from '@shopify/lang-jsonc'; -import { graphql } from 'cm6-graphql'; -import type { GraphQLSchema } from 'graphql'; -import { activeRequestIdAtom } from '../../../hooks/useActiveRequestId'; -import type { WrappedEnvironmentVariable } from '../../../hooks/useEnvironmentVariables'; -import { jotaiStore } from '../../../lib/jotai'; -import { renderMarkdown } from '../../../lib/markdown'; -import { pluralizeCount } from '../../../lib/pluralize'; -import { showGraphQLDocExplorerAtom } from '../../graphql/graphqlAtoms'; -import type { EditorProps } from './Editor'; -import { jsonParseLinter } from './json-lint'; -import { pairs } from './pairs/extension'; -import { searchMatchCount } from './searchMatchCount'; -import { text } from './text/extension'; -import { timeline } from './timeline/extension'; -import type { TwigCompletionOption } from './twig/completion'; -import { twig } from './twig/extension'; -import { pathParametersPlugin } from './twig/pathParameters'; -import { url } from './url/extension'; +} from "@codemirror/view"; +import { tags as t } from "@lezer/highlight"; +import { jsonc, jsoncLanguage } from "@shopify/lang-jsonc"; +import { graphql } from "cm6-graphql"; +import type { GraphQLSchema } from "graphql"; +import { activeRequestIdAtom } from "../../../hooks/useActiveRequestId"; +import type { WrappedEnvironmentVariable } from "../../../hooks/useEnvironmentVariables"; +import { jotaiStore } from "../../../lib/jotai"; +import { renderMarkdown } from "../../../lib/markdown"; +import { pluralizeCount } from "../../../lib/pluralize"; +import { showGraphQLDocExplorerAtom } from "../../graphql/graphqlAtoms"; +import type { EditorProps } from "./Editor"; +import { jsonParseLinter } from "./json-lint"; +import { pairs } from "./pairs/extension"; +import { searchMatchCount } from "./searchMatchCount"; +import { text } from "./text/extension"; +import { timeline } from "./timeline/extension"; +import type { TwigCompletionOption } from "./twig/completion"; +import { twig } from "./twig/extension"; +import { pathParametersPlugin } from "./twig/pathParameters"; +import { url } from "./url/extension"; export const syntaxHighlightStyle = HighlightStyle.define([ { tag: [t.documentMeta, t.blockComment, t.lineComment, t.docComment, t.comment], - color: 'var(--textSubtlest)', + color: "var(--textSubtlest)", }, { tag: [t.emphasis], - textDecoration: 'underline', + textDecoration: "underline", }, { tag: [t.angleBracket, t.paren, t.bracket, t.squareBracket, t.brace, t.separator, t.punctuation], - color: 'var(--textSubtle)', + color: "var(--textSubtle)", }, { tag: [t.link, t.name, t.tagName, t.angleBracket, t.docString, t.number], - color: 'var(--info)', + color: "var(--info)", }, - { tag: [t.variableName], color: 'var(--success)' }, - { tag: [t.bool], color: 'var(--warning)' }, - { tag: [t.attributeName, t.propertyName], color: 'var(--primary)' }, - { tag: [t.attributeValue], color: 'var(--warning)' }, - { tag: [t.string], color: 'var(--notice)' }, - { tag: [t.atom, t.meta, t.operator, t.bool, t.null, t.keyword], color: 'var(--danger)' }, + { tag: [t.variableName], color: "var(--success)" }, + { tag: [t.bool], color: "var(--warning)" }, + { tag: [t.attributeName, t.propertyName], color: "var(--primary)" }, + { tag: [t.attributeValue], color: "var(--warning)" }, + { tag: [t.string], color: "var(--notice)" }, + { tag: [t.atom, t.meta, t.operator, t.bool, t.null, t.keyword], color: "var(--danger)" }, ]); const syntaxTheme = EditorView.theme({}, { dark: true }); @@ -102,7 +102,7 @@ const legacyLang = (mode: Parameters[0]) => { }; const syntaxExtensions: Record< - NonNullable, + NonNullable, null | (() => LanguageSupport) > = { graphql: null, @@ -134,11 +134,11 @@ const syntaxExtensions: Record< swift: legacyLang(swift), }; -const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ['json', 'javascript', 'graphql']; +const closeBracketsFor: (keyof typeof syntaxExtensions)[] = ["json", "javascript", "graphql"]; export function getLanguageExtension({ useTemplating, - language = 'text', + language = "text", lintExtension, environmentVariables, autocomplete, @@ -156,10 +156,10 @@ export function getLanguageExtension({ onClickPathParameter: (name: string) => void; completionOptions: TwigCompletionOption[]; graphQLSchema: GraphQLSchema | null; -} & Pick) { +} & Pick) { const extraExtensions: Extension[] = []; - if (language === 'url') { + if (language === "url") { extraExtensions.push(pathParametersPlugin(onClickPathParameter)); } @@ -169,13 +169,13 @@ export function getLanguageExtension({ } // GraphQL is a special exception - if (language === 'graphql') { + if (language === "graphql") { return [ graphql(graphQLSchema ?? undefined, { async onCompletionInfoRender(gqlCompletionItem): Promise { if (!gqlCompletionItem.documentation) return null; const innerHTML = await renderMarkdown(gqlCompletionItem.documentation); - const span = document.createElement('span'); + const span = document.createElement("span"); span.innerHTML = innerHTML; return span; }, @@ -192,11 +192,11 @@ export function getLanguageExtension({ ]; } - if (language === 'json') { + if (language === "json") { extraExtensions.push(lintExtension ?? linter(jsonParseLinter())); extraExtensions.push( jsoncLanguage.data.of({ - commentTokens: { line: '//', block: { open: '/*', close: '*/' } }, + commentTokens: { line: "//", block: { open: "/*", close: "*/" } }, }), ); if (!hideGutter) { @@ -205,7 +205,7 @@ export function getLanguageExtension({ } const maybeBase = language ? syntaxExtensions[language] : null; - const base = typeof maybeBase === 'function' ? maybeBase() : null; + const base = typeof maybeBase === "function" ? maybeBase() : null; if (base == null) { return []; } @@ -229,10 +229,10 @@ export function getLanguageExtension({ // Filter out autocomplete start triggers from completionKeymap since we handle it via configurable hotkeys. // Keep navigation keys (ArrowUp/Down, Enter, Escape, etc.) but remove startCompletion bindings. const filteredCompletionKeymap = completionKeymap.filter((binding) => { - const key = binding.key?.toLowerCase() ?? ''; - const mac = (binding as { mac?: string }).mac?.toLowerCase() ?? ''; + const key = binding.key?.toLowerCase() ?? ""; + const mac = (binding as { mac?: string }).mac?.toLowerCase() ?? ""; // Filter out Ctrl-Space and Mac-specific autocomplete triggers (Alt-`, Alt-i) - const isStartTrigger = key.includes('space') || mac.includes('alt-') || mac.includes('`'); + const isStartTrigger = key.includes("space") || mac.includes("alt-") || mac.includes("`"); return !isStartTrigger; }); @@ -242,7 +242,7 @@ export const baseExtensions = [ dropCursor(), drawSelection(), autocompletion({ - tooltipClass: () => 'x-theme-menu', + tooltipClass: () => "x-theme-menu", closeOnBlur: true, // Set to `false` for debugging in devtools without closing it defaultKeymap: false, // We handle the trigger via configurable hotkeys compareCompletions: (a, b) => { @@ -257,7 +257,7 @@ export const baseExtensions = [ export const readonlyExtensions = [ EditorState.readOnly.of(true), - EditorView.contentAttributes.of({ tabindex: '-1' }), + EditorView.contentAttributes.of({ tabindex: "-1" }), ]; export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => [ @@ -269,11 +269,11 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => lineNumbers(), foldGutter({ markerDOM: (open) => { - const el = document.createElement('div'); - el.classList.add('fold-gutter-icon'); + const el = document.createElement("div"); + el.classList.add("fold-gutter-icon"); el.tabIndex = -1; if (open) { - el.setAttribute('data-open', ''); + el.setAttribute("data-open", ""); } return el; }, @@ -281,12 +281,12 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => ], codeFolding({ placeholderDOM(_view, onclick, prepared) { - const el = document.createElement('span'); + const el = document.createElement("span"); el.onclick = onclick; - el.className = 'cm-foldPlaceholder'; - el.innerText = prepared || '…'; - el.title = 'unfold'; - el.ariaLabel = 'folded code'; + el.className = "cm-foldPlaceholder"; + el.innerText = prepared || "…"; + el.title = "unfold"; + el.ariaLabel = "folded code"; return el; }, /** @@ -295,15 +295,15 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => */ preparePlaceholder(state, range) { let count: number | undefined; - let startToken = '{'; - let endToken = '}'; + let startToken = "{"; + let endToken = "}"; const prevLine = state.doc.lineAt(range.from).text; - const isArray = prevLine.lastIndexOf('[') > prevLine.lastIndexOf('{'); + const isArray = prevLine.lastIndexOf("[") > prevLine.lastIndexOf("{"); if (isArray) { - startToken = '['; - endToken = ']'; + startToken = "["; + endToken = "]"; } const internal = state.sliceDoc(range.from, range.to); @@ -317,7 +317,7 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => } if (count !== undefined) { - const label = isArray ? 'item' : 'key'; + const label = isArray ? "item" : "key"; return pluralizeCount(label, count); } }, diff --git a/src-web/components/core/Editor/filter/extension.ts b/src-web/components/core/Editor/filter/extension.ts index 580f1511..88f6c4bb 100644 --- a/src-web/components/core/Editor/filter/extension.ts +++ b/src-web/components/core/Editor/filter/extension.ts @@ -1,8 +1,8 @@ -import type { Completion, CompletionContext, CompletionResult } from '@codemirror/autocomplete'; -import { autocompletion, startCompletion } from '@codemirror/autocomplete'; -import { LanguageSupport, LRLanguage, syntaxTree } from '@codemirror/language'; -import type { SyntaxNode } from '@lezer/common'; -import { parser } from './filter'; +import type { Completion, CompletionContext, CompletionResult } from "@codemirror/autocomplete"; +import { autocompletion, startCompletion } from "@codemirror/autocomplete"; +import { LanguageSupport, LRLanguage, syntaxTree } from "@codemirror/language"; +import type { SyntaxNode } from "@lezer/common"; +import { parser } from "./filter"; export interface FieldDef { name: string; @@ -43,7 +43,7 @@ function inPhrase(ctx: CompletionContext): boolean { // Lezer node names from your grammar: Phrase is the quoted token let n: SyntaxNode | null = syntaxTree(ctx.state).resolveInner(ctx.pos, -1); while (n) { - if (n.name === 'Phrase') return true; + if (n.name === "Phrase") return true; n = n.parent; } return false; @@ -53,7 +53,7 @@ function inPhrase(ctx: CompletionContext): boolean { function inUnclosedQuote(doc: string, pos: number): boolean { let quotes = 0; for (let i = 0; i < pos; i++) { - if (doc[i] === '"' && doc[i - 1] !== '\\') quotes++; + if (doc[i] === '"' && doc[i - 1] !== "\\") quotes++; } return quotes % 2 === 1; // odd = inside an open quote } @@ -64,13 +64,13 @@ function inUnclosedQuote(doc: string, pos: number): boolean { * - Otherwise, we're in a field name or bare term position. */ function contextInfo(stateDoc: string, pos: number) { - const lastColon = stateDoc.lastIndexOf(':', pos - 1); + const lastColon = stateDoc.lastIndexOf(":", pos - 1); const lastBoundary = Math.max( - stateDoc.lastIndexOf(' ', pos - 1), - stateDoc.lastIndexOf('\t', pos - 1), - stateDoc.lastIndexOf('\n', pos - 1), - stateDoc.lastIndexOf('(', pos - 1), - stateDoc.lastIndexOf(')', pos - 1), + stateDoc.lastIndexOf(" ", pos - 1), + stateDoc.lastIndexOf("\t", pos - 1), + stateDoc.lastIndexOf("\n", pos - 1), + stateDoc.lastIndexOf("(", pos - 1), + stateDoc.lastIndexOf(")", pos - 1), ); const inValue = lastColon > lastBoundary; @@ -96,7 +96,7 @@ function contextInfo(stateDoc: string, pos: number) { function fieldNameCompletions(fieldNames: string[]): Completion[] { return fieldNames.map((name) => ({ label: name, - type: 'property', + type: "property", apply: (view, _completion, from, to) => { // Insert "name:" (leave cursor right after colon) view.dispatch({ @@ -117,7 +117,7 @@ function fieldValueCompletions( return vals.map((v) => ({ label: v.match(IDENT_ONLY) ? v : `"${v}"`, displayLabel: v, - type: 'constant', + type: "constant", })); } @@ -169,7 +169,7 @@ function makeCompletionSource(opts: FilterOptions) { } const language = LRLanguage.define({ - name: 'filter', + name: "filter", parser, languageData: { autocompletion: {}, diff --git a/src-web/components/core/Editor/filter/filter.ts b/src-web/components/core/Editor/filter/filter.ts index e943978f..f7a1dae3 100644 --- a/src-web/components/core/Editor/filter/filter.ts +++ b/src-web/components/core/Editor/filter/filter.ts @@ -1,20 +1,20 @@ /* oxlint-disable */ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "%QOVQPOOPeOPOOOVQPO'#CfOjQPO'#ChO!XQPO'#CgOOQO'#Cc'#CcOVQPO'#CaOOQO'#Ca'#CaO!oQPO'#C`O!|QPO'#C_OOQO'#C^'#C^QOQPOOPOOO'#Cp'#CpP#XOPO)C>jO#`QPO,59QO#eQPO,59ROOQO,58{,58{OVQPO'#CqOOQO'#Cq'#CqO#mQPO,58zOVQPO'#CrO#zQPO,58yPOOO-E6n-E6nOOQO1G.l1G.lOOQO'#Cm'#CmOOQO'#Ck'#CkOOQO1G.m1G.mOOQO,59],59]OOQO-E6o-E6oOOQO,59^,59^OOQO-E6p-E6p", stateData: - '$]~OiPQ~OUUOXQO]RO`TO~Oi[O~OUaXXaX]aX^[X`aXbaXcaXgaXWaX~O^_O~OUUOXQO]RO`TObaO~OcSXgSXWSX~P!^OcdOgRXWRX~Oi[O~Qh]WgO~O]hO`iO~OcSagSaWSa~P!^OcdOgRaWRa~OUbc]c~', - goto: '#hgPPhnryP!YPP!c!c!lPP!uP!xPP#U#[#bQZOR^QTYOQSXOQRmdUWOQdQ`USbWcRka_VOQUWacd_TOQUWacd_SOQUWacdRj_^TOQUWacdRi_Q]PRf]QcWRlcQeXRne', + "$]~OiPQ~OUUOXQO]RO`TO~Oi[O~OUaXXaX]aX^[X`aXbaXcaXgaXWaX~O^_O~OUUOXQO]RO`TObaO~OcSXgSXWSX~P!^OcdOgRXWRX~Oi[O~Qh]WgO~O]hO`iO~OcSagSaWSa~P!^OcdOgRaWRa~OUbc]c~", + goto: "#hgPPhnryP!YPP!c!c!lPP!uP!xPP#U#[#bQZOR^QTYOQSXOQRmdUWOQdQ`USbWcRka_VOQUWacd_TOQUWacd_SOQUWacdRj_^TOQUWacdRi_Q]PRf]QcWRlcQeXRne", nodeNames: - '⚠ Query Expr OrExpr AndExpr Unary Not Primary RParen LParen Group Field FieldName Word Colon FieldValue Phrase Term And Or', + "⚠ Query Expr OrExpr AndExpr Unary Not Primary RParen LParen Group Field FieldName Word Colon FieldValue Phrase Term And Or", maxTerm: 25, nodeProps: [ - ['openedBy', 8, 'LParen'], - ['closedBy', 9, 'RParen'], + ["openedBy", 8, "LParen"], + ["closedBy", 9, "RParen"], ], propSources: [highlight], skippedNodes: [0, 20], diff --git a/src-web/components/core/Editor/filter/highlight.ts b/src-web/components/core/Editor/filter/highlight.ts index 6529fda9..0491e5bb 100644 --- a/src-web/components/core/Editor/filter/highlight.ts +++ b/src-web/components/core/Editor/filter/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ // Boolean operators @@ -16,6 +16,6 @@ export const highlight = styleTags({ Phrase: t.string, // "quoted string" // Fields - 'FieldName/Word': t.attributeName, - 'FieldValue/Term/Word': t.attributeValue, + "FieldName/Word": t.attributeName, + "FieldValue/Term/Word": t.attributeValue, }); diff --git a/src-web/components/core/Editor/filter/query.ts b/src-web/components/core/Editor/filter/query.ts index 0299e70e..6c3eb5be 100644 --- a/src-web/components/core/Editor/filter/query.ts +++ b/src-web/components/core/Editor/filter/query.ts @@ -1,33 +1,33 @@ // query.ts // A tiny query language parser with NOT/AND/OR, parentheses, phrases, negation, and field:value. -import { fuzzyMatch } from 'fuzzbunny'; +import { fuzzyMatch } from "fuzzbunny"; ///////////////////////// // AST ///////////////////////// export type Ast = - | { type: 'Term'; value: string } // foo - | { type: 'Phrase'; value: string } // "hi there" - | { type: 'Field'; field: string; value: string } // method:POST or title:"exact phrase" - | { type: 'Not'; node: Ast } // -foo or NOT foo - | { type: 'And'; left: Ast; right: Ast } // a AND b - | { type: 'Or'; left: Ast; right: Ast }; // a OR b + | { type: "Term"; value: string } // foo + | { type: "Phrase"; value: string } // "hi there" + | { type: "Field"; field: string; value: string } // method:POST or title:"exact phrase" + | { type: "Not"; node: Ast } // -foo or NOT foo + | { type: "And"; left: Ast; right: Ast } // a AND b + | { type: "Or"; left: Ast; right: Ast }; // a OR b ///////////////////////// // Tokenizer ///////////////////////// type Tok = - | { kind: 'LPAREN' } - | { kind: 'RPAREN' } - | { kind: 'AND' } - | { kind: 'OR' } - | { kind: 'NOT' } // explicit NOT - | { kind: 'MINUS' } // unary minus before term/phrase/paren group - | { kind: 'COLON' } - | { kind: 'WORD'; text: string } // bareword (unquoted) - | { kind: 'PHRASE'; text: string } // "quoted phrase" - | { kind: 'EOF' }; + | { kind: "LPAREN" } + | { kind: "RPAREN" } + | { kind: "AND" } + | { kind: "OR" } + | { kind: "NOT" } // explicit NOT + | { kind: "MINUS" } // unary minus before term/phrase/paren group + | { kind: "COLON" } + | { kind: "WORD"; text: string } // bareword (unquoted) + | { kind: "PHRASE"; text: string } // "quoted phrase" + | { kind: "EOF" }; const isSpace = (c: string) => /\s/.test(c); const isIdent = (c: string) => /[A-Za-z0-9_\-./]/.test(c); @@ -37,11 +37,11 @@ export function tokenize(input: string): Tok[] { let i = 0; const n = input.length; - const peek = () => input[i] ?? ''; + const peek = () => input[i] ?? ""; const advance = () => input[i++]; const readWord = () => { - let s = ''; + let s = ""; while (i < n && isIdent(peek())) s += advance(); return s; }; @@ -49,11 +49,11 @@ export function tokenize(input: string): Tok[] { const readPhrase = () => { // assumes current char is opening quote advance(); // consume opening " - let s = ''; + let s = ""; while (i < n) { const c = advance(); if (c === `"`) break; - if (c === '\\' && i < n) { + if (c === "\\" && i < n) { // escape \" and \\ (simple) const next = advance(); s += next; @@ -72,28 +72,28 @@ export function tokenize(input: string): Tok[] { continue; } - if (c === '(') { - toks.push({ kind: 'LPAREN' }); + if (c === "(") { + toks.push({ kind: "LPAREN" }); i++; continue; } - if (c === ')') { - toks.push({ kind: 'RPAREN' }); + if (c === ")") { + toks.push({ kind: "RPAREN" }); i++; continue; } - if (c === ':') { - toks.push({ kind: 'COLON' }); + if (c === ":") { + toks.push({ kind: "COLON" }); i++; continue; } if (c === `"`) { const text = readPhrase(); - toks.push({ kind: 'PHRASE', text }); + toks.push({ kind: "PHRASE", text }); continue; } - if (c === '-') { - toks.push({ kind: 'MINUS' }); + if (c === "-") { + toks.push({ kind: "MINUS" }); i++; continue; } @@ -102,10 +102,10 @@ export function tokenize(input: string): Tok[] { if (isIdent(c)) { const w = readWord(); const upper = w.toUpperCase(); - if (upper === 'AND') toks.push({ kind: 'AND' }); - else if (upper === 'OR') toks.push({ kind: 'OR' }); - else if (upper === 'NOT') toks.push({ kind: 'NOT' }); - else toks.push({ kind: 'WORD', text: w }); + if (upper === "AND") toks.push({ kind: "AND" }); + else if (upper === "OR") toks.push({ kind: "OR" }); + else if (upper === "NOT") toks.push({ kind: "NOT" }); + else toks.push({ kind: "WORD", text: w }); continue; } @@ -113,7 +113,7 @@ export function tokenize(input: string): Tok[] { i++; } - toks.push({ kind: 'EOF' }); + toks.push({ kind: "EOF" }); return toks; } @@ -122,20 +122,20 @@ class Parser { constructor(private toks: Tok[]) {} private peek(): Tok { - return this.toks[this.i] ?? { kind: 'EOF' }; + return this.toks[this.i] ?? { kind: "EOF" }; } private advance(): Tok { - return this.toks[this.i++] ?? { kind: 'EOF' }; + return this.toks[this.i++] ?? { kind: "EOF" }; } - private at(kind: Tok['kind']) { + private at(kind: Tok["kind"]) { return this.peek().kind === kind; } // Top-level: parse OR-precedence chain, allowing implicit AND. parse(): Ast | null { - if (this.at('EOF')) return null; + if (this.at("EOF")) return null; const expr = this.parseOr(); - if (!this.at('EOF')) { + if (!this.at("EOF")) { // Optionally, consume remaining tokens or throw } return expr; @@ -144,10 +144,10 @@ class Parser { // Precedence: NOT (highest), AND, OR (lowest) private parseOr(): Ast { let node = this.parseAnd(); - while (this.at('OR')) { + while (this.at("OR")) { this.advance(); const rhs = this.parseAnd(); - node = { type: 'Or', left: node, right: rhs }; + node = { type: "Or", left: node, right: rhs }; } return node; } @@ -155,31 +155,31 @@ class Parser { private parseAnd(): Ast { let node = this.parseUnary(); // Implicit AND: if next token starts a primary, treat as AND. - while (this.at('AND') || this.startsPrimary()) { - if (this.at('AND')) this.advance(); + while (this.at("AND") || this.startsPrimary()) { + if (this.at("AND")) this.advance(); const rhs = this.parseUnary(); - node = { type: 'And', left: node, right: rhs }; + node = { type: "And", left: node, right: rhs }; } return node; } private parseUnary(): Ast { - if (this.at('NOT') || this.at('MINUS')) { + if (this.at("NOT") || this.at("MINUS")) { this.advance(); const node = this.parseUnary(); - return { type: 'Not', node }; + return { type: "Not", node }; } return this.parsePrimaryOrField(); } private startsPrimary(): boolean { const k = this.peek().kind; - return k === 'WORD' || k === 'PHRASE' || k === 'LPAREN' || k === 'MINUS' || k === 'NOT'; + return k === "WORD" || k === "PHRASE" || k === "LPAREN" || k === "MINUS" || k === "NOT"; } private parsePrimaryOrField(): Ast { // Parenthesized group - if (this.at('LPAREN')) { + if (this.at("LPAREN")) { this.advance(); const inside = this.parseOr(); // if (!this.at('RPAREN')) throw new Error("Missing closing ')'"); @@ -188,59 +188,59 @@ class Parser { } // Phrase - if (this.at('PHRASE')) { - const t = this.advance() as Extract; - return { type: 'Phrase', value: t.text }; + if (this.at("PHRASE")) { + const t = this.advance() as Extract; + return { type: "Phrase", value: t.text }; } // Field or bare word - if (this.at('WORD')) { - const wordTok = this.advance() as Extract; + if (this.at("WORD")) { + const wordTok = this.advance() as Extract; - if (this.at('COLON')) { + if (this.at("COLON")) { // field:value or field:"phrase" this.advance(); // : let value: string; - if (this.at('PHRASE')) { - const p = this.advance() as Extract; + if (this.at("PHRASE")) { + const p = this.advance() as Extract; value = p.text; - } else if (this.at('WORD')) { - const w = this.advance() as Extract; + } else if (this.at("WORD")) { + const w = this.advance() as Extract; value = w.text; } else { // Anything else after colon is treated literally as a single Term token. const t = this.advance(); value = tokText(t); } - return { type: 'Field', field: wordTok.text, value }; + return { type: "Field", field: wordTok.text, value }; } // plain term - return { type: 'Term', value: wordTok.text }; + return { type: "Term", value: wordTok.text }; } - const w = this.advance() as Extract; - return { type: 'Phrase', value: 'text' in w ? w.text : '' }; + const w = this.advance() as Extract; + return { type: "Phrase", value: "text" in w ? w.text : "" }; } } function tokText(t: Tok): string { - if ('text' in t) return t.text; + if ("text" in t) return t.text; switch (t.kind) { - case 'COLON': - return ':'; - case 'LPAREN': - return '('; - case 'RPAREN': - return ')'; + case "COLON": + return ":"; + case "LPAREN": + return "("; + case "RPAREN": + return ")"; default: - return ''; + return ""; } } export function parseQuery(q: string): Ast | null { - if (q.trim() === '') return null; + if (q.trim() === "") return null; const toks = tokenize(q); const parser = new Parser(toks); return parser.parse(); @@ -251,45 +251,45 @@ export type Doc = { fields?: Record; }; -type Technique = 'substring' | 'fuzzy' | 'strict'; +type Technique = "substring" | "fuzzy" | "strict"; function includes(hay: string | undefined, needle: string, technique: Technique): boolean { if (!hay || !needle) return false; - if (technique === 'strict') return hay === needle; - if (technique === 'fuzzy') return !!fuzzyMatch(hay, needle); + if (technique === "strict") return hay === needle; + if (technique === "fuzzy") return !!fuzzyMatch(hay, needle); return hay.indexOf(needle) !== -1; } export function evaluate(ast: Ast | null, doc: Doc): boolean { if (!ast) return true; // Match everything if no query is provided - const text = (doc.text ?? '').toLowerCase(); + const text = (doc.text ?? "").toLowerCase(); const fieldsNorm: Record = {}; for (const [k, v] of Object.entries(doc.fields ?? {})) { - if (!(typeof v === 'string' || Array.isArray(v))) continue; + if (!(typeof v === "string" || Array.isArray(v))) continue; fieldsNorm[k.toLowerCase()] = Array.isArray(v) - ? v.filter((v) => typeof v === 'string').map((s) => s.toLowerCase()) - : [String(v ?? '').toLowerCase()]; + ? v.filter((v) => typeof v === "string").map((s) => s.toLowerCase()) + : [String(v ?? "").toLowerCase()]; } const evalNode = (node: Ast): boolean => { switch (node.type) { - case 'Term': - return includes(text, node.value.toLowerCase(), 'fuzzy'); - case 'Phrase': + case "Term": + return includes(text, node.value.toLowerCase(), "fuzzy"); + case "Phrase": // Quoted phrases match exactly - return includes(text, node.value.toLowerCase(), 'substring'); - case 'Field': { + return includes(text, node.value.toLowerCase(), "substring"); + case "Field": { const vals = fieldsNorm[node.field.toLowerCase()] ?? []; if (vals.length === 0) return false; - return vals.some((v) => includes(v, node.value.toLowerCase(), 'substring')); + return vals.some((v) => includes(v, node.value.toLowerCase(), "substring")); } - case 'Not': + case "Not": return !evalNode(node.node); - case 'And': + case "And": return evalNode(node.left) && evalNode(node.right); - case 'Or': + case "Or": return evalNode(node.left) || evalNode(node.right); } }; diff --git a/src-web/components/core/Editor/genericCompletion.ts b/src-web/components/core/Editor/genericCompletion.ts index bfdecef2..4eaef118 100644 --- a/src-web/components/core/Editor/genericCompletion.ts +++ b/src-web/components/core/Editor/genericCompletion.ts @@ -1,6 +1,6 @@ -import type { CompletionContext } from '@codemirror/autocomplete'; -import type { GenericCompletionOption } from '@yaakapp-internal/plugins'; -import { defaultBoost } from './twig/completion'; +import type { CompletionContext } from "@codemirror/autocomplete"; +import type { GenericCompletionOption } from "@yaakapp-internal/plugins"; +import { defaultBoost } from "./twig/completion"; export interface GenericCompletionConfig { minMatch?: number; diff --git a/src-web/components/core/Editor/hyperlink/extension.ts b/src-web/components/core/Editor/hyperlink/extension.ts index 73e5cf8e..7f1f1a96 100644 --- a/src-web/components/core/Editor/hyperlink/extension.ts +++ b/src-web/components/core/Editor/hyperlink/extension.ts @@ -1,9 +1,9 @@ -import type { DecorationSet, ViewUpdate } from '@codemirror/view'; -import { Decoration, EditorView, hoverTooltip, MatchDecorator, ViewPlugin } from '@codemirror/view'; -import { activeWorkspaceIdAtom } from '../../../../hooks/useActiveWorkspace'; -import { copyToClipboard } from '../../../../lib/copy'; -import { createRequestAndNavigate } from '../../../../lib/createRequestAndNavigate'; -import { jotaiStore } from '../../../../lib/jotai'; +import type { DecorationSet, ViewUpdate } from "@codemirror/view"; +import { Decoration, EditorView, hoverTooltip, MatchDecorator, ViewPlugin } from "@codemirror/view"; +import { activeWorkspaceIdAtom } from "../../../../hooks/useActiveWorkspace"; +import { copyToClipboard } from "../../../../lib/copy"; +import { createRequestAndNavigate } from "../../../../lib/createRequestAndNavigate"; +import { jotaiStore } from "../../../../lib/jotai"; const REGEX = /(https?:\/\/([-a-zA-Z0-9@:%._+*~#=]{1,256})+(\.[a-zA-Z0-9()]{1,6})?\b([-a-zA-Z0-9()@:%_+*.~#?&/={}[\]]*))/g; @@ -39,26 +39,26 @@ const tooltip = hoverTooltip( create() { const workspaceId = jotaiStore.get(activeWorkspaceIdAtom); const link = text.substring(found?.start - from, found?.end - from); - const dom = document.createElement('div'); + const dom = document.createElement("div"); - const $open = document.createElement('a'); - $open.textContent = 'Open in browser'; + const $open = document.createElement("a"); + $open.textContent = "Open in browser"; $open.href = link; - $open.target = '_blank'; - $open.rel = 'noopener noreferrer'; + $open.target = "_blank"; + $open.rel = "noopener noreferrer"; - const $copy = document.createElement('button'); - $copy.textContent = 'Copy to clipboard'; - $copy.addEventListener('click', () => { + const $copy = document.createElement("button"); + $copy.textContent = "Copy to clipboard"; + $copy.addEventListener("click", () => { copyToClipboard(link); }); - const $create = document.createElement('button'); - $create.textContent = 'Create new request'; - $create.addEventListener('click', async () => { + const $create = document.createElement("button"); + $create.textContent = "Create new request"; + $create.addEventListener("click", async () => { await createRequestAndNavigate({ - model: 'http_request', - workspaceId: workspaceId ?? 'n/a', + model: "http_request", + workspaceId: workspaceId ?? "n/a", url: link, }); }); @@ -94,12 +94,12 @@ const decorator = () => { const groupMatch = match[1]; if (groupMatch == null) { // Should never happen, but make TS happy - console.warn('Group match was empty', match); + console.warn("Group match was empty", match); return Decoration.replace({}); } return Decoration.mark({ - class: 'hyperlink-widget', + class: "hyperlink-widget", }); }, }); diff --git a/src-web/components/core/Editor/json-lint.ts b/src-web/components/core/Editor/json-lint.ts index cb859ad1..4353bd76 100644 --- a/src-web/components/core/Editor/json-lint.ts +++ b/src-web/components/core/Editor/json-lint.ts @@ -1,6 +1,6 @@ -import type { Diagnostic } from '@codemirror/lint'; -import type { EditorView } from '@codemirror/view'; -import { parse as jsonLintParse } from '@prantlf/jsonlint'; +import type { Diagnostic } from "@codemirror/lint"; +import type { EditorView } from "@codemirror/view"; +import { parse as jsonLintParse } from "@prantlf/jsonlint"; const TEMPLATE_SYNTAX_REGEX = /\$\{\[[\s\S]*?]}/g; @@ -15,14 +15,14 @@ export function jsonParseLinter(options?: JsonLintOptions) { const doc = view.state.doc.toString(); // We need lint to not break on stuff like {"foo:" ${[ ... ]}} so we'll replace all template // syntax with repeating `1` characters, so it's valid JSON and the position is still correct. - const escapedDoc = doc.replace(TEMPLATE_SYNTAX_REGEX, (m) => '1'.repeat(m.length)); + const escapedDoc = doc.replace(TEMPLATE_SYNTAX_REGEX, (m) => "1".repeat(m.length)); jsonLintParse(escapedDoc, { - mode: (options?.allowComments ?? true) ? 'cjson' : 'json', + mode: (options?.allowComments ?? true) ? "cjson" : "json", ignoreTrailingCommas: options?.allowTrailingCommas ?? false, }); // oxlint-disable-next-line no-explicit-any } catch (err: any) { - if (!('location' in err)) { + if (!("location" in err)) { return []; } @@ -33,7 +33,7 @@ export function jsonParseLinter(options?: JsonLintOptions) { { from: err.location.start.offset, to: err.location.start.offset, - severity: 'error', + severity: "error", message: err.message, }, ]; diff --git a/src-web/components/core/Editor/pairs/extension.ts b/src-web/components/core/Editor/pairs/extension.ts index cf4f20f7..823e9b30 100644 --- a/src-web/components/core/Editor/pairs/extension.ts +++ b/src-web/components/core/Editor/pairs/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './pairs'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./pairs"; const language = LRLanguage.define({ - name: 'pairs', + name: "pairs", parser, languageData: {}, }); diff --git a/src-web/components/core/Editor/pairs/highlight.ts b/src-web/components/core/Editor/pairs/highlight.ts index 13fb4b79..954deff9 100644 --- a/src-web/components/core/Editor/pairs/highlight.ts +++ b/src-web/components/core/Editor/pairs/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ Sep: t.bracket, diff --git a/src-web/components/core/Editor/pairs/pairs.ts b/src-web/components/core/Editor/pairs/pairs.ts index 968a6cd3..f5cc1403 100644 --- a/src-web/components/core/Editor/pairs/pairs.ts +++ b/src-web/components/core/Editor/pairs/pairs.ts @@ -1,12 +1,12 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "zQQOPOOOVOQO'#CaQQOPOOO[OSO,58{OOOO-E6_-E6_OaOQO1G.gOOOO7+$R7+$R", - stateData: 'f~OQPO~ORRO~OSTO~OVUO~O', - goto: ']UPPPPPVQQORSQ', - nodeNames: '⚠ pairs Key Sep Value', + stateData: "f~OQPO~ORRO~OSTO~OVUO~O", + goto: "]UPPPPPVQQORSQ", + nodeNames: "⚠ pairs Key Sep Value", maxTerm: 7, propSources: [highlight], skippedNodes: [0], @@ -17,13 +17,13 @@ export const parser = LRParser.deserialize({ topRules: { pairs: [0, 1] }, tokenPrec: 0, termNames: { - '0': '⚠', - '1': '@top', - '2': 'Key', - '3': 'Sep', - '4': 'Value', - '5': '(Key Sep Value "\\n")+', - '6': '␄', - '7': '"\\n"', + "0": "⚠", + "1": "@top", + "2": "Key", + "3": "Sep", + "4": "Value", + "5": '(Key Sep Value "\\n")+', + "6": "␄", + "7": '"\\n"', }, }); diff --git a/src-web/components/core/Editor/searchMatchCount.ts b/src-web/components/core/Editor/searchMatchCount.ts index 79b0937a..b2069191 100644 --- a/src-web/components/core/Editor/searchMatchCount.ts +++ b/src-web/components/core/Editor/searchMatchCount.ts @@ -1,6 +1,6 @@ -import { getSearchQuery, searchPanelOpen } from '@codemirror/search'; -import type { Extension } from '@codemirror/state'; -import { type EditorView, ViewPlugin, type ViewUpdate } from '@codemirror/view'; +import { getSearchQuery, searchPanelOpen } from "@codemirror/search"; +import type { Extension } from "@codemirror/state"; +import { type EditorView, ViewPlugin, type ViewUpdate } from "@codemirror/view"; /** * A CodeMirror extension that displays the total number of search matches @@ -41,7 +41,7 @@ export function searchMatchCount(): Extension { if (!query.search) { if (this.countEl) { - this.countEl.textContent = '0/0'; + this.countEl.textContent = "0/0"; } return; } @@ -64,7 +64,7 @@ export function searchMatchCount(): Extension { if (count > MAX_COUNT) { this.countEl.textContent = `${MAX_COUNT}+`; } else if (count === 0) { - this.countEl.textContent = '0/0'; + this.countEl.textContent = "0/0"; } else if (currentIndex > 0) { this.countEl.textContent = `${currentIndex}/${count}`; } else { @@ -75,7 +75,7 @@ export function searchMatchCount(): Extension { private ensureCountEl() { // Find the search panel in the editor DOM - const panel = this.view.dom.querySelector('.cm-search'); + const panel = this.view.dom.querySelector(".cm-search"); if (!panel) { this.countEl = null; return; @@ -85,11 +85,11 @@ export function searchMatchCount(): Extension { return; // Already attached } - this.countEl = document.createElement('span'); - this.countEl.className = 'cm-search-match-count'; + this.countEl = document.createElement("span"); + this.countEl.className = "cm-search-match-count"; // Reorder: insert prev button, then next button, then count after the search input - const searchInput = panel.querySelector('input'); + const searchInput = panel.querySelector("input"); const prevBtn = panel.querySelector('button[name="prev"]'); const nextBtn = panel.querySelector('button[name="next"]'); if (searchInput && searchInput.parentElement === panel) { diff --git a/src-web/components/core/Editor/singleLine.ts b/src-web/components/core/Editor/singleLine.ts index 7b040d7c..20c4b14b 100644 --- a/src-web/components/core/Editor/singleLine.ts +++ b/src-web/components/core/Editor/singleLine.ts @@ -1,5 +1,5 @@ -import type { Extension, TransactionSpec } from '@codemirror/state'; -import { EditorSelection, EditorState, Transaction } from '@codemirror/state'; +import type { Extension, TransactionSpec } from "@codemirror/state"; +import { EditorSelection, EditorState, Transaction } from "@codemirror/state"; /** * A CodeMirror extension that forces single-line input by stripping @@ -17,14 +17,14 @@ import { EditorSelection, EditorState, Transaction } from '@codemirror/state'; export function singleLineExtensions(): Extension { return EditorState.transactionFilter.of( (tr: Transaction): TransactionSpec | readonly TransactionSpec[] => { - if (!tr.isUserEvent('input') || tr.isUserEvent('input.type.compose')) return tr; + if (!tr.isUserEvent("input") || tr.isUserEvent("input.type.compose")) return tr; const changes: { from: number; to: number; insert: string }[] = []; tr.changes.iterChanges((_fromA, toA, fromB, _toB, inserted) => { - let insert = ''; + let insert = ""; for (const line of inserted.iterLines()) { - insert += line.replace(/\n/g, ''); + insert += line.replace(/\n/g, ""); } if (insert !== inserted.toString()) { diff --git a/src-web/components/core/Editor/text/extension.ts b/src-web/components/core/Editor/text/extension.ts index 8af165f4..a398f2ad 100644 --- a/src-web/components/core/Editor/text/extension.ts +++ b/src-web/components/core/Editor/text/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './text'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./text"; export const textLanguage = LRLanguage.define({ - name: 'text', + name: "text", parser, languageData: {}, }); diff --git a/src-web/components/core/Editor/text/text.ts b/src-web/components/core/Editor/text/text.ts index e2e986c0..b09b79c4 100644 --- a/src-web/components/core/Editor/text/text.ts +++ b/src-web/components/core/Editor/text/text.ts @@ -1,11 +1,11 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; +import { LRParser } from "@lezer/lr"; export const parser = LRParser.deserialize({ version: 14, - states: '[OQOPOOQOOOOO', - stateData: 'V~OQPO~O', - goto: 'QPP', - nodeNames: '⚠ Template Text', + states: "[OQOPOOQOOOOO", + stateData: "V~OQPO~O", + goto: "QPP", + nodeNames: "⚠ Template Text", maxTerm: 3, skippedNodes: [0], repeatNodeCount: 0, diff --git a/src-web/components/core/Editor/timeline/extension.ts b/src-web/components/core/Editor/timeline/extension.ts index 74d20b93..b1a00412 100644 --- a/src-web/components/core/Editor/timeline/extension.ts +++ b/src-web/components/core/Editor/timeline/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './timeline'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./timeline"; export const timelineLanguage = LRLanguage.define({ - name: 'timeline', + name: "timeline", parser, languageData: {}, }); diff --git a/src-web/components/core/Editor/timeline/highlight.ts b/src-web/components/core/Editor/timeline/highlight.ts index b872957b..6f8c9e9a 100644 --- a/src-web/components/core/Editor/timeline/highlight.ts +++ b/src-web/components/core/Editor/timeline/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ OutgoingText: t.propertyName, // > lines - primary color (matches timeline icons) diff --git a/src-web/components/core/Editor/timeline/timeline.terms.ts b/src-web/components/core/Editor/timeline/timeline.terms.ts index 1b3bff48..8f03d420 100644 --- a/src-web/components/core/Editor/timeline/timeline.terms.ts +++ b/src-web/components/core/Editor/timeline/timeline.terms.ts @@ -1,6 +1,5 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -export const - Timeline = 1, +export const Timeline = 1, OutgoingLine = 2, OutgoingText = 3, Newline = 4, @@ -9,4 +8,4 @@ export const InfoLine = 7, InfoText = 8, PlainLine = 9, - PlainText = 10 + PlainText = 10; diff --git a/src-web/components/core/Editor/timeline/timeline.ts b/src-web/components/core/Editor/timeline/timeline.ts index 235578d6..fee8020d 100644 --- a/src-web/components/core/Editor/timeline/timeline.ts +++ b/src-web/components/core/Editor/timeline/timeline.ts @@ -1,18 +1,21 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import {LRParser} from "@lezer/lr" -import {highlight} from "./highlight" +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, - states: "!pQQOPOOO`OPO'#C^OeOPO'#CaOjOPO'#CcOoOPO'#CeOOOO'#Ci'#CiOOOO'#Cg'#CgQQOPOOOOOO,58x,58xOOOO,58{,58{OOOO,58},58}OOOO,59P,59POOOO-E6e-E6e", + states: + "!pQQOPOOO`OPO'#C^OeOPO'#CaOjOPO'#CcOoOPO'#CeOOOO'#Ci'#CiOOOO'#Cg'#CgQQOPOOOOOO,58x,58xOOOO,58{,58{OOOO,58},58}OOOO,59P,59POOOO-E6e-E6e", stateData: "z~ORPOUQOWROYSO~OSWO~OSXO~OSYO~OSZO~ORUWYW~", goto: "m^PP_PP_P_P_PcPiTTOVQVOR[VTUOV", - nodeNames: "⚠ Timeline OutgoingLine OutgoingText Newline IncomingLine IncomingText InfoLine InfoText PlainLine PlainText", + nodeNames: + "⚠ Timeline OutgoingLine OutgoingText Newline IncomingLine IncomingText InfoLine InfoText PlainLine PlainText", maxTerm: 13, propSources: [highlight], skippedNodes: [0], repeatNodeCount: 1, - tokenData: "%h~RZOYtYZ!]Zztz{!b{!^t!^!_#d!_!`t!`!a$f!a;'St;'S;=`!V<%lOt~ySY~OYtZ;'St;'S;=`!V<%lOt~!YP;=`<%lt~!bOS~~!gUY~OYtZptpq!yq;'St;'S;=`!V<%lOt~#QSW~Y~OY!yZ;'S!y;'S;=`#^<%lO!y~#aP;=`<%l!y~#iUY~OYtZptpq#{q;'St;'S;=`!V<%lOt~$SSU~Y~OY#{Z;'S#{;'S;=`$`<%lO#{~$cP;=`<%l#{~$kUY~OYtZptpq$}q;'St;'S;=`!V<%lOt~%USR~Y~OY$}Z;'S$};'S;=`%b<%lO$}~%eP;=`<%l$}", + tokenData: + "%h~RZOYtYZ!]Zztz{!b{!^t!^!_#d!_!`t!`!a$f!a;'St;'S;=`!V<%lOt~ySY~OYtZ;'St;'S;=`!V<%lOt~!YP;=`<%lt~!bOS~~!gUY~OYtZptpq!yq;'St;'S;=`!V<%lOt~#QSW~Y~OY!yZ;'S!y;'S;=`#^<%lO!y~#aP;=`<%l!y~#iUY~OYtZptpq#{q;'St;'S;=`!V<%lOt~$SSU~Y~OY#{Z;'S#{;'S;=`$`<%lO#{~$cP;=`<%l#{~$kUY~OYtZptpq$}q;'St;'S;=`!V<%lOt~%USR~Y~OY$}Z;'S$};'S;=`%b<%lO$}~%eP;=`<%l$}", tokenizers: [0], - topRules: {"Timeline":[0,1]}, - tokenPrec: 36 -}) + topRules: { Timeline: [0, 1] }, + tokenPrec: 36, +}); diff --git a/src-web/components/core/Editor/twig/completion.ts b/src-web/components/core/Editor/twig/completion.ts index 5c9bdde2..39793223 100644 --- a/src-web/components/core/Editor/twig/completion.ts +++ b/src-web/components/core/Editor/twig/completion.ts @@ -1,20 +1,20 @@ -import type { Completion, CompletionContext } from '@codemirror/autocomplete'; -import { startCompletion } from '@codemirror/autocomplete'; -import type { TemplateFunction } from '@yaakapp-internal/plugins'; +import type { Completion, CompletionContext } from "@codemirror/autocomplete"; +import { startCompletion } from "@codemirror/autocomplete"; +import type { TemplateFunction } from "@yaakapp-internal/plugins"; -const openTag = '${[ '; -const closeTag = ' ]}'; +const openTag = "${[ "; +const closeTag = " ]}"; export type TwigCompletionOptionVariable = { - type: 'variable'; + type: "variable"; }; export type TwigCompletionOptionNamespace = { - type: 'namespace'; + type: "namespace"; }; export type TwigCompletionOptionFunction = TemplateFunction & { - type: 'function'; + type: "function"; }; export type TwigCompletionOption = ( @@ -50,17 +50,17 @@ export function twigCompletion({ options }: TwigCompletionConfig) { const completions: Completion[] = options .flatMap((o): Completion[] => { - const matchSegments = toMatch.text.replace(/^\$/, '').split('.'); - const optionSegments = o.name.split('.'); + const matchSegments = toMatch.text.replace(/^\$/, "").split("."); + const optionSegments = o.name.split("."); // If not on the last segment, only complete the namespace if (matchSegments.length < optionSegments.length) { - const prefix = optionSegments.slice(0, matchSegments.length).join('.'); + const prefix = optionSegments.slice(0, matchSegments.length).join("."); return [ { label: `${prefix}.*`, - type: 'namespace', - detail: 'namespace', + type: "namespace", + detail: "namespace", apply: (view, _completion, from, to) => { const insert = `${prefix}.`; view.dispatch({ @@ -75,13 +75,13 @@ export function twigCompletion({ options }: TwigCompletionConfig) { } // If on the last segment, wrap the entire tag - const inner = o.type === 'function' ? `${o.name}()` : o.name; + const inner = o.type === "function" ? `${o.name}()` : o.name; return [ { label: o.name, info: o.description, detail: o.type, - type: o.type === 'variable' ? 'variable' : 'function', + type: o.type === "variable" ? "variable" : "function", apply: (view, _completion, from, to) => { const insert = openTag + inner + closeTag; view.dispatch({ @@ -94,7 +94,7 @@ export function twigCompletion({ options }: TwigCompletionConfig) { }) .filter((v) => v != null); - const uniqueCompletions = uniqueBy(completions, 'label'); + const uniqueCompletions = uniqueBy(completions, "label"); const sortedCompletions = uniqueCompletions.sort((a, b) => { const boostDiff = defaultBoost(b) - defaultBoost(a); if (boostDiff !== 0) return boostDiff; @@ -119,9 +119,9 @@ export function uniqueBy(arr: T[], key: K): T[] { } export function defaultBoost(o: Completion) { - if (o.type === 'variable') return 4; - if (o.type === 'constant') return 3; - if (o.type === 'function') return 2; - if (o.type === 'namespace') return 1; + if (o.type === "variable") return 4; + if (o.type === "constant") return 3; + if (o.type === "function") return 2; + if (o.type === "namespace") return 1; return 0; } diff --git a/src-web/components/core/Editor/twig/extension.ts b/src-web/components/core/Editor/twig/extension.ts index 45aeaf43..bbd4c8cf 100644 --- a/src-web/components/core/Editor/twig/extension.ts +++ b/src-web/components/core/Editor/twig/extension.ts @@ -1,15 +1,15 @@ -import type { LanguageSupport } from '@codemirror/language'; -import { LRLanguage } from '@codemirror/language'; -import type { Extension } from '@codemirror/state'; -import { parseMixed } from '@lezer/common'; -import type { WrappedEnvironmentVariable } from '../../../../hooks/useEnvironmentVariables'; -import type { GenericCompletionConfig } from '../genericCompletion'; -import { genericCompletion } from '../genericCompletion'; -import { textLanguage } from '../text/extension'; -import type { TwigCompletionOption } from './completion'; -import { twigCompletion } from './completion'; -import { templateTagsPlugin } from './templateTags'; -import { parser as twigParser } from './twig'; +import type { LanguageSupport } from "@codemirror/language"; +import { LRLanguage } from "@codemirror/language"; +import type { Extension } from "@codemirror/state"; +import { parseMixed } from "@lezer/common"; +import type { WrappedEnvironmentVariable } from "../../../../hooks/useEnvironmentVariables"; +import type { GenericCompletionConfig } from "../genericCompletion"; +import { genericCompletion } from "../genericCompletion"; +import { textLanguage } from "../text/extension"; +import type { TwigCompletionOption } from "./completion"; +import { twigCompletion } from "./completion"; +import { templateTagsPlugin } from "./templateTags"; +import { parser as twigParser } from "./twig"; export function twig({ base, @@ -35,7 +35,7 @@ export function twig({ environmentVariables.map((v) => ({ name: v.variable.name, value: v.variable.value, - type: 'variable', + type: "variable", label: v.variable.name, description: `Inherited from ${v.source}`, onClick: (rawTag: string, startPos: number) => onClickVariable(v, rawTag, startPos), @@ -74,12 +74,12 @@ function mixLanguage(base: LanguageSupport): LRLanguage { return { parser: base.language.parser, - overlay: (node) => node.type.name === 'Text', + overlay: (node) => node.type.name === "Text", }; }), }); - const language = LRLanguage.define({ name: 'twig', parser }); + const language = LRLanguage.define({ name: "twig", parser }); mixedLanguagesCache[base.language.name] = language; return language; } diff --git a/src-web/components/core/Editor/twig/highlight.ts b/src-web/components/core/Editor/twig/highlight.ts index b91d6ed9..d3a614ec 100644 --- a/src-web/components/core/Editor/twig/highlight.ts +++ b/src-web/components/core/Editor/twig/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ TagOpen: t.bracket, diff --git a/src-web/components/core/Editor/twig/pathParameters.ts b/src-web/components/core/Editor/twig/pathParameters.ts index e32f5f6d..dc226adc 100644 --- a/src-web/components/core/Editor/twig/pathParameters.ts +++ b/src-web/components/core/Editor/twig/pathParameters.ts @@ -1,7 +1,7 @@ -import { syntaxTree } from '@codemirror/language'; -import type { Range } from '@codemirror/state'; -import type { DecorationSet, ViewUpdate } from '@codemirror/view'; -import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view'; +import { syntaxTree } from "@codemirror/language"; +import type { Range } from "@codemirror/state"; +import type { DecorationSet, ViewUpdate } from "@codemirror/view"; +import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view"; class PathPlaceholderWidget extends WidgetType { readonly #clickListenerCallback: () => void; @@ -22,15 +22,15 @@ class PathPlaceholderWidget extends WidgetType { } toDOM() { - const elt = document.createElement('span'); - elt.className = 'x-theme-templateTag x-theme-templateTag--secondary template-tag'; + const elt = document.createElement("span"); + elt.className = "x-theme-templateTag x-theme-templateTag--secondary template-tag"; elt.textContent = this.rawText; - elt.addEventListener('click', this.#clickListenerCallback); + elt.addEventListener("click", this.#clickListenerCallback); return elt; } destroy(dom: HTMLElement) { - dom.removeEventListener('click', this.#clickListenerCallback); + dom.removeEventListener("click", this.#clickListenerCallback); super.destroy(dom); } @@ -50,14 +50,14 @@ function pathParameters( from, to, enter(node) { - if (node.name === 'Text') { + if (node.name === "Text") { // Find the `url` node and then jump into it to find the placeholders for (let i = node.from; i < node.to; i++) { const innerTree = syntaxTree(view.state).resolveInner(i); - if (innerTree.node.name === 'url') { + if (innerTree.node.name === "url") { innerTree.toTree().iterate({ enter(node) { - if (node.name !== 'Placeholder') return; + if (node.name !== "Placeholder") return; const globalFrom = innerTree.node.from + node.from; const globalTo = innerTree.node.from + node.to; const rawText = view.state.doc.sliceString(globalFrom, globalTo); diff --git a/src-web/components/core/Editor/twig/templateTags.ts b/src-web/components/core/Editor/twig/templateTags.ts index 41701fad..4ca45113 100644 --- a/src-web/components/core/Editor/twig/templateTags.ts +++ b/src-web/components/core/Editor/twig/templateTags.ts @@ -1,13 +1,13 @@ -import { syntaxTree } from '@codemirror/language'; -import type { Range } from '@codemirror/state'; -import type { DecorationSet, ViewUpdate } from '@codemirror/view'; -import { Decoration, EditorView, ViewPlugin, WidgetType } from '@codemirror/view'; -import type { SyntaxNodeRef } from '@lezer/common'; -import { applyFormInputDefaults, validateTemplateFunctionArgs } from '@yaakapp-internal/lib'; -import type { FormInput, JsonPrimitive, TemplateFunction } from '@yaakapp-internal/plugins'; -import { parseTemplate } from '@yaakapp-internal/templates'; -import type { TwigCompletionOption } from './completion'; -import { collectArgumentValues } from './util'; +import { syntaxTree } from "@codemirror/language"; +import type { Range } from "@codemirror/state"; +import type { DecorationSet, ViewUpdate } from "@codemirror/view"; +import { Decoration, EditorView, ViewPlugin, WidgetType } from "@codemirror/view"; +import type { SyntaxNodeRef } from "@lezer/common"; +import { applyFormInputDefaults, validateTemplateFunctionArgs } from "@yaakapp-internal/lib"; +import type { FormInput, JsonPrimitive, TemplateFunction } from "@yaakapp-internal/plugins"; +import { parseTemplate } from "@yaakapp-internal/templates"; +import type { TwigCompletionOption } from "./completion"; +import { collectArgumentValues } from "./util"; class TemplateTagWidget extends WidgetType { readonly #clickListenerCallback: () => void; @@ -34,24 +34,24 @@ class TemplateTagWidget extends WidgetType { } toDOM() { - const elt = document.createElement('span'); + const elt = document.createElement("span"); elt.className = `x-theme-templateTag template-tag ${ this.option.invalid - ? 'x-theme-templateTag--danger' - : this.option.type === 'variable' - ? 'x-theme-templateTag--primary' - : 'x-theme-templateTag--info' + ? "x-theme-templateTag--danger" + : this.option.type === "variable" + ? "x-theme-templateTag--primary" + : "x-theme-templateTag--info" }`; - elt.title = this.option.invalid ? 'Not Found' : (this.option.value ?? ''); - elt.setAttribute('data-tag-type', this.option.type); - if (typeof this.option.label === 'string') elt.textContent = this.option.label; + elt.title = this.option.invalid ? "Not Found" : (this.option.value ?? ""); + elt.setAttribute("data-tag-type", this.option.type); + if (typeof this.option.label === "string") elt.textContent = this.option.label; else elt.appendChild(this.option.label); - elt.addEventListener('click', this.#clickListenerCallback); + elt.addEventListener("click", this.#clickListenerCallback); return elt; } destroy(dom: HTMLElement) { - dom.removeEventListener('click', this.#clickListenerCallback); + dom.removeEventListener("click", this.#clickListenerCallback); super.destroy(dom); } @@ -72,34 +72,34 @@ function templateTags( from, to, enter(node) { - if (node.name === 'Tag') { + if (node.name === "Tag") { // Don't decorate if the cursor is inside the match if (isSelectionInsideNode(view, node)) return; const rawTag = view.state.doc.sliceString(node.from, node.to); // TODO: Search `node.tree` instead of using Regex here - const inner = rawTag.replace(/^\$\{\[\s*/, '').replace(/\s*]}$/, ''); + const inner = rawTag.replace(/^\$\{\[\s*/, "").replace(/\s*]}$/, ""); let name = inner.match(/([\w.]+)[(]/)?.[1] ?? inner; - if (inner.includes('\n')) { + if (inner.includes("\n")) { return; } // The beta named the function `Response` but was changed in stable. // Keep this here for a while because there's no easy way to migrate - if (name === 'Response') { - name = 'response'; + if (name === "Response") { + name = "response"; } let option = options.find( - (o) => o.name === name || (o.type === 'function' && o.aliases?.includes(name)), + (o) => o.name === name || (o.type === "function" && o.aliases?.includes(name)), ); if (option == null) { const from = node.from; // Cache here so the reference doesn't change option = { - type: 'variable', + type: "variable", invalid: true, name: inner, value: null, @@ -110,7 +110,7 @@ function templateTags( }; } - if (option.type === 'function') { + if (option.type === "function") { const tokens = parseTemplate(rawTag); const rawValues = collectArgumentValues(tokens, option); const values = applyFormInputDefaults(option.args, rawValues); @@ -175,49 +175,49 @@ function makeFunctionLabel( ): HTMLElement | string { if (fn.args.length === 0) return fn.name; - const $outer = document.createElement('span'); - $outer.className = 'fn'; - const $bOpen = document.createElement('span'); - $bOpen.className = 'fn-bracket'; - $bOpen.textContent = '('; + const $outer = document.createElement("span"); + $outer.className = "fn"; + const $bOpen = document.createElement("span"); + $bOpen.className = "fn-bracket"; + $bOpen.textContent = "("; $outer.appendChild(document.createTextNode(fn.name)); $outer.appendChild($bOpen); - const $inner = document.createElement('span'); - $inner.className = 'fn-inner'; - $inner.title = ''; + const $inner = document.createElement("span"); + $inner.className = "fn-inner"; + $inner.title = ""; fn.previewArgs?.forEach((name: string, i: number, all: string[]) => { - const v = String(values[name] || ''); + const v = String(values[name] || ""); if (!v) return; if (all.length > 1) { - const $c = document.createElement('span'); - $c.className = 'fn-arg-name'; + const $c = document.createElement("span"); + $c.className = "fn-arg-name"; $c.textContent = i > 0 ? `, ${name}=` : `${name}=`; $inner.appendChild($c); } - const $v = document.createElement('span'); - $v.className = 'fn-arg-value'; - $v.textContent = v.includes(' ') ? `'${v}'` : v; + const $v = document.createElement("span"); + $v.className = "fn-arg-value"; + $v.textContent = v.includes(" ") ? `'${v}'` : v; $inner.appendChild($v); }); fn.args.forEach((a: FormInput, i: number) => { - if (!('name' in a)) return; + if (!("name" in a)) return; const v = values[a.name]; if (v == null) return; - if (i > 0) $inner.title += '\n'; + if (i > 0) $inner.title += "\n"; $inner.title += `${a.name} = ${JSON.stringify(v)}`; }); if ($inner.childNodes.length === 0) { - $inner.appendChild(document.createTextNode('…')); + $inner.appendChild(document.createTextNode("…")); } $outer.appendChild($inner); - const $bClose = document.createElement('span'); - $bClose.className = 'fn-bracket'; - $bClose.textContent = ')'; + const $bClose = document.createElement("span"); + $bClose.className = "fn-bracket"; + $bClose.textContent = ")"; $outer.appendChild($bClose); return $outer; diff --git a/src-web/components/core/Editor/twig/twig.test.ts b/src-web/components/core/Editor/twig/twig.test.ts index 016ed6e1..2ce95f01 100644 --- a/src-web/components/core/Editor/twig/twig.test.ts +++ b/src-web/components/core/Editor/twig/twig.test.ts @@ -1,14 +1,14 @@ /* oxlint-disable no-template-curly-in-string */ -import { describe, expect, test } from 'vite-plus/test'; -import { parser } from './twig'; +import { describe, expect, test } from "vite-plus/test"; +import { parser } from "./twig"; function getNodeNames(input: string): string[] { const tree = parser.parse(input); const nodes: string[] = []; const cursor = tree.cursor(); do { - if (cursor.name !== 'Template') { + if (cursor.name !== "Template") { nodes.push(cursor.name); } } while (cursor.next()); @@ -16,93 +16,93 @@ function getNodeNames(input: string): string[] { } function hasTag(input: string): boolean { - return getNodeNames(input).includes('Tag'); + return getNodeNames(input).includes("Tag"); } function hasError(input: string): boolean { - return getNodeNames(input).includes('⚠'); + return getNodeNames(input).includes("⚠"); } -describe('twig grammar', () => { - describe('${[var]} format (valid template tags)', () => { - test('parses simple variable as Tag', () => { - expect(hasTag('${[var]}')).toBe(true); - expect(hasError('${[var]}')).toBe(false); +describe("twig grammar", () => { + describe("${[var]} format (valid template tags)", () => { + test("parses simple variable as Tag", () => { + expect(hasTag("${[var]}")).toBe(true); + expect(hasError("${[var]}")).toBe(false); }); - test('parses variable with whitespace as Tag', () => { - expect(hasTag('${[ var ]}')).toBe(true); - expect(hasError('${[ var ]}')).toBe(false); + test("parses variable with whitespace as Tag", () => { + expect(hasTag("${[ var ]}")).toBe(true); + expect(hasError("${[ var ]}")).toBe(false); }); - test('parses embedded variable as Tag', () => { - expect(hasTag('hello ${[name]} world')).toBe(true); - expect(hasError('hello ${[name]} world')).toBe(false); + test("parses embedded variable as Tag", () => { + expect(hasTag("hello ${[name]} world")).toBe(true); + expect(hasError("hello ${[name]} world")).toBe(false); }); - test('parses function call as Tag', () => { - expect(hasTag('${[fn()]}')).toBe(true); - expect(hasError('${[fn()]}')).toBe(false); + test("parses function call as Tag", () => { + expect(hasTag("${[fn()]}")).toBe(true); + expect(hasError("${[fn()]}")).toBe(false); }); }); - describe('${var} format (should be plain text, not tags)', () => { - test('parses ${var} as plain Text without errors', () => { - expect(hasTag('${var}')).toBe(false); - expect(hasError('${var}')).toBe(false); + describe("${var} format (should be plain text, not tags)", () => { + test("parses ${var} as plain Text without errors", () => { + expect(hasTag("${var}")).toBe(false); + expect(hasError("${var}")).toBe(false); }); - test('parses embedded ${var} as plain Text', () => { - expect(hasTag('hello ${name} world')).toBe(false); - expect(hasError('hello ${name} world')).toBe(false); + test("parses embedded ${var} as plain Text", () => { + expect(hasTag("hello ${name} world")).toBe(false); + expect(hasError("hello ${name} world")).toBe(false); }); - test('parses JSON with ${var} as plain Text', () => { + test("parses JSON with ${var} as plain Text", () => { const json = '{"key": "${value}"}'; expect(hasTag(json)).toBe(false); expect(hasError(json)).toBe(false); }); - test('parses multiple ${var} as plain Text', () => { - expect(hasTag('${a} and ${b}')).toBe(false); - expect(hasError('${a} and ${b}')).toBe(false); + test("parses multiple ${var} as plain Text", () => { + expect(hasTag("${a} and ${b}")).toBe(false); + expect(hasError("${a} and ${b}")).toBe(false); }); }); - describe('mixed content', () => { - test('distinguishes ${var} from ${[var]} in same string', () => { - const input = '${plain} and ${[tag]}'; + describe("mixed content", () => { + test("distinguishes ${var} from ${[var]} in same string", () => { + const input = "${plain} and ${[tag]}"; expect(hasTag(input)).toBe(true); expect(hasError(input)).toBe(false); }); - test('parses JSON with ${[var]} as having Tag', () => { + test("parses JSON with ${[var]} as having Tag", () => { const json = '{"key": "${[value]}"}'; expect(hasTag(json)).toBe(true); expect(hasError(json)).toBe(false); }); }); - describe('edge cases', () => { - test('handles $ at end of string', () => { - expect(hasError('hello$')).toBe(false); - expect(hasTag('hello$')).toBe(false); + describe("edge cases", () => { + test("handles $ at end of string", () => { + expect(hasError("hello$")).toBe(false); + expect(hasTag("hello$")).toBe(false); }); - test('handles ${ at end of string without crash', () => { + test("handles ${ at end of string without crash", () => { // Incomplete syntax may produce errors, but should not crash - expect(() => parser.parse('hello${')).not.toThrow(); + expect(() => parser.parse("hello${")).not.toThrow(); }); - test('handles ${[ without closing without crash', () => { + test("handles ${[ without closing without crash", () => { // Unclosed tag may produce partial match, but should not crash - expect(() => parser.parse('${[unclosed')).not.toThrow(); + expect(() => parser.parse("${[unclosed")).not.toThrow(); }); - test('handles empty ${[]}', () => { + test("handles empty ${[]}", () => { // Empty tags may or may not be valid depending on grammar // Just ensure no crash - expect(() => parser.parse('${[]}')).not.toThrow(); + expect(() => parser.parse("${[]}")).not.toThrow(); }); }); }); diff --git a/src-web/components/core/Editor/twig/twig.ts b/src-web/components/core/Editor/twig/twig.ts index e124c988..78af862c 100644 --- a/src-web/components/core/Editor/twig/twig.ts +++ b/src-web/components/core/Editor/twig/twig.ts @@ -1,20 +1,20 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LocalTokenGroup, LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LocalTokenGroup, LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "!^QQOPOOOOOO'#C_'#C_OYOQO'#C^OOOO'#Cc'#CcQQOPOOOOOO'#Cd'#CdO_OQO,58xOOOO-E6a-E6aOOOO-E6b-E6bOOOO1G.d1G.d", - stateData: 'g~OUROYPO~OSTO~OSTOTXO~O', - goto: 'nXPPY^PPPbhTROSTQOSQSORVSQUQRWU', - nodeNames: '⚠ Template Tag TagOpen TagContent TagClose Text', + stateData: "g~OUROYPO~OSTO~OSTOTXO~O", + goto: "nXPPY^PPPbhTROSTQOSQSORVSQUQRWU", + nodeNames: "⚠ Template Tag TagOpen TagContent TagClose Text", maxTerm: 10, propSources: [highlight], skippedNodes: [0], repeatNodeCount: 2, tokenData: "#{~RTOtbtu!zu;'Sb;'S;=`!o<%lOb~gTU~Otbtuvu;'Sb;'S;=`!o<%lOb~yVO#ob#o#p!`#p;'Sb;'S;=`!o<%l~b~Ob~~!u~!cSO!}b#O;'Sb;'S;=`!o<%lOb~!rP;=`<%lb~!zOU~~!}VO#ob#o#p#d#p;'Sb;'S;=`!o<%l~b~Ob~~!u~#gTO!}b!}#O#v#O;'Sb;'S;=`!o<%lOb~#{OY~", - tokenizers: [1, new LocalTokenGroup('b~RP#P#QU~XP#q#r[~aOT~~', 17, 4)], + tokenizers: [1, new LocalTokenGroup("b~RP#P#QU~XP#q#r[~aOT~~", 17, 4)], topRules: { Template: [0, 1] }, tokenPrec: 0, }); diff --git a/src-web/components/core/Editor/twig/util.ts b/src-web/components/core/Editor/twig/util.ts index 37d6efd1..330ea851 100644 --- a/src-web/components/core/Editor/twig/util.ts +++ b/src-web/components/core/Editor/twig/util.ts @@ -1,5 +1,5 @@ -import type { FormInput, TemplateFunction } from '@yaakapp-internal/plugins'; -import type { Tokens } from '@yaakapp-internal/templates'; +import type { FormInput, TemplateFunction } from "@yaakapp-internal/plugins"; +import type { Tokens } from "@yaakapp-internal/templates"; /** * Process the initial tokens from the template and merge those with the default values pulled from @@ -8,21 +8,21 @@ import type { Tokens } from '@yaakapp-internal/templates'; export function collectArgumentValues(initialTokens: Tokens, templateFunction: TemplateFunction) { const initial: Record = {}; const initialArgs = - initialTokens.tokens[0]?.type === 'tag' && initialTokens.tokens[0]?.val.type === 'fn' + initialTokens.tokens[0]?.type === "tag" && initialTokens.tokens[0]?.val.type === "fn" ? initialTokens.tokens[0]?.val.args : []; const processArg = (arg: FormInput) => { - if ('inputs' in arg && arg.inputs) { + if ("inputs" in arg && arg.inputs) { arg.inputs.forEach(processArg); } - if (!('name' in arg)) return; + if (!("name" in arg)) return; const initialArg = initialArgs.find((a) => a.name === arg.name); const initialArgValue = - initialArg?.value.type === 'str' + initialArg?.value.type === "str" ? initialArg?.value.text - : initialArg?.value.type === 'bool' + : initialArg?.value.type === "bool" ? initialArg.value.value : undefined; const value = initialArgValue ?? arg.defaultValue; diff --git a/src-web/components/core/Editor/url/completion.ts b/src-web/components/core/Editor/url/completion.ts index 40acefa3..0b6612e1 100644 --- a/src-web/components/core/Editor/url/completion.ts +++ b/src-web/components/core/Editor/url/completion.ts @@ -1,9 +1,9 @@ -import { genericCompletion } from '../genericCompletion'; +import { genericCompletion } from "../genericCompletion"; export const completions = genericCompletion({ options: [ - { label: 'http://', type: 'constant' }, - { label: 'https://', type: 'constant' }, + { label: "http://", type: "constant" }, + { label: "https://", type: "constant" }, ], minMatch: 1, }); diff --git a/src-web/components/core/Editor/url/extension.ts b/src-web/components/core/Editor/url/extension.ts index a46fc1d8..e900b823 100644 --- a/src-web/components/core/Editor/url/extension.ts +++ b/src-web/components/core/Editor/url/extension.ts @@ -1,8 +1,8 @@ -import { LanguageSupport, LRLanguage } from '@codemirror/language'; -import { parser } from './url'; +import { LanguageSupport, LRLanguage } from "@codemirror/language"; +import { parser } from "./url"; const urlLanguage = LRLanguage.define({ - name: 'url', + name: "url", parser, languageData: {}, }); diff --git a/src-web/components/core/Editor/url/highlight.ts b/src-web/components/core/Editor/url/highlight.ts index 85467f4f..69c8ac9b 100644 --- a/src-web/components/core/Editor/url/highlight.ts +++ b/src-web/components/core/Editor/url/highlight.ts @@ -1,4 +1,4 @@ -import { styleTags, tags as t } from '@lezer/highlight'; +import { styleTags, tags as t } from "@lezer/highlight"; export const highlight = styleTags({ Protocol: t.comment, diff --git a/src-web/components/core/Editor/url/url.ts b/src-web/components/core/Editor/url/url.ts index ca17436c..ad421be6 100644 --- a/src-web/components/core/Editor/url/url.ts +++ b/src-web/components/core/Editor/url/url.ts @@ -1,13 +1,13 @@ // This file was generated by lezer-generator. You probably shouldn't edit it. -import { LRParser } from '@lezer/lr'; -import { highlight } from './highlight'; +import { LRParser } from "@lezer/lr"; +import { highlight } from "./highlight"; export const parser = LRParser.deserialize({ version: 14, states: "!|OQOPOOQYOPOOOTOPOOObOQO'#CdOjOPO'#C`OuOSO'#CcQOOOOOQ]OPOOOOOO,59O,59OOOOO-E6b-E6bOzOPO,58}O!SOSO'#CeO!XOPO1G.iOOOO,59P,59POOOO-E6c-E6c", - stateData: '!g~OQQORPO~OZRO[TO~OTWOUWO~OZROYSX[SX~O]YO~O^ZOYVa~O]]O~O^ZOYVi~OQRTUT~', - goto: 'nYPPPPZPP^bhRVPTUPVQSPRXSQ[YR^[', - nodeNames: '⚠ url Protocol Host Path Placeholder PathSegment Query', + stateData: "!g~OQQORPO~OZRO[TO~OTWOUWO~OZROYSX[SX~O]YO~O^ZOYVa~O]]O~O^ZOYVi~OQRTUT~", + goto: "nYPPPPZPP^bhRVPTUPVQSPRXSQ[YR^[", + nodeNames: "⚠ url Protocol Host Path Placeholder PathSegment Query", maxTerm: 14, propSources: [highlight], skippedNodes: [0], diff --git a/src-web/components/core/EventViewer.tsx b/src-web/components/core/EventViewer.tsx index 41cac8e0..dc8e677c 100644 --- a/src-web/components/core/EventViewer.tsx +++ b/src-web/components/core/EventViewer.tsx @@ -1,17 +1,17 @@ -import type { Virtualizer } from '@tanstack/react-virtual'; -import { format } from 'date-fns'; -import type { ReactNode } from 'react'; -import { useCallback, useMemo, useRef, useState } from 'react'; -import { useEventViewerKeyboard } from '../../hooks/useEventViewerKeyboard'; -import { CopyIconButton } from '../CopyIconButton'; -import { AutoScroller } from './AutoScroller'; -import { Banner } from './Banner'; -import { Button } from './Button'; -import { Separator } from './Separator'; -import { SplitLayout } from './SplitLayout'; -import { HStack } from './Stacks'; -import { IconButton } from './IconButton'; -import classNames from 'classnames'; +import type { Virtualizer } from "@tanstack/react-virtual"; +import { format } from "date-fns"; +import type { ReactNode } from "react"; +import { useCallback, useMemo, useRef, useState } from "react"; +import { useEventViewerKeyboard } from "../../hooks/useEventViewerKeyboard"; +import { CopyIconButton } from "../CopyIconButton"; +import { AutoScroller } from "./AutoScroller"; +import { Banner } from "./Banner"; +import { Button } from "./Button"; +import { Separator } from "./Separator"; +import { SplitLayout } from "./SplitLayout"; +import { HStack } from "./Stacks"; +import { IconButton } from "./IconButton"; +import classNames from "classnames"; interface EventViewerProps { /** Array of events to display */ @@ -70,8 +70,8 @@ export function EventViewer({ defaultRatio = 0.4, enableKeyboardNav = true, isLoading = false, - loadingMessage = 'Loading events...', - emptyMessage = 'No events recorded', + loadingMessage = "Loading events...", + emptyMessage = "No events recorded", onActiveIndexChange, }: EventViewerProps) { const [activeIndex, setActiveIndexInternal] = useState(null); @@ -82,7 +82,7 @@ export function EventViewer({ (indexOrUpdater: number | null | ((prev: number | null) => number | null)) => { setActiveIndexInternal((prev) => { const newIndex = - typeof indexOrUpdater === 'function' ? indexOrUpdater(prev) : indexOrUpdater; + typeof indexOrUpdater === "function" ? indexOrUpdater(prev) : indexOrUpdater; onActiveIndexChange?.(newIndex); return newIndex; }); @@ -129,7 +129,7 @@ export function EventViewer({ setIsPanelOpen(true); // Scroll to ensure selected item is visible after panel opens requestAnimationFrame(() => { - virtualizerRef.current?.scrollToIndex(index, { align: 'auto' }); + virtualizerRef.current?.scrollToIndex(index, { align: "auto" }); }); }, [setActiveIndex], @@ -189,7 +189,11 @@ export function EventViewer({
- {renderDetail({ event: activeEvent, index: activeIndex ?? 0, onClose: handleClose })} + {renderDetail({ + event: activeEvent, + index: activeIndex ?? 0, + onClose: handleClose, + })}
) @@ -228,7 +232,7 @@ export function EventDetailHeader({ copyText, onClose, }: EventDetailHeaderProps) { - const formattedTime = timestamp ? format(new Date(`${timestamp}Z`), 'HH:mm:ss.SSS') : null; + const formattedTime = timestamp ? format(new Date(`${timestamp}Z`), "HH:mm:ss.SSS") : null; return (
@@ -249,8 +253,21 @@ export function EventDetailHeader({ {formattedTime && ( {formattedTime} )} -
0 && "border-l border-l-surface-highlight ml-2 pl-3")}> - +
0 && "border-l border-l-surface-highlight ml-2 pl-3"), + )} + > +
diff --git a/src-web/components/core/EventViewerRow.tsx b/src-web/components/core/EventViewerRow.tsx index d1a40cc3..19a71067 100644 --- a/src-web/components/core/EventViewerRow.tsx +++ b/src-web/components/core/EventViewerRow.tsx @@ -1,6 +1,6 @@ -import classNames from 'classnames'; -import { format } from 'date-fns'; -import type { ReactNode } from 'react'; +import classNames from "classnames"; +import { format } from "date-fns"; +import type { ReactNode } from "react"; interface EventViewerRowProps { isActive: boolean; @@ -23,15 +23,15 @@ export function EventViewerRow({ type="button" onClick={onClick} className={classNames( - 'w-full grid grid-cols-[auto_minmax(0,1fr)_auto] gap-2 items-center text-left', - 'px-1.5 h-xs font-mono text-editor cursor-default group focus:outline-none focus:text-text rounded', - isActive && 'bg-surface-active !text-text', - 'text-text-subtle hover:text', + "w-full grid grid-cols-[auto_minmax(0,1fr)_auto] gap-2 items-center text-left", + "px-1.5 h-xs font-mono text-editor cursor-default group focus:outline-none focus:text-text rounded", + isActive && "bg-surface-active !text-text", + "text-text-subtle hover:text", )} > {icon}
{content}
- {timestamp &&
{format(`${timestamp}Z`, 'HH:mm:ss.SSS')}
} + {timestamp &&
{format(`${timestamp}Z`, "HH:mm:ss.SSS")}
}
); diff --git a/src-web/components/core/FormattedError.tsx b/src-web/components/core/FormattedError.tsx index b7889467..72fbb92f 100644 --- a/src-web/components/core/FormattedError.tsx +++ b/src-web/components/core/FormattedError.tsx @@ -1,5 +1,5 @@ -import classNames from 'classnames'; -import type { ReactNode } from 'react'; +import classNames from "classnames"; +import type { ReactNode } from "react"; interface Props { children: ReactNode; @@ -11,10 +11,10 @@ export function FormattedError({ children, className }: Props) {
       {children}
diff --git a/src-web/components/core/Heading.tsx b/src-web/components/core/Heading.tsx
index afa6ed27..bb68cd96 100644
--- a/src-web/components/core/Heading.tsx
+++ b/src-web/components/core/Heading.tsx
@@ -1,20 +1,20 @@
-import classNames from 'classnames';
-import type { HTMLAttributes } from 'react';
+import classNames from "classnames";
+import type { HTMLAttributes } from "react";
 
 interface Props extends HTMLAttributes {
   level?: 1 | 2 | 3;
 }
 
 export function Heading({ className, level = 1, ...props }: Props) {
-  const Component = level === 1 ? 'h1' : level === 2 ? 'h2' : 'h3';
+  const Component = level === 1 ? "h1" : level === 2 ? "h2" : "h3";
   return (
     
diff --git a/src-web/components/core/Hotkey.tsx b/src-web/components/core/Hotkey.tsx
index 8d020e21..652ed012 100644
--- a/src-web/components/core/Hotkey.tsx
+++ b/src-web/components/core/Hotkey.tsx
@@ -1,12 +1,12 @@
-import classNames from 'classnames';
-import type { HotkeyAction } from '../../hooks/useHotKey';
-import { useFormattedHotkey } from '../../hooks/useHotKey';
-import { HStack } from './Stacks';
+import classNames from "classnames";
+import type { HotkeyAction } from "../../hooks/useHotKey";
+import { useFormattedHotkey } from "../../hooks/useHotKey";
+import { HStack } from "./Stacks";
 
 interface Props {
   action: HotkeyAction | null;
   className?: string;
-  variant?: 'text' | 'with-bg';
+  variant?: "text" | "with-bg";
 }
 
 export function Hotkey({ action, className, variant }: Props) {
@@ -21,7 +21,7 @@ export function Hotkey({ action, className, variant }: Props) {
 interface HotkeyRawProps {
   labelParts: string[];
   className?: string;
-  variant?: 'text' | 'with-bg';
+  variant?: "text" | "with-bg";
 }
 
 export function HotkeyRaw({ labelParts, className, variant }: HotkeyRawProps) {
@@ -29,9 +29,9 @@ export function HotkeyRaw({ labelParts, className, variant }: HotkeyRawProps) {
     
       {labelParts.map((char, index) => (
diff --git a/src-web/components/core/HotkeyLabel.tsx b/src-web/components/core/HotkeyLabel.tsx
index a4a6fb84..ab2e7360 100644
--- a/src-web/components/core/HotkeyLabel.tsx
+++ b/src-web/components/core/HotkeyLabel.tsx
@@ -1,6 +1,6 @@
-import classNames from 'classnames';
-import type { HotkeyAction } from '../../hooks/useHotKey';
-import { useHotkeyLabel } from '../../hooks/useHotKey';
+import classNames from "classnames";
+import type { HotkeyAction } from "../../hooks/useHotKey";
+import { useHotkeyLabel } from "../../hooks/useHotKey";
 
 interface Props {
   action: HotkeyAction;
@@ -10,6 +10,6 @@ interface Props {
 export function HotkeyLabel({ action, className }: Props) {
   const label = useHotkeyLabel(action);
   return (
-    {label}
+    {label}
   );
 }
diff --git a/src-web/components/core/HotkeyList.tsx b/src-web/components/core/HotkeyList.tsx
index 06719d27..202dd059 100644
--- a/src-web/components/core/HotkeyList.tsx
+++ b/src-web/components/core/HotkeyList.tsx
@@ -1,9 +1,9 @@
-import classNames from 'classnames';
-import type { ReactNode } from 'react';
-import { Fragment } from 'react';
-import type { HotkeyAction } from '../../hooks/useHotKey';
-import { Hotkey } from './Hotkey';
-import { HotkeyLabel } from './HotkeyLabel';
+import classNames from "classnames";
+import type { ReactNode } from "react";
+import { Fragment } from "react";
+import type { HotkeyAction } from "../../hooks/useHotKey";
+import { Hotkey } from "./Hotkey";
+import { HotkeyLabel } from "./HotkeyLabel";
 
 interface Props {
   hotkeys: HotkeyAction[];
@@ -13,7 +13,7 @@ interface Props {
 
 export const HotkeyList = ({ hotkeys, bottomSlot, className }: Props) => {
   return (
-    
+
{hotkeys.map((hotkey) => ( diff --git a/src-web/components/core/HttpMethodTag.tsx b/src-web/components/core/HttpMethodTag.tsx index dfed18a5..057971e2 100644 --- a/src-web/components/core/HttpMethodTag.tsx +++ b/src-web/components/core/HttpMethodTag.tsx @@ -1,8 +1,8 @@ -import type { GrpcRequest, HttpRequest, WebsocketRequest } from '@yaakapp-internal/models'; -import { settingsAtom } from '@yaakapp-internal/models'; -import classNames from 'classnames'; -import { useAtomValue } from 'jotai'; -import { memo } from 'react'; +import type { GrpcRequest, HttpRequest, WebsocketRequest } from "@yaakapp-internal/models"; +import { settingsAtom } from "@yaakapp-internal/models"; +import classNames from "classnames"; +import { useAtomValue } from "jotai"; +import { memo } from "react"; interface Props { request: HttpRequest | GrpcRequest | WebsocketRequest; @@ -12,27 +12,32 @@ interface Props { } const methodNames: Record = { - get: 'GET', - put: 'PUT', - post: 'POST', - patch: 'PTCH', - delete: 'DELE', - options: 'OPTN', - head: 'HEAD', - query: 'QURY', - graphql: 'GQL', - grpc: 'GRPC', - websocket: 'WS', + get: "GET", + put: "PUT", + post: "POST", + patch: "PTCH", + delete: "DELE", + options: "OPTN", + head: "HEAD", + query: "QURY", + graphql: "GQL", + grpc: "GRPC", + websocket: "WS", }; -export const HttpMethodTag = memo(function HttpMethodTag({ request, className, short, noAlias }: Props) { +export const HttpMethodTag = memo(function HttpMethodTag({ + request, + className, + short, + noAlias, +}: Props) { const method = - request.model === 'http_request' && (request.bodyType === 'graphql' && !noAlias) - ? 'graphql' - : request.model === 'grpc_request' - ? 'grpc' - : request.model === 'websocket_request' - ? 'websocket' + request.model === "http_request" && request.bodyType === "graphql" && !noAlias + ? "graphql" + : request.model === "grpc_request" + ? "grpc" + : request.model === "websocket_request" + ? "websocket" : request.method; return ; @@ -52,7 +57,7 @@ export function HttpMethodTagRaw({ let label = method.toUpperCase(); if (short) { label = methodNames[method.toLowerCase()] ?? method.slice(0, 4); - label = label.padStart(4, ' '); + label = label.padStart(4, " "); } const m = method.toUpperCase(); @@ -64,20 +69,20 @@ export function HttpMethodTagRaw({ {label} diff --git a/src-web/components/core/HttpResponseDurationTag.tsx b/src-web/components/core/HttpResponseDurationTag.tsx index 21bae26c..6c7c2ad2 100644 --- a/src-web/components/core/HttpResponseDurationTag.tsx +++ b/src-web/components/core/HttpResponseDurationTag.tsx @@ -1,5 +1,5 @@ -import type { HttpResponse } from '@yaakapp-internal/models'; -import { useEffect, useRef, useState } from 'react'; +import type { HttpResponse } from "@yaakapp-internal/models"; +import { useEffect, useRef, useState } from "react"; interface Props { response: HttpResponse; @@ -12,17 +12,17 @@ export function HttpResponseDurationTag({ response }: Props) { // Calculate the duration of the response for use when the response hasn't finished yet useEffect(() => { clearInterval(timeout.current); - if (response.state === 'closed') return; + if (response.state === "closed") return; timeout.current = setInterval(() => { setFallbackElapsed(Date.now() - new Date(`${response.createdAt}Z`).getTime()); }, 100); return () => clearInterval(timeout.current); }, [response.createdAt, response.state]); - const dnsValue = response.elapsedDns > 0 ? formatMillis(response.elapsedDns) : '--'; + const dnsValue = response.elapsedDns > 0 ? formatMillis(response.elapsedDns) : "--"; const title = `DNS: ${dnsValue}\nHEADER: ${formatMillis(response.elapsedHeaders)}\nTOTAL: ${formatMillis(response.elapsed)}`; - const elapsed = response.state === 'closed' ? response.elapsed : fallbackElapsed; + const elapsed = response.state === "closed" ? response.elapsed : fallbackElapsed; return ( diff --git a/src-web/components/core/HttpStatusTag.tsx b/src-web/components/core/HttpStatusTag.tsx index 7a56752a..a705f520 100644 --- a/src-web/components/core/HttpStatusTag.tsx +++ b/src-web/components/core/HttpStatusTag.tsx @@ -1,5 +1,5 @@ -import type { HttpResponse, HttpResponseState } from '@yaakapp-internal/models'; -import classNames from 'classnames'; +import type { HttpResponse, HttpResponseState } from "@yaakapp-internal/models"; +import classNames from "classnames"; interface Props { response: HttpResponse; @@ -20,35 +20,35 @@ export function HttpStatusTagRaw({ showReason, statusReason, short, -}: Omit & { +}: Omit & { status: number | string; state?: HttpResponseState; statusReason?: string | null; }) { let colorClass: string; let label = `${status}`; - const statusN = typeof status === 'number' ? status : parseInt(status, 10); + const statusN = typeof status === "number" ? status : parseInt(status, 10); - if (state === 'initialized') { - label = short ? 'CONN' : 'CONNECTING'; - colorClass = 'text-text-subtle'; + if (state === "initialized") { + label = short ? "CONN" : "CONNECTING"; + colorClass = "text-text-subtle"; } else if (statusN < 100) { - label = short ? 'ERR' : 'ERROR'; - colorClass = 'text-danger'; + label = short ? "ERR" : "ERROR"; + colorClass = "text-danger"; } else if (statusN < 200) { - colorClass = 'text-info'; + colorClass = "text-info"; } else if (statusN < 300) { - colorClass = 'text-success'; + colorClass = "text-success"; } else if (statusN < 400) { - colorClass = 'text-primary'; + colorClass = "text-primary"; } else if (statusN < 500) { - colorClass = 'text-warning'; + colorClass = "text-warning"; } else { - colorClass = 'text-danger'; + colorClass = "text-danger"; } return ( - + {label} {showReason && statusReason} ); diff --git a/src-web/components/core/Icon.tsx b/src-web/components/core/Icon.tsx index 61c1ca32..e2a11d03 100644 --- a/src-web/components/core/Icon.tsx +++ b/src-web/components/core/Icon.tsx @@ -1,5 +1,5 @@ -import type { Color } from '@yaakapp-internal/plugins'; -import classNames from 'classnames'; +import type { Color } from "@yaakapp-internal/plugins"; +import classNames from "classnames"; import { AlarmClockIcon, AlertTriangleIcon, @@ -134,9 +134,9 @@ import { WifiIcon, WrenchIcon, XIcon, -} from 'lucide-react'; -import type { CSSProperties, HTMLAttributes } from 'react'; -import { memo } from 'react'; +} from "lucide-react"; +import type { CSSProperties, HTMLAttributes } from "react"; +import { memo } from "react"; const icons = { alarm_clock: AlarmClockIcon, @@ -281,17 +281,17 @@ export interface IconProps { icon: keyof typeof icons; className?: string; style?: CSSProperties; - size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + size?: "2xs" | "xs" | "sm" | "md" | "lg" | "xl"; spin?: boolean; title?: string; - color?: Color | 'custom' | 'default'; + color?: Color | "custom" | "default"; } export const Icon = memo(function Icon({ icon, - color = 'default', + color = "default", spin, - size = 'md', + size = "md", style, className, title, @@ -303,23 +303,23 @@ export const Icon = memo(function Icon({ title={title} className={classNames( className, - !spin && 'transform-gpu', - spin && 'animate-spin', - 'flex-shrink-0', - size === 'xl' && 'h-6 w-6', - size === 'lg' && 'h-5 w-5', - size === 'md' && 'h-4 w-4', - size === 'sm' && 'h-3.5 w-3.5', - size === 'xs' && 'h-3 w-3', - size === '2xs' && 'h-2.5 w-2.5', - color === 'default' && 'inherit', - color === 'danger' && 'text-danger', - color === 'warning' && 'text-warning', - color === 'notice' && 'text-notice', - color === 'info' && 'text-info', - color === 'success' && 'text-success', - color === 'primary' && 'text-primary', - color === 'secondary' && 'text-secondary', + !spin && "transform-gpu", + spin && "animate-spin", + "flex-shrink-0", + size === "xl" && "h-6 w-6", + size === "lg" && "h-5 w-5", + size === "md" && "h-4 w-4", + size === "sm" && "h-3.5 w-3.5", + size === "xs" && "h-3 w-3", + size === "2xs" && "h-2.5 w-2.5", + color === "default" && "inherit", + color === "danger" && "text-danger", + color === "warning" && "text-warning", + color === "notice" && "text-notice", + color === "info" && "text-info", + color === "success" && "text-success", + color === "primary" && "text-primary", + color === "secondary" && "text-secondary", )} /> ); diff --git a/src-web/components/core/IconButton.tsx b/src-web/components/core/IconButton.tsx index 9b051862..ddff89b6 100644 --- a/src-web/components/core/IconButton.tsx +++ b/src-web/components/core/IconButton.tsx @@ -1,19 +1,19 @@ -import classNames from 'classnames'; -import type { MouseEvent } from 'react'; -import { forwardRef, useCallback } from 'react'; -import { useTimedBoolean } from '../../hooks/useTimedBoolean'; -import type { ButtonProps } from './Button'; -import { Button } from './Button'; -import type { IconProps } from './Icon'; -import { Icon } from './Icon'; -import { LoadingIcon } from './LoadingIcon'; +import classNames from "classnames"; +import type { MouseEvent } from "react"; +import { forwardRef, useCallback } from "react"; +import { useTimedBoolean } from "../../hooks/useTimedBoolean"; +import type { ButtonProps } from "./Button"; +import { Button } from "./Button"; +import type { IconProps } from "./Icon"; +import { Icon } from "./Icon"; +import { LoadingIcon } from "./LoadingIcon"; export type IconButtonProps = IconProps & ButtonProps & { showConfirm?: boolean; iconClassName?: string; - iconSize?: IconProps['size']; - iconColor?: IconProps['color']; + iconSize?: IconProps["size"]; + iconColor?: IconProps["color"]; title: string; showBadge?: boolean; }; @@ -22,18 +22,18 @@ export const IconButton = forwardRef(functio { showConfirm, icon, - color = 'default', + color = "default", spin, onClick, className, iconClassName, tabIndex, - size = 'md', + size = "md", iconSize, showBadge, iconColor, isLoading, - type = 'button', + type = "button", ...props }: IconButtonProps, ref, @@ -50,9 +50,9 @@ export const IconButton = forwardRef(functio return ( ); } @@ -586,7 +590,7 @@ function EncryptionInput({ disableObscureToggle autocompleteFunctions={autocompleteFunctions} autocompleteVariables={autocompleteVariables} - defaultValue={state.value ?? ''} + defaultValue={state.value ?? ""} forceUpdateKey={forceUpdateKey} onChange={handleInputChange} tint={tint} @@ -600,12 +604,12 @@ function EncryptionInput({ } const templateToSecure = createFastMutation({ - mutationKey: ['template-to-secure'], + mutationKey: ["template-to-secure"], mutationFn: convertTemplateToSecure, }); const templateToInsecure = createFastMutation({ - mutationKey: ['template-to-insecure'], + mutationKey: ["template-to-insecure"], mutationFn: convertTemplateToInsecure, disableToastError: true, }); diff --git a/src-web/components/core/JsonAttributeTree.tsx b/src-web/components/core/JsonAttributeTree.tsx index cbc21955..21ed5e53 100644 --- a/src-web/components/core/JsonAttributeTree.tsx +++ b/src-web/components/core/JsonAttributeTree.tsx @@ -1,7 +1,7 @@ -import classNames from 'classnames'; -import type { ReactNode } from 'react'; -import { useMemo, useState } from 'react'; -import { Icon } from './Icon'; +import classNames from "classnames"; +import type { ReactNode } from "react"; +import { useMemo, useState } from "react"; +import { Icon } from "./Icon"; interface Props { depth?: number; @@ -31,7 +31,7 @@ export const JsonAttributeTree = ({ labelClassName?: string; }>(() => { const jsonType = Object.prototype.toString.call(attrValue); - if (jsonType === '[object Object]') { + if (jsonType === "[object Object]") { return { children: isExpanded ? Object.keys(attrValue) @@ -47,11 +47,11 @@ export const JsonAttributeTree = ({ )) : null, isExpandable: Object.keys(attrValue).length > 0, - label: isExpanded ? `{${Object.keys(attrValue).length || ' '}}` : '{⋯}', - labelClassName: 'text-text-subtlest', + label: isExpanded ? `{${Object.keys(attrValue).length || " "}}` : "{⋯}", + labelClassName: "text-text-subtlest", }; } - if (jsonType === '[object Array]') { + if (jsonType === "[object Array]") { return { children: isExpanded ? // oxlint-disable-next-line no-explicit-any @@ -67,26 +67,26 @@ export const JsonAttributeTree = ({ )) : null, isExpandable: attrValue.length > 0, - label: isExpanded ? `[${attrValue.length || ' '}]` : '[⋯]', - labelClassName: 'text-text-subtlest', + label: isExpanded ? `[${attrValue.length || " "}]` : "[⋯]", + labelClassName: "text-text-subtlest", }; } return { children: null, isExpandable: false, - label: jsonType === '[object String]' ? `"${attrValue}"` : `${attrValue}`, + label: jsonType === "[object String]" ? `"${attrValue}"` : `${attrValue}`, labelClassName: classNames( - jsonType === '[object Boolean]' && 'text-primary', - jsonType === '[object Number]' && 'text-info', - jsonType === '[object String]' && 'text-notice', - jsonType === '[object Null]' && 'text-danger', + jsonType === "[object Boolean]" && "text-primary", + jsonType === "[object Number]" && "text-info", + jsonType === "[object String]" && "text-notice", + jsonType === "[object Null]" && "text-danger", ), }; }, [attrValue, attrKeyJsonPath, isExpanded, depth]); const labelEl = ( {label} @@ -95,8 +95,8 @@ export const JsonAttributeTree = ({
@@ -110,13 +110,13 @@ export const JsonAttributeTree = ({ size="xs" icon="chevron_right" className={classNames( - 'left-0 absolute transition-transform flex items-center', - 'group-hover:text-text-subtle', - isExpanded ? 'rotate-90' : '', + "left-0 absolute transition-transform flex items-center", + "group-hover:text-text-subtle", + isExpanded ? "rotate-90" : "", )} /> - {attrKey === undefined ? '$' : attrKey}: + {attrKey === undefined ? "$" : attrKey}: {labelEl} @@ -142,5 +142,5 @@ function joinObjectKey(baseKey: string | undefined, key: string): string { } function joinArrayKey(baseKey: string | undefined, index: number): string { - return `${baseKey ?? ''}[${index}]`; + return `${baseKey ?? ""}[${index}]`; } diff --git a/src-web/components/core/KeyValueRow.tsx b/src-web/components/core/KeyValueRow.tsx index 9e35c436..91aee687 100644 --- a/src-web/components/core/KeyValueRow.tsx +++ b/src-web/components/core/KeyValueRow.tsx @@ -1,5 +1,5 @@ -import classNames from 'classnames'; -import type { HTMLAttributes, ReactElement, ReactNode } from 'react'; +import classNames from "classnames"; +import type { HTMLAttributes, ReactElement, ReactNode } from "react"; interface Props { children: @@ -27,7 +27,7 @@ interface KeyValueRowProps { rightSlot?: ReactNode; leftSlot?: ReactNode; labelClassName?: string; - labelColor?: 'secondary' | 'primary' | 'info'; + labelColor?: "secondary" | "primary" | "info"; } export function KeyValueRow({ @@ -35,18 +35,18 @@ export function KeyValueRow({ children, rightSlot, leftSlot, - labelColor = 'secondary', + labelColor = "secondary", labelClassName, }: KeyValueRowProps) { return ( <>
{label} diff --git a/src-web/components/core/Label.tsx b/src-web/components/core/Label.tsx index afcc3725..3bcb65a0 100644 --- a/src-web/components/core/Label.tsx +++ b/src-web/components/core/Label.tsx @@ -1,6 +1,6 @@ -import classNames from 'classnames'; -import type { HTMLAttributes, ReactNode } from 'react'; -import { IconTooltip } from './IconTooltip'; +import classNames from "classnames"; +import type { HTMLAttributes, ReactNode } from "react"; +import { IconTooltip } from "./IconTooltip"; export function Label({ htmlFor, @@ -26,9 +26,9 @@ export function Label({ htmlFor={htmlFor ?? undefined} className={classNames( className, - visuallyHidden && 'sr-only', - 'flex-shrink-0 text-sm', - 'text-text-subtle whitespace-nowrap flex items-center gap-1 mb-0.5', + visuallyHidden && "sr-only", + "flex-shrink-0 text-sm", + "text-text-subtle whitespace-nowrap flex items-center gap-1 mb-0.5", )} {...props} > diff --git a/src-web/components/core/Link.tsx b/src-web/components/core/Link.tsx index 00dc7250..5b15d252 100644 --- a/src-web/components/core/Link.tsx +++ b/src-web/components/core/Link.tsx @@ -1,8 +1,8 @@ -import { Link as RouterLink } from '@tanstack/react-router'; -import classNames from 'classnames'; -import type { HTMLAttributes } from 'react'; -import { appInfo } from '../../lib/appInfo'; -import { Icon } from './Icon'; +import { Link as RouterLink } from "@tanstack/react-router"; +import classNames from "classnames"; +import type { HTMLAttributes } from "react"; +import { appInfo } from "../../lib/appInfo"; +import { Icon } from "./Icon"; interface Props extends HTMLAttributes { href: string; @@ -14,17 +14,17 @@ export function Link({ href, children, noUnderline, className, ...other }: Props className = classNames( className, - 'relative', - 'inline-flex items-center hover:underline group', - !noUnderline && 'underline', + "relative", + "inline-flex items-center hover:underline group", + !noUnderline && "underline", ); if (isExternal) { - const isYaakLink = href.startsWith('https://yaak.app'); + const isYaakLink = href.startsWith("https://yaak.app"); let finalHref = href; if (isYaakLink) { const url = new URL(href); - url.searchParams.set('ref', appInfo.identifier); + url.searchParams.set("ref", appInfo.identifier); finalHref = url.toString(); } return ( @@ -32,7 +32,7 @@ export function Link({ href, children, noUnderline, className, ...other }: Props e.preventDefault()} className={className} {...other} diff --git a/src-web/components/core/LoadingIcon.tsx b/src-web/components/core/LoadingIcon.tsx index 0353baa8..ccdb119a 100644 --- a/src-web/components/core/LoadingIcon.tsx +++ b/src-web/components/core/LoadingIcon.tsx @@ -1,34 +1,34 @@ -import classNames from 'classnames'; +import classNames from "classnames"; interface Props { - size?: '2xs' | 'xs' | 'sm' | 'md' | 'lg' | 'xl'; + size?: "2xs" | "xs" | "sm" | "md" | "lg" | "xl"; className?: string; } -export function LoadingIcon({ size = 'md', className }: Props) { +export function LoadingIcon({ size = "md", className }: Props) { const classes = classNames( className, - 'text-inherit flex-shrink-0', - size === 'xl' && 'h-6 w-6', - size === 'lg' && 'h-5 w-5', - size === 'md' && 'h-4 w-4', - size === 'sm' && 'h-3.5 w-3.5', - size === 'xs' && 'h-3 w-3', - size === '2xs' && 'h-2.5 w-2.5', - 'animate-spin', + "text-inherit flex-shrink-0", + size === "xl" && "h-6 w-6", + size === "lg" && "h-5 w-5", + size === "md" && "h-4 w-4", + size === "sm" && "h-3.5 w-3.5", + size === "xs" && "h-3 w-3", + size === "2xs" && "h-2.5 w-2.5", + "animate-spin", ); return (
); diff --git a/src-web/components/core/PairEditor.tsx b/src-web/components/core/PairEditor.tsx index 2b7b7867..4b42c6e6 100644 --- a/src-web/components/core/PairEditor.tsx +++ b/src-web/components/core/PairEditor.tsx @@ -1,4 +1,4 @@ -import type { DragEndEvent, DragMoveEvent, DragStartEvent } from '@dnd-kit/core'; +import type { DragEndEvent, DragMoveEvent, DragStartEvent } from "@dnd-kit/core"; import { DndContext, DragOverlay, @@ -8,33 +8,33 @@ import { useDroppable, useSensor, useSensors, -} from '@dnd-kit/core'; -import { basename } from '@tauri-apps/api/path'; -import classNames from 'classnames'; -import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from 'react'; -import type { WrappedEnvironmentVariable } from '../../hooks/useEnvironmentVariables'; -import { useRandomKey } from '../../hooks/useRandomKey'; -import { useToggle } from '../../hooks/useToggle'; -import { languageFromContentType } from '../../lib/contentType'; -import { showDialog } from '../../lib/dialog'; -import { computeSideForDragMove } from '../../lib/dnd'; -import { showPrompt } from '../../lib/prompt'; -import { DropMarker } from '../DropMarker'; -import { SelectFile } from '../SelectFile'; -import { Button } from './Button'; -import { Checkbox } from './Checkbox'; -import type { DropdownItem } from './Dropdown'; -import { Dropdown } from './Dropdown'; -import type { EditorProps } from './Editor/Editor'; -import type { GenericCompletionConfig } from './Editor/genericCompletion'; -import { Editor } from './Editor/LazyEditor'; -import { Icon } from './Icon'; -import { IconButton } from './IconButton'; -import type { InputHandle, InputProps } from './Input'; -import { Input } from './Input'; -import { ensurePairId } from './PairEditor.util'; -import type { RadioDropdownItem } from './RadioDropdown'; -import { RadioDropdown } from './RadioDropdown'; +} from "@dnd-kit/core"; +import { basename } from "@tauri-apps/api/path"; +import classNames from "classnames"; +import { Fragment, useCallback, useEffect, useMemo, useRef, useState } from "react"; +import type { WrappedEnvironmentVariable } from "../../hooks/useEnvironmentVariables"; +import { useRandomKey } from "../../hooks/useRandomKey"; +import { useToggle } from "../../hooks/useToggle"; +import { languageFromContentType } from "../../lib/contentType"; +import { showDialog } from "../../lib/dialog"; +import { computeSideForDragMove } from "../../lib/dnd"; +import { showPrompt } from "../../lib/prompt"; +import { DropMarker } from "../DropMarker"; +import { SelectFile } from "../SelectFile"; +import { Button } from "./Button"; +import { Checkbox } from "./Checkbox"; +import type { DropdownItem } from "./Dropdown"; +import { Dropdown } from "./Dropdown"; +import type { EditorProps } from "./Editor/Editor"; +import type { GenericCompletionConfig } from "./Editor/genericCompletion"; +import { Editor } from "./Editor/LazyEditor"; +import { Icon } from "./Icon"; +import { IconButton } from "./IconButton"; +import type { InputHandle, InputProps } from "./Input"; +import { Input } from "./Input"; +import { ensurePairId } from "./PairEditor.util"; +import type { RadioDropdownItem } from "./RadioDropdown"; +import { RadioDropdown } from "./RadioDropdown"; export interface PairEditorHandle { focusName(id: string): void; @@ -51,18 +51,18 @@ export type PairEditorProps = { nameAutocompleteFunctions?: boolean; nameAutocompleteVariables?: boolean; namePlaceholder?: string; - nameValidate?: InputProps['validate']; + nameValidate?: InputProps["validate"]; noScroll?: boolean; onChange: (pairs: PairWithId[]) => void; pairs: Pair[]; - stateKey: InputProps['stateKey']; + stateKey: InputProps["stateKey"]; setRef?: (n: PairEditorHandle) => void; valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined; valueAutocompleteFunctions?: boolean; - valueAutocompleteVariables?: boolean | 'environment'; + valueAutocompleteVariables?: boolean | "environment"; valuePlaceholder?: string; - valueType?: InputProps['type'] | ((pair: Pair) => InputProps['type']); - valueValidate?: InputProps['validate']; + valueType?: InputProps["type"] | ((pair: Pair) => InputProps["type"]); + valueValidate?: InputProps["validate"]; }; export type Pair = { @@ -188,7 +188,7 @@ export function PairEditor({ if (focusPrevious) { const index = pairs.findIndex((p) => p.id === pair.id); const id = pairs[index - 1]?.id ?? null; - rowsRef.current[id ?? 'n/a']?.focusName(); + rowsRef.current[id ?? "n/a"]?.focusName(); } return setPairsAndSave((oldPairs) => oldPairs.filter((p) => p.id !== pair.id)); }, @@ -224,7 +224,7 @@ export function PairEditor({ const side = computeSideForDragMove(overPair.id, e); const overIndex = pairs.findIndex((p) => p.id === overId); - const hoveredIndex = overIndex + (side === 'before' ? 0 : 1); + const hoveredIndex = overIndex + (side === "before" ? 0 : 1); setHoveredIndex(hoveredIndex); }, @@ -270,14 +270,14 @@ export function PairEditor({
@@ -379,22 +379,22 @@ type PairEditorRowProps = { setRef?: (id: string, n: RowHandle | null) => void; } & Pick< PairEditorProps, - | 'allowFileValues' - | 'allowMultilineValues' - | 'forcedEnvironmentId' - | 'forceUpdateKey' - | 'nameAutocomplete' - | 'nameAutocompleteVariables' - | 'namePlaceholder' - | 'nameValidate' - | 'nameAutocompleteFunctions' - | 'stateKey' - | 'valueAutocomplete' - | 'valueAutocompleteFunctions' - | 'valueAutocompleteVariables' - | 'valuePlaceholder' - | 'valueType' - | 'valueValidate' + | "allowFileValues" + | "allowMultilineValues" + | "forcedEnvironmentId" + | "forceUpdateKey" + | "nameAutocomplete" + | "nameAutocompleteVariables" + | "namePlaceholder" + | "nameValidate" + | "nameAutocompleteFunctions" + | "stateKey" + | "valueAutocomplete" + | "valueAutocompleteFunctions" + | "valueAutocompleteVariables" + | "valuePlaceholder" + | "valueType" + | "valueValidate" >; interface RowHandle { @@ -485,7 +485,7 @@ export function PairEditorRow({ const handleChangeValueFile = useMemo( () => ({ filePath }: { filePath: string | null }) => - onChange?.({ ...pair, value: filePath ?? '', isFile: true }), + onChange?.({ ...pair, value: filePath ?? "", isFile: true }), [onChange, pair], ); @@ -502,8 +502,8 @@ export function PairEditorRow({ const handleEditMultiLineValue = useCallback( () => showDialog({ - id: 'pair-edit-multiline', - size: 'dynamic', + id: "pair-edit-multiline", + size: "dynamic", title: <>Edit {pair.name}, render: ({ hide }) => ( [ { - label: 'Edit Multi-line', + label: "Edit Multi-line", onSelect: handleEditMultiLineValue, hidden: !allowMultilineValues, }, { - label: 'Delete', + label: "Delete", onSelect: handleDelete, - color: 'danger', + color: "danger", }, ], [allowMultilineValues, handleDelete, handleEditMultiLineValue], @@ -537,8 +537,8 @@ export function PairEditorRow({ const { setNodeRef: setDroppableRef } = useDroppable({ id: pair.id }); // Filter out the current pair name - const valueAutocompleteVariablesFiltered = useMemo(() => { - if (valueAutocompleteVariables === 'environment') { + const valueAutocompleteVariablesFiltered = useMemo(() => { + if (valueAutocompleteVariables === "environment") { return (v: WrappedEnvironmentVariable): boolean => v.variable.name !== pair.name; } return valueAutocompleteVariables; @@ -557,17 +557,17 @@ export function PairEditorRow({ ref={handleSetRef} className={classNames( className, - 'group/pair-row grid grid-cols-[auto_auto_minmax(0,1fr)_auto]', - 'grid-rows-1 items-center', - !pair.enabled && 'opacity-60', + "group/pair-row grid grid-cols-[auto_auto_minmax(0,1fr)_auto]", + "grid-rows-1 items-center", + !pair.enabled && "opacity-60", )} > {!isLast && !disableDrag ? ( @@ -575,8 +575,8 @@ export function PairEditorRow({ {...attributes} {...listeners} className={classNames( - 'py-2 h-7 w-4 flex items-center', - 'justify-center opacity-0 group-hover/pair-row:opacity-70', + "py-2 h-7 w-4 flex items-center", + "justify-center opacity-0 group-hover/pair-row:opacity-70", )} > @@ -586,9 +586,9 @@ export function PairEditorRow({ )}
- ) : pair.value.includes('\n') ? ( + ) : pair.value.includes("\n") ? ( ) : ( @@ -687,8 +687,8 @@ export function PairEditorRow({ } const fileItems: RadioDropdownItem[] = [ - { label: 'Text', value: 'text' }, - { label: 'File', value: 'file' }, + { label: "Text", value: "text" }, + { label: "File", value: "file" }, ]; function FileActionsDropdown({ @@ -710,8 +710,8 @@ function FileActionsDropdown({ }) { const onChange = useCallback( (v: string) => { - if (v === 'file') onChangeFile({ filePath: '' }); - else onChangeText(''); + if (v === "file") onChangeFile({ filePath: "" }); + else onChangeText(""); }, [onChangeFile, onChangeText], ); @@ -719,51 +719,51 @@ function FileActionsDropdown({ const itemsAfter = useMemo( () => [ { - label: 'Edit Multi-Line', + label: "Edit Multi-Line", leftSlot: , hidden: pair.isFile, onSelect: editMultiLine, }, { - label: 'Set Content-Type', + label: "Set Content-Type", leftSlot: , onSelect: async () => { const contentType = await showPrompt({ - id: 'content-type', - title: 'Override Content-Type', - label: 'Content-Type', + id: "content-type", + title: "Override Content-Type", + label: "Content-Type", required: false, - placeholder: 'text/plain', - defaultValue: pair.contentType ?? '', - confirmText: 'Set', - description: 'Leave blank to auto-detect', + placeholder: "text/plain", + defaultValue: pair.contentType ?? "", + confirmText: "Set", + description: "Leave blank to auto-detect", }); if (contentType == null) return; onChangeContentType(contentType); }, }, { - label: 'Set File Name', + label: "Set File Name", leftSlot: , onSelect: async () => { - console.log('PAIR', pair); - const defaultFilename = await basename(pair.value ?? ''); + console.log("PAIR", pair); + const defaultFilename = await basename(pair.value ?? ""); const filename = await showPrompt({ - id: 'filename', - title: 'Override Filename', - label: 'Filename', + id: "filename", + title: "Override Filename", + label: "Filename", required: false, - placeholder: defaultFilename ?? 'myfile.png', + placeholder: defaultFilename ?? "myfile.png", defaultValue: pair.filename, - confirmText: 'Set', - description: 'Leave blank to use the name of the selected file', + confirmText: "Set", + description: "Leave blank to use the name of the selected file", }); if (filename == null) return; onChangeFilename(filename); }, }, { - label: 'Unset File', + label: "Unset File", leftSlot: , hidden: pair.isFile, onSelect: async () => { @@ -771,11 +771,11 @@ function FileActionsDropdown({ }, }, { - label: 'Delete', + label: "Delete", onSelect: onDelete, - variant: 'danger', + variant: "danger", leftSlot: , - color: 'danger', + color: "danger", }, ], [ @@ -793,7 +793,7 @@ function FileActionsDropdown({ return ( ({ - namespace: 'global', - key: ['bulk_edit', preferenceName], + namespace: "global", + key: ["bulk_edit", preferenceName], fallback: false, }); @@ -24,13 +24,13 @@ export function PairOrBulkEditor({ preferenceName, ...props }: Props) { setUseBulk((b) => !b)} - icon={useBulk ? 'table' : 'file_code'} + icon={useBulk ? "table" : "file_code"} />
diff --git a/src-web/components/core/PillButton.tsx b/src-web/components/core/PillButton.tsx index 6f63836e..fe8b40cc 100644 --- a/src-web/components/core/PillButton.tsx +++ b/src-web/components/core/PillButton.tsx @@ -1,13 +1,13 @@ -import classNames from 'classnames'; -import type { ButtonProps } from './Button'; -import { Button } from './Button'; +import classNames from "classnames"; +import type { ButtonProps } from "./Button"; +import { Button } from "./Button"; export function PillButton({ className, ...props }: ButtonProps) { return ( diff --git a/src-web/components/core/RadioCards.tsx b/src-web/components/core/RadioCards.tsx index dbf48650..4caa581a 100644 --- a/src-web/components/core/RadioCards.tsx +++ b/src-web/components/core/RadioCards.tsx @@ -1,5 +1,5 @@ -import classNames from 'classnames'; -import type { ReactNode } from 'react'; +import classNames from "classnames"; +import type { ReactNode } from "react"; export interface RadioCardOption { value: T; @@ -28,11 +28,9 @@ export function RadioCards({