From bd322162c8715365671e17cb8a7ad2b2ccc87223 Mon Sep 17 00:00:00 2001 From: Gregory Schier Date: Fri, 17 Jan 2025 05:53:03 -0800 Subject: [PATCH] Auth plugins (#155) --- package-lock.json | 2 +- packages/plugin-runtime-types/package.json | 7 +- .../src/bindings/events.ts | 477 +- .../src/bindings/models.ts | 8 +- .../src/plugins/AuthenticationPlugin.ts | 9 + .../plugin-runtime-types/src/plugins/index.ts | 2 + packages/plugin-runtime/src/index.worker.ts | 29 +- src-tauri/Cargo.lock | 494 +-- src-tauri/Cargo.toml | 5 +- src-tauri/src/http_request.rs | 85 +- src-tauri/src/lib.rs | 188 +- src-tauri/src/render.rs | 18 +- src-tauri/src/template_callback.rs | 12 +- .../plugins/auth-basic/build/index.js | 55 + .../vendored/plugins/auth-basic/package.json | 9 + .../plugins/auth-bearer/build/index.js | 50 + .../vendored/plugins/auth-bearer/package.json | 9 + .../vendored/plugins/auth-jwt/build/index.js | 3822 +++++++++++++++++ .../vendored/plugins/auth-jwt/package.json | 15 + src-tauri/yaak-grpc/Cargo.toml | 15 +- src-tauri/yaak-grpc/src/client.rs | 172 + src-tauri/yaak-grpc/src/lib.rs | 4 +- src-tauri/yaak-grpc/src/manager.rs | 9 +- .../yaak-grpc/src/{proto.rs => reflection.rs} | 81 +- src-tauri/yaak-grpc/src/transport.rs | 19 + src-tauri/yaak-models/bindings/models.ts | 6 +- src-tauri/yaak-models/src/models.rs | 9 +- src-tauri/yaak-plugins/Cargo.toml | 6 +- src-tauri/yaak-plugins/bindings/events.ts | 258 +- src-tauri/yaak-plugins/bindings/models.ts | 6 +- src-tauri/yaak-plugins/src/events.rs | 104 +- src-tauri/yaak-plugins/src/manager.rs | 61 +- src-tauri/yaak-sync/bindings/models.ts | 6 +- src-web/components/DynamicForm.tsx | 251 ++ src-web/components/GlobalHooks.tsx | 2 + .../components/GrpcConnectionSetupPane.tsx | 52 +- .../components/HttpAuthenticationEditor.tsx | 48 + src-web/components/ImportCurlButton.tsx | 3 +- src-web/components/RecentRequestsDropdown.tsx | 12 +- .../components/RecentResponsesDropdown.tsx | 1 - src-web/components/RequestPane.tsx | 36 +- src-web/components/TemplateFunctionDialog.tsx | 233 +- src-web/components/core/BulkPairEditor.tsx | 8 +- src-web/components/core/Dropdown.tsx | 87 +- src-web/components/core/Editor/Editor.css | 8 +- src-web/components/core/Editor/Editor.tsx | 28 +- src-web/components/core/Icon.tsx | 1 + src-web/components/core/Input.tsx | 9 +- src-web/components/core/PairEditor.tsx | 33 +- src-web/hooks/useHttpAuthentication.ts | 38 + src-web/hooks/useKeyboardEvent.ts | 18 + src-web/hooks/useSendAnyHttpRequest.ts | 2 - src-web/hooks/useTemplateFunctions.ts | 8 +- src-web/lib/model_util.ts | 4 - src-web/lib/tauri.ts | 1 + src-web/main.css | 7 +- 56 files changed, 5468 insertions(+), 1474 deletions(-) create mode 100644 packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts create mode 100644 src-tauri/vendored/plugins/auth-basic/build/index.js create mode 100644 src-tauri/vendored/plugins/auth-basic/package.json create mode 100644 src-tauri/vendored/plugins/auth-bearer/build/index.js create mode 100644 src-tauri/vendored/plugins/auth-bearer/package.json create mode 100644 src-tauri/vendored/plugins/auth-jwt/build/index.js create mode 100644 src-tauri/vendored/plugins/auth-jwt/package.json create mode 100644 src-tauri/yaak-grpc/src/client.rs rename src-tauri/yaak-grpc/src/{proto.rs => reflection.rs} (78%) create mode 100644 src-tauri/yaak-grpc/src/transport.rs create mode 100644 src-web/components/DynamicForm.tsx create mode 100644 src-web/components/HttpAuthenticationEditor.tsx create mode 100644 src-web/hooks/useHttpAuthentication.ts create mode 100644 src-web/hooks/useKeyboardEvent.ts diff --git a/package-lock.json b/package-lock.json index bb7b17b7..d43098e8 100644 --- a/package-lock.json +++ b/package-lock.json @@ -15808,7 +15808,7 @@ }, "packages/plugin-runtime-types": { "name": "@yaakapp/api", - "version": "0.2.17", + "version": "0.2.25", "dependencies": { "@types/node": "^22.5.4" }, diff --git a/packages/plugin-runtime-types/package.json b/packages/plugin-runtime-types/package.json index 31aea7dd..d8950448 100644 --- a/packages/plugin-runtime-types/package.json +++ b/packages/plugin-runtime-types/package.json @@ -1,6 +1,6 @@ { "name": "@yaakapp/api", - "version": "0.2.17", + "version": "0.2.26", "main": "lib/index.js", "typings": "./lib/index.d.ts", "files": [ @@ -11,8 +11,9 @@ "build": "run-s build:copy-types build:tsc", "build:tsc": "tsc", "build:copy-types": "run-p build:copy-types:*", - "build:copy-types:root": "cpy --flat ../../src-tauri/yaak-plugin-runtime/bindings/*.ts ./src/bindings", - "build:copy-types:next": "cpy --flat ../../src-tauri/yaak-plugin-runtime/bindings/serde_json/*.ts ./src/bindings/serde_json", + "build:copy-types:root": "cpy --flat ../../src-tauri/yaak-plugins/bindings/*.ts ./src/bindings", + "build:copy-types:next": "cpy --flat ../../src-tauri/yaak-plugins/bindings/serde_json/*.ts ./src/bindings/serde_json", + "publish": "npm publish", "prepublishOnly": "npm run build" }, "dependencies": { diff --git a/packages/plugin-runtime-types/src/bindings/events.ts b/packages/plugin-runtime-types/src/bindings/events.ts index 88f08ff3..de27461c 100644 --- a/packages/plugin-runtime-types/src/bindings/events.ts +++ b/packages/plugin-runtime-types/src/bindings/events.ts @@ -1,338 +1,223 @@ // This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually. -import type { - Environment, - Folder, - GrpcRequest, - HttpRequest, - HttpResponse, - Workspace, -} from './models'; -import type { JsonValue } from './serde_json/JsonValue'; +import type { Environment } from "./models"; +import type { Folder } from "./models"; +import type { GrpcRequest } from "./models"; +import type { HttpRequest } from "./models"; +import type { HttpResponse } from "./models"; +import type { JsonValue } from "./serde_json/JsonValue"; +import type { Workspace } from "./models"; -export type BootRequest = { dir: string; watch: boolean }; +export type BootRequest = { dir: string, watch: boolean, }; -export type BootResponse = { name: string; version: string; capabilities: Array }; +export type BootResponse = { name: string, version: string, capabilities: Array, }; -export type CallHttpRequestActionArgs = { httpRequest: HttpRequest }; +export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array, }; -export type CallHttpRequestActionRequest = { - key: string; - pluginRefId: string; - args: CallHttpRequestActionArgs; -}; +export type CallHttpAuthenticationResponse = { url: string, headers: Array, }; -export type CallTemplateFunctionArgs = { - purpose: RenderPurpose; - values: { [key in string]?: string }; -}; +export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, }; -export type CallTemplateFunctionRequest = { name: string; args: CallTemplateFunctionArgs }; +export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, }; -export type CallTemplateFunctionResponse = { value: string | null }; +export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, }; -export type Color = - | 'custom' - | 'default' - | 'primary' - | 'secondary' - | 'info' - | 'success' - | 'notice' - | 'warning' - | 'danger'; +export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, }; -export type CopyTextRequest = { text: string }; +export type CallTemplateFunctionResponse = { value: string | null, }; -export type ExportHttpRequestRequest = { httpRequest: HttpRequest }; +export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger"; -export type ExportHttpRequestResponse = { content: string }; +export type CopyTextRequest = { text: string, }; -export type FilterRequest = { content: string; filter: string }; +export type EmptyPayload = {}; -export type FilterResponse = { content: string }; +export type ExportHttpRequestRequest = { httpRequest: HttpRequest, }; -export type FindHttpResponsesRequest = { requestId: string; limit?: number }; +export type ExportHttpRequestResponse = { content: string, }; -export type FindHttpResponsesResponse = { httpResponses: Array }; +export type FileFilter = { name: string, +/** + * File extensions to require + */ +extensions: Array, }; + +export type FilterRequest = { content: string, filter: string, }; + +export type FilterResponse = { content: string, }; + +export type FindHttpResponsesRequest = { requestId: string, limit?: number, }; + +export type FindHttpResponsesResponse = { httpResponses: Array, }; + +export type FormInput = { "type": "text" } & FormInputText | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest; + +export type FormInputBase = { name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputCheckbox = { name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputFile = { +/** + * The title of the file selection window + */ +title: string, +/** + * Allow selecting multiple files + */ +multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array, name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputHttpRequest = { name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputSelect = { +/** + * The options that will be available in the select input + */ +options: Array, name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputSelectOption = { name: string, value: string, }; + +export type FormInputText = { +/** + * Placeholder for the text input + */ +placeholder?: string | null, name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type GetHttpAuthenticationResponse = { name: string, pluginName: string, config: Array, }; export type GetHttpRequestActionsRequest = Record; -export type GetHttpRequestActionsResponse = { - actions: Array; - pluginRefId: string; -}; +export type GetHttpRequestActionsResponse = { actions: Array, pluginRefId: string, }; -export type GetHttpRequestByIdRequest = { id: string }; +export type GetHttpRequestByIdRequest = { id: string, }; -export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null }; +export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, }; -export type GetTemplateFunctionsResponse = { - functions: Array; - pluginRefId: string; -}; +export type GetTemplateFunctionsResponse = { functions: Array, pluginRefId: string, }; -export type HttpRequestAction = { key: string; label: string; icon?: Icon }; +export type HttpHeader = { name: string, value: string, }; -export type Icon = 'copy' | 'info' | 'check_circle' | 'alert_triangle' | '_unknown'; +export type HttpRequestAction = { key: string, label: string, icon?: Icon, }; -export type ImportRequest = { content: string }; +export type Icon = "copy" | "info" | "check_circle" | "alert_triangle" | "_unknown"; -export type ImportResources = { - workspaces: Array; - environments: Array; - folders: Array; - httpRequests: Array; - grpcRequests: Array; -}; +export type ImportRequest = { content: string, }; -export type ImportResponse = { resources: ImportResources }; +export type ImportResources = { workspaces: Array, environments: Array, folders: Array, httpRequests: Array, grpcRequests: Array, }; -export type InternalEvent = { - id: string; - pluginRefId: string; - replyId: string | null; - payload: InternalEventPayload; - windowContext: WindowContext; -}; +export type ImportResponse = { resources: ImportResources, }; -export type InternalEventPayload = - | ({ type: 'boot_request' } & BootRequest) - | ({ type: 'boot_response' } & BootResponse) - | { type: 'reload_request' } - | { type: 'reload_response' } - | { type: 'terminate_request' } - | { type: 'terminate_response' } - | ({ type: 'import_request' } & ImportRequest) - | ({ type: 'import_response' } & ImportResponse) - | ({ type: 'filter_request' } & FilterRequest) - | ({ type: 'filter_response' } & FilterResponse) - | ({ type: 'export_http_request_request' } & ExportHttpRequestRequest) - | ({ type: 'export_http_request_response' } & ExportHttpRequestResponse) - | ({ type: 'send_http_request_request' } & SendHttpRequestRequest) - | ({ type: 'send_http_request_response' } & SendHttpRequestResponse) - | ({ type: 'get_http_request_actions_request' } & GetHttpRequestActionsRequest) - | ({ type: 'get_http_request_actions_response' } & GetHttpRequestActionsResponse) - | ({ type: 'call_http_request_action_request' } & CallHttpRequestActionRequest) - | { type: 'get_template_functions_request' } - | ({ type: 'get_template_functions_response' } & GetTemplateFunctionsResponse) - | ({ type: 'call_template_function_request' } & CallTemplateFunctionRequest) - | ({ type: 'call_template_function_response' } & CallTemplateFunctionResponse) - | ({ type: 'copy_text_request' } & CopyTextRequest) - | ({ type: 'render_http_request_request' } & RenderHttpRequestRequest) - | ({ type: 'render_http_request_response' } & RenderHttpRequestResponse) - | ({ type: 'template_render_request' } & TemplateRenderRequest) - | ({ type: 'template_render_response' } & TemplateRenderResponse) - | ({ type: 'show_toast_request' } & ShowToastRequest) - | ({ type: 'prompt_text_request' } & PromptTextRequest) - | ({ type: 'prompt_text_response' } & PromptTextResponse) - | ({ type: 'get_http_request_by_id_request' } & GetHttpRequestByIdRequest) - | ({ type: 'get_http_request_by_id_response' } & GetHttpRequestByIdResponse) - | ({ type: 'find_http_responses_request' } & FindHttpResponsesRequest) - | ({ type: 'find_http_responses_response' } & FindHttpResponsesResponse) - | { type: 'empty_response' }; +export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, }; -export type OpenFileFilter = { - name: string; - /** - * File extensions to require - */ - extensions: Array; -}; +export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } & EmptyPayload | { "type": "reload_response" } & EmptyPayload | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_request" } & EmptyPayload | { "type": "get_http_authentication_response" } & GetHttpAuthenticationResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" } & EmptyPayload; -export type PromptTextRequest = { - id: string; - title: string; - label: string; - description?: string; - defaultValue?: string; - placeholder?: string; - /** - * Text to add to the confirmation button - */ - confirmText?: string; - /** - * Text to add to the cancel button - */ - cancelText?: string; - /** - * Require the user to enter a non-empty value - */ - require?: boolean; -}; +export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string, +/** + * Text to add to the confirmation button + */ +confirmText?: string, +/** + * Text to add to the cancel button + */ +cancelText?: string, +/** + * Require the user to enter a non-empty value + */ +require?: boolean, }; -export type PromptTextResponse = { value: string | null }; +export type PromptTextResponse = { value: string | null, }; -export type RenderHttpRequestRequest = { httpRequest: HttpRequest; purpose: RenderPurpose }; +export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, }; -export type RenderHttpRequestResponse = { httpRequest: HttpRequest }; +export type RenderHttpRequestResponse = { httpRequest: HttpRequest, }; -export type RenderPurpose = 'send' | 'preview'; +export type RenderPurpose = "send" | "preview"; -export type SendHttpRequestRequest = { httpRequest: HttpRequest }; +export type SendHttpRequestRequest = { httpRequest: HttpRequest, }; -export type SendHttpRequestResponse = { httpResponse: HttpResponse }; +export type SendHttpRequestResponse = { httpResponse: HttpResponse, }; -export type ShowToastRequest = { message: string; color?: Color; icon?: Icon }; +export type ShowToastRequest = { message: string, color?: Color, icon?: Icon, }; -export type TemplateFunction = { - name: string; - description?: string; - /** - * Also support alternative names. This is useful for not breaking existing - * tags when changing the `name` property - */ - aliases?: Array; - args: Array; -}; +export type TemplateFunction = { name: string, description?: string, +/** + * Also support alternative names. This is useful for not breaking existing + * tags when changing the `name` property + */ +aliases?: Array, args: Array, }; -export type TemplateFunctionArg = - | ({ type: 'text' } & TemplateFunctionTextArg) - | ({ - type: 'select'; - } & TemplateFunctionSelectArg) - | ({ type: 'checkbox' } & TemplateFunctionCheckboxArg) - | ({ - type: 'http_request'; - } & TemplateFunctionHttpRequestArg) - | ({ type: 'file' } & TemplateFunctionFileArg); +export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, }; -export type TemplateFunctionBaseArg = { - /** - * The name of the argument. Should be `camelCase` format - */ - name: string; - /** - * Whether the user must fill in the argument - */ - optional?: boolean; - /** - * The label of the input - */ - label?: string; - /** - * The default value - */ - defaultValue?: string; -}; +export type TemplateRenderResponse = { data: JsonValue, }; -export type TemplateFunctionCheckboxArg = { - /** - * The name of the argument. Should be `camelCase` format - */ - name: string; - /** - * Whether the user must fill in the argument - */ - optional?: boolean; - /** - * The label of the input - */ - label?: string; - /** - * The default value - */ - defaultValue?: string; -}; - -export type TemplateFunctionFileArg = { - /** - * The title of the file selection window - */ - title: string; - /** - * Allow selecting multiple files - */ - multiple?: boolean; - directory?: boolean; - defaultPath?: string; - filters?: Array; - /** - * The name of the argument. Should be `camelCase` format - */ - name: string; - /** - * Whether the user must fill in the argument - */ - optional?: boolean; - /** - * The label of the input - */ - label?: string; - /** - * The default value - */ - defaultValue?: string; -}; - -export type TemplateFunctionHttpRequestArg = { - /** - * The name of the argument. Should be `camelCase` format - */ - name: string; - /** - * Whether the user must fill in the argument - */ - optional?: boolean; - /** - * The label of the input - */ - label?: string; - /** - * The default value - */ - defaultValue?: string; -}; - -export type TemplateFunctionSelectArg = { - /** - * The options that will be available in the select input - */ - options: Array; - /** - * The name of the argument. Should be `camelCase` format - */ - name: string; - /** - * Whether the user must fill in the argument - */ - optional?: boolean; - /** - * The label of the input - */ - label?: string; - /** - * The default value - */ - defaultValue?: string; -}; - -export type TemplateFunctionSelectOption = { label: string; value: string }; - -export type TemplateFunctionTextArg = { - /** - * Placeholder for the text input - */ - placeholder?: string; - /** - * The name of the argument. Should be `camelCase` format - */ - name: string; - /** - * Whether the user must fill in the argument - */ - optional?: boolean; - /** - * The label of the input - */ - label?: string; - /** - * The default value - */ - defaultValue?: string; -}; - -export type TemplateRenderRequest = { data: JsonValue; purpose: RenderPurpose }; - -export type TemplateRenderResponse = { data: JsonValue }; - -export type WindowContext = { type: 'none' } | { type: 'label'; label: string }; +export type WindowContext = { "type": "none" } | { "type": "label", label: string, }; diff --git a/packages/plugin-runtime-types/src/bindings/models.ts b/packages/plugin-runtime-types/src/bindings/models.ts index 0ea9b8f7..42a93e00 100644 --- a/packages/plugin-runtime-types/src/bindings/models.ts +++ b/packages/plugin-runtime-types/src/bindings/models.ts @@ -2,17 +2,17 @@ export type Environment = { model: "environment", id: string, workspaceId: string, environmentId: string | null, createdAt: string, updatedAt: string, name: string, variables: Array, }; -export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, }; +export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id: string, }; export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, }; -export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, }; +export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, }; export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record, description: string, message: string, metadata: Array, method: string | null, name: string, service: string | null, sortPriority: number, url: string, }; export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record, authenticationType: string | null, body: Record, bodyType: string | null, description: string, headers: Array, method: string, name: string, sortPriority: number, url: string, urlParameters: Array, }; -export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, }; +export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, }; export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, }; @@ -20,6 +20,6 @@ export type HttpResponseHeader = { name: string, value: string, }; export type HttpResponseState = "initialized" | "connected" | "closed"; -export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, }; +export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, }; export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, }; diff --git a/packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts b/packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts new file mode 100644 index 00000000..9d79d17a --- /dev/null +++ b/packages/plugin-runtime-types/src/plugins/AuthenticationPlugin.ts @@ -0,0 +1,9 @@ +import {CallHttpAuthenticationRequest, CallHttpAuthenticationResponse, GetHttpAuthenticationResponse} from '..'; +import type { Context } from './Context'; + +export type AuthenticationPlugin = Omit & { + onApply( + ctx: Context, + args: CallHttpAuthenticationRequest, + ): Promise | CallHttpAuthenticationResponse; +}; diff --git a/packages/plugin-runtime-types/src/plugins/index.ts b/packages/plugin-runtime-types/src/plugins/index.ts index 528fcf03..d22453f4 100644 --- a/packages/plugin-runtime-types/src/plugins/index.ts +++ b/packages/plugin-runtime-types/src/plugins/index.ts @@ -1,3 +1,4 @@ +import { AuthenticationPlugin } from './AuthenticationPlugin'; import type { FilterPlugin } from './FilterPlugin'; import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin'; import type { ImporterPlugin } from './ImporterPlugin'; @@ -13,6 +14,7 @@ export type PluginDefinition = { importer?: ImporterPlugin; theme?: ThemePlugin; filter?: FilterPlugin; + authentication?: AuthenticationPlugin; httpRequestActions?: HttpRequestActionPlugin[]; templateFunctions?: TemplateFunctionPlugin[]; }; diff --git a/packages/plugin-runtime/src/index.worker.ts b/packages/plugin-runtime/src/index.worker.ts index 5bce613a..f49ec2cf 100644 --- a/packages/plugin-runtime/src/index.worker.ts +++ b/packages/plugin-runtime/src/index.worker.ts @@ -18,7 +18,7 @@ import type { HttpRequestActionPlugin } from '@yaakapp/api/lib/plugins/HttpReque import type { TemplateFunctionPlugin } from '@yaakapp/api/lib/plugins/TemplateFunctionPlugin'; import interceptStdout from 'intercept-stdout'; import * as console from 'node:console'; -import type { Stats} from 'node:fs'; +import type { Stats } from 'node:fs'; import { readFileSync, statSync, watch } from 'node:fs'; import path from 'node:path'; import * as util from 'node:util'; @@ -303,6 +303,33 @@ async function initialize() { return; } + if (payload.type === 'get_http_authentication_request' && mod.plugin?.authentication) { + const replyPayload: InternalEventPayload = { + type: 'get_http_authentication_response', + name: mod.plugin.authentication.name, + pluginName: pkg.name, + config: mod.plugin.authentication.config, + }; + sendPayload(windowContext, replyPayload, replyId); + return; + } + + if (payload.type === 'call_http_authentication_request' && mod.plugin?.authentication) { + const auth = mod.plugin.authentication; + if (typeof auth?.onApply === 'function') { + const result = await auth.onApply(ctx, payload); + sendPayload( + windowContext, + { + ...result, + type: 'call_http_authentication_response', + }, + replyId, + ); + return; + } + } + if ( payload.type === 'call_http_request_action_request' && Array.isArray(mod.plugin?.httpRequestActions) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ef1b4b4b..47eafbfe 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -392,34 +392,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" -[[package]] -name = "axum" -version = "0.6.20" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf" -dependencies = [ - "async-trait", - "axum-core 0.3.4", - "bitflags 1.3.2", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "itoa 1.0.11", - "matchit", - "memchr", - "mime", - "percent-encoding", - "pin-project-lite", - "rustversion", - "serde", - "sync_wrapper 0.1.2", - "tower 0.4.13", - "tower-layer", - "tower-service", -] - [[package]] name = "axum" version = "0.7.5" @@ -427,11 +399,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a6c9af12842a67734c9a2e355436e5d03b22383ed60cf13cd0c18fbfe3dcbcf" dependencies = [ "async-trait", - "axum-core 0.4.3", + "axum-core", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "itoa 1.0.11", "matchit", @@ -447,23 +419,6 @@ dependencies = [ "tower-service", ] -[[package]] -name = "axum-core" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "759fa577a247914fd3f7f76d62972792636412fbfd634cd452f6a385a74d2d2c" -dependencies = [ - "async-trait", - "bytes", - "futures-util", - "http 0.2.12", - "http-body 0.4.6", - "mime", - "rustversion", - "tower-layer", - "tower-service", -] - [[package]] name = "axum-core" version = "0.4.3" @@ -473,8 +428,8 @@ dependencies = [ "async-trait", "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "http-body-util", "mime", "pin-project-lite", @@ -1528,17 +1483,11 @@ dependencies = [ [[package]] name = "eventsource-client" -version = "0.13.0" -source = "git+https://github.com/yaakapp/rust-eventsource-client#e9e1e52421f11f0409179389b997aa49275a8461" +version = "0.14.0" +source = "git+https://github.com/yaakapp/rust-eventsource-client#60e0e3ac5038149c4778dc4979b09b152214f9a8" dependencies = [ - "futures", - "hyper 0.14.30", - "hyper-rustls 0.24.2", - "hyper-timeout 0.4.1", "log", "pin-project", - "rand 0.8.5", - "tokio", ] [[package]] @@ -1709,21 +1658,6 @@ dependencies = [ "new_debug_unreachable", ] -[[package]] -name = "futures" -version = "0.3.30" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" -dependencies = [ - "futures-channel", - "futures-core", - "futures-executor", - "futures-io", - "futures-sink", - "futures-task", - "futures-util", -] - [[package]] name = "futures-channel" version = "0.3.30" @@ -1810,7 +1744,6 @@ version = "0.3.30" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" dependencies = [ - "futures-channel", "futures-core", "futures-io", "futures-macro", @@ -2136,25 +2069,6 @@ dependencies = [ "syn 2.0.87", ] -[[package]] -name = "h2" -version = "0.3.26" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81fe527a889e1532da5c525686d96d4c2e74cdd345badf8dfef9f6b39dd5f5e8" -dependencies = [ - "bytes", - "fnv", - "futures-core", - "futures-sink", - "futures-util", - "http 0.2.12", - "indexmap 2.3.0", - "slab", - "tokio", - "tokio-util", - "tracing", -] - [[package]] name = "h2" version = "0.4.5" @@ -2166,7 +2080,7 @@ dependencies = [ "fnv", "futures-core", "futures-sink", - "http 1.1.0", + "http", "indexmap 2.3.0", "slab", "tokio", @@ -2284,37 +2198,15 @@ dependencies = [ [[package]] name = "http" -version = "0.2.12" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "601cbb57e577e2f5ef5be8e7b83f0f63994f25aa94d673e54a92d5c516d101f1" +checksum = "f16ca2af56261c99fba8bac40a10251ce8188205a4c448fbb745a2e4daa76fea" dependencies = [ "bytes", "fnv", "itoa 1.0.11", ] -[[package]] -name = "http" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b9ddb458710bc376481b842f5da65cdf31522de232c1ca8146abce2a358258" -dependencies = [ - "bytes", - "fnv", - "itoa 1.0.11", -] - -[[package]] -name = "http-body" -version = "0.4.6" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" -dependencies = [ - "bytes", - "http 0.2.12", - "pin-project-lite", -] - [[package]] name = "http-body" version = "1.0.1" @@ -2322,7 +2214,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.1.0", + "http", ] [[package]] @@ -2333,8 +2225,8 @@ checksum = "793429d76616a256bcb62c2a2ec2bed781c8307e797e2598c50010f2bee2544f" dependencies = [ "bytes", "futures-util", - "http 1.1.0", - "http-body 1.0.1", + "http", + "http-body", "pin-project-lite", ] @@ -2358,40 +2250,16 @@ checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" [[package]] name = "hyper" -version = "0.14.30" +version = "1.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a152ddd61dfaec7273fe8419ab357f33aee0d914c5f4efbf0d96fa749eea5ec9" -dependencies = [ - "bytes", - "futures-channel", - "futures-core", - "futures-util", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "httparse", - "httpdate", - "itoa 1.0.11", - "pin-project-lite", - "socket2", - "tokio", - "tower-service", - "tracing", - "want", -] - -[[package]] -name = "hyper" -version = "1.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50dfd22e0e76d0f662d429a5f80fcaf3855009297eab6a0a9f8543834744ba05" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" dependencies = [ "bytes", "futures-channel", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "httparse", "httpdate", "itoa 1.0.11", @@ -2403,57 +2271,30 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.24.2" +version = "0.27.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec3efd23720e2049821a693cbc7e65ea87c72f1c58ff2f9522ff332b1491e590" +checksum = "2d191583f3da1305256f22463b9bb0471acad48a4e534a5218b9963e9c1f59b2" dependencies = [ "futures-util", - "http 0.2.12", - "hyper 0.14.30", - "log", - "rustls 0.21.12", - "rustls-native-certs 0.6.3", - "tokio", - "tokio-rustls 0.24.1", -] - -[[package]] -name = "hyper-rustls" -version = "0.27.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08afdbb5c31130e3034af566421053ab03787c640246a446327f550d11bcb333" -dependencies = [ - "futures-util", - "http 1.1.0", - "hyper 1.4.1", + "http", + "hyper", "hyper-util", - "rustls 0.23.21", + "rustls", "rustls-pki-types", + "rustls-platform-verifier", "tokio", - "tokio-rustls 0.26.0", + "tokio-rustls", "tower-service", "webpki-roots", ] -[[package]] -name = "hyper-timeout" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bbb958482e8c7be4bc3cf272a766a2b0bf1a6755e7a6ae777f017a31d11b13b1" -dependencies = [ - "hyper 0.14.30", - "pin-project-lite", - "tokio", - "tokio-io-timeout", -] - [[package]] name = "hyper-timeout" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3203a961e5c83b6f5498933e78b6b263e208c197b63e9c6c53cc82ffd3f63793" dependencies = [ - "hyper 1.4.1", + "hyper", "hyper-util", "pin-project-lite", "tokio", @@ -2468,7 +2309,7 @@ checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" dependencies = [ "bytes", "http-body-util", - "hyper 1.4.1", + "hyper", "hyper-util", "native-tls", "tokio", @@ -2485,9 +2326,9 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.1.0", - "http-body 1.0.1", - "hyper 1.4.1", + "http", + "http-body", + "hyper", "pin-project-lite", "socket2", "tokio", @@ -2672,15 +2513,6 @@ dependencies = [ "nom 4.2.3", ] -[[package]] -name = "itertools" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba291022dbbd398a455acf126c1e341954079855bc60dfdda641363bd6922569" -dependencies = [ - "either", -] - [[package]] name = "itertools" version = "0.13.0" @@ -4114,22 +3946,12 @@ dependencies = [ [[package]] name = "prost" -version = "0.12.6" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "deb1435c188b76130da55f17a466d252ff7b1418b2ad3e037d127b94e3411f29" +checksum = "2c0fef6c4230e4ccf618a35c59d7ede15dea37de8427500f50aff708806e42ec" dependencies = [ "bytes", - "prost-derive 0.12.6", -] - -[[package]] -name = "prost" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7b0487d90e047de87f984913713b85c601c05609aad5b0df4b4573fbf69aa13f" -dependencies = [ - "bytes", - "prost-derive 0.13.3", + "prost-derive", ] [[package]] @@ -4140,14 +3962,14 @@ checksum = "5bb182580f71dd070f88d01ce3de9f4da5021db7115d2e1c3605a754153b77c1" dependencies = [ "bytes", "heck 0.5.0", - "itertools 0.13.0", + "itertools", "log", "multimap", "once_cell", "petgraph", "prettyplease", - "prost 0.13.3", - "prost-types 0.13.3", + "prost", + "prost-types", "regex", "syn 2.0.87", "tempfile", @@ -4155,25 +3977,12 @@ dependencies = [ [[package]] name = "prost-derive" -version = "0.12.6" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81bddcdb20abf9501610992b6759a4c888aef7d1a7247ef75e2404275ac24af1" +checksum = "157c5a9d7ea5c2ed2d9fb8f495b64759f7816c7eaea54ba3978f0d63000162e3" dependencies = [ "anyhow", - "itertools 0.12.1", - "proc-macro2", - "quote", - "syn 2.0.87", -] - -[[package]] -name = "prost-derive" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9552f850d5f0964a4e4d0bf306459ac29323ddfbae05e35a7c0d35cb0803cc5" -dependencies = [ - "anyhow", - "itertools 0.13.0", + "itertools", "proc-macro2", "quote", "syn 2.0.87", @@ -4181,24 +3990,24 @@ dependencies = [ [[package]] name = "prost-reflect" -version = "0.12.0" +version = "0.14.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "057237efdb71cf4b3f9396302a3d6599a92fa94063ba537b66130980ea9909f3" +checksum = "bc9647f03b808b79abca8408b1609be9887ba90453c940d00332a60eeb6f5748" dependencies = [ - "base64 0.21.7", + "base64 0.22.1", "once_cell", - "prost 0.12.6", + "prost", "prost-reflect-derive", - "prost-types 0.12.6", + "prost-types", "serde", "serde-value", ] [[package]] name = "prost-reflect-derive" -version = "0.12.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "172da1212c02be2c94901440cb27183cd92bff00ebacca5c323bf7520b8f9c04" +checksum = "f4fce6b22f15cc8d8d400a2b98ad29202b33bd56c7d9ddd815bc803a807ecb65" dependencies = [ "proc-macro2", "quote", @@ -4207,20 +4016,11 @@ dependencies = [ [[package]] name = "prost-types" -version = "0.12.6" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9091c90b0a32608e984ff2fa4091273cbdd755d54935c51d520887f4a1dbd5b0" +checksum = "cc2f1e56baa61e93533aebc21af4d2134b70f66275e0fcdf3cbe43d77ff7e8fc" dependencies = [ - "prost 0.12.6", -] - -[[package]] -name = "prost-types" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4759aa0d3a6232fb8dbdb97b61de2c20047c68aca932c7ed76da9d788508d670" -dependencies = [ - "prost 0.13.3", + "prost", ] [[package]] @@ -4288,7 +4088,7 @@ dependencies = [ "quinn-proto", "quinn-udp", "rustc-hash", - "rustls 0.23.21", + "rustls", "socket2", "thiserror 1.0.63", "tokio", @@ -4305,7 +4105,7 @@ dependencies = [ "rand 0.8.5", "ring", "rustc-hash", - "rustls 0.23.21", + "rustls", "slab", "thiserror 1.0.63", "tinyvec", @@ -4536,12 +4336,12 @@ dependencies = [ "encoding_rs", "futures-core", "futures-util", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.4.1", - "hyper-rustls 0.27.3", + "hyper", + "hyper-rustls", "hyper-tls", "hyper-util", "ipnet", @@ -4554,8 +4354,8 @@ dependencies = [ "percent-encoding", "pin-project-lite", "quinn", - "rustls 0.23.21", - "rustls-pemfile 2.1.3", + "rustls", + "rustls-pemfile", "rustls-pki-types", "serde", "serde_json", @@ -4564,7 +4364,7 @@ dependencies = [ "system-configuration", "tokio", "tokio-native-tls", - "tokio-rustls 0.26.0", + "tokio-rustls", "tokio-util", "tower 0.5.2", "tower-service", @@ -4741,18 +4541,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "rustls" -version = "0.21.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f56a14d1f48b391359b22f731fd4bd7e43c97f3c50eee276f3aa09c94784d3e" -dependencies = [ - "log", - "ring", - "rustls-webpki 0.101.7", - "sct", -] - [[package]] name = "rustls" version = "0.23.21" @@ -4762,23 +4550,11 @@ dependencies = [ "once_cell", "ring", "rustls-pki-types", - "rustls-webpki 0.102.8", + "rustls-webpki", "subtle", "zeroize", ] -[[package]] -name = "rustls-native-certs" -version = "0.6.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a9aace74cb666635c918e9c12bc0d348266037aa8eb599b5cba565709a8dff00" -dependencies = [ - "openssl-probe", - "rustls-pemfile 1.0.4", - "schannel", - "security-framework 2.11.1", -] - [[package]] name = "rustls-native-certs" version = "0.8.0" @@ -4786,21 +4562,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fcaf18a4f2be7326cd874a5fa579fae794320a0f388d365dca7e480e55f83f8a" dependencies = [ "openssl-probe", - "rustls-pemfile 2.1.3", + "rustls-pemfile", "rustls-pki-types", "schannel", "security-framework 2.11.1", ] -[[package]] -name = "rustls-pemfile" -version = "1.0.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" -dependencies = [ - "base64 0.21.7", -] - [[package]] name = "rustls-pemfile" version = "2.1.3" @@ -4828,10 +4595,10 @@ dependencies = [ "jni", "log", "once_cell", - "rustls 0.23.21", - "rustls-native-certs 0.8.0", + "rustls", + "rustls-native-certs", "rustls-platform-verifier-android", - "rustls-webpki 0.102.8", + "rustls-webpki", "security-framework 3.2.0", "security-framework-sys", "webpki-root-certs", @@ -4844,16 +4611,6 @@ version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" -[[package]] -name = "rustls-webpki" -version = "0.101.7" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b6275d1ee7a1cd780b64aca7726599a1dbc893b1e64144529e55c3c2f745765" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "rustls-webpki" version = "0.102.8" @@ -4943,16 +4700,6 @@ version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -[[package]] -name = "sct" -version = "0.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "da046153aa2352493d6cb7da4b6e5c0c057d8a1d0a9aa8560baffdd945acd414" -dependencies = [ - "ring", - "untrusted", -] - [[package]] name = "sea-query" version = "0.32.1" @@ -5445,8 +5192,8 @@ dependencies = [ "once_cell", "paste", "percent-encoding", - "rustls 0.23.21", - "rustls-pemfile 2.1.3", + "rustls", + "rustls-pemfile", "serde", "serde_json", "sha2", @@ -5854,7 +5601,7 @@ dependencies = [ "glob", "gtk", "heck 0.5.0", - "http 1.1.0", + "http", "http-range", "jni", "libc", @@ -6133,7 +5880,7 @@ dependencies = [ "dirs", "flate2", "futures-util", - "http 1.1.0", + "http", "infer", "minisign-verify", "percent-encoding", @@ -6176,7 +5923,7 @@ checksum = "2274ef891ccc0a8d318deffa9d70053f947664d12d58b9c0d1ae5e89237e01f7" dependencies = [ "dpi", "gtk", - "http 1.1.0", + "http", "jni", "raw-window-handle", "serde", @@ -6194,7 +5941,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0" dependencies = [ "gtk", - "http 1.1.0", + "http", "jni", "log", "objc2", @@ -6225,7 +5972,7 @@ dependencies = [ "dunce", "glob", "html5ever", - "http 1.1.0", + "http", "infer", "json-patch", "kuchikiki", @@ -6416,16 +6163,6 @@ dependencies = [ "windows-sys 0.52.0", ] -[[package]] -name = "tokio-io-timeout" -version = "1.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "30b74022ada614a1b4834de765f9bb43877f910cc8ce4be40e89042c9223a8bf" -dependencies = [ - "pin-project-lite", - "tokio", -] - [[package]] name = "tokio-macros" version = "2.4.0" @@ -6447,23 +6184,13 @@ dependencies = [ "tokio", ] -[[package]] -name = "tokio-rustls" -version = "0.24.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c28327cf380ac148141087fbfb9de9d7bd4e84ab5d2c28fbc911d753de8a7081" -dependencies = [ - "rustls 0.21.12", - "tokio", -] - [[package]] name = "tokio-rustls" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4" dependencies = [ - "rustls 0.23.21", + "rustls", "rustls-pki-types", "tokio", ] @@ -6564,52 +6291,25 @@ dependencies = [ [[package]] name = "tonic" -version = "0.10.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d560933a0de61cf715926b9cac824d4c883c2c43142f787595e48280c40a1d0e" +checksum = "877c5b330756d856ffcc4553ab34a5684481ade925ecc54bcd1bf02b1d0d4d52" dependencies = [ "async-stream", "async-trait", - "axum 0.6.20", - "base64 0.21.7", - "bytes", - "h2 0.3.26", - "http 0.2.12", - "http-body 0.4.6", - "hyper 0.14.30", - "hyper-timeout 0.4.1", - "percent-encoding", - "pin-project", - "prost 0.12.6", - "tokio", - "tokio-stream", - "tower 0.4.13", - "tower-layer", - "tower-service", - "tracing", -] - -[[package]] -name = "tonic" -version = "0.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38659f4a91aba8598d27821589f5db7dddd94601e7a01b1e485a50e5484c7401" -dependencies = [ - "async-stream", - "async-trait", - "axum 0.7.5", + "axum", "base64 0.22.1", "bytes", - "h2 0.4.5", - "http 1.1.0", - "http-body 1.0.1", + "h2", + "http", + "http-body", "http-body-util", - "hyper 1.4.1", - "hyper-timeout 0.5.1", + "hyper", + "hyper-timeout", "hyper-util", "percent-encoding", "pin-project", - "prost 0.13.3", + "prost", "socket2", "tokio", "tokio-stream", @@ -6634,15 +6334,15 @@ dependencies = [ [[package]] name = "tonic-reflection" -version = "0.10.2" +version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fa37c513df1339d197f4ba21d28c918b9ef1ac1768265f11ecb6b7f1cba1b76" +checksum = "878d81f52e7fcfd80026b7fdb6a9b578b3c3653ba987f87f0dce4b64043cba27" dependencies = [ - "prost 0.12.6", - "prost-types 0.12.6", + "prost", + "prost-types", "tokio", "tokio-stream", - "tonic 0.10.2", + "tonic", ] [[package]] @@ -7703,7 +7403,7 @@ dependencies = [ "gdkx11", "gtk", "html5ever", - "http 1.1.0", + "http", "javascriptcore-rs", "jni", "kuchikiki", @@ -7803,13 +7503,12 @@ dependencies = [ name = "yaak-app" version = "0.0.0" dependencies = [ - "base64 0.22.1", "chrono", "cocoa 0.26.0", "datetime", "eventsource-client", "hex_color", - "http 1.1.0", + "http", "log", "mime_guess", "objc", @@ -7818,7 +7517,7 @@ dependencies = [ "regex", "reqwest", "reqwest_cookie_store", - "rustls 0.23.21", + "rustls", "rustls-platform-verifier", "serde", "serde_json", @@ -7855,20 +7554,21 @@ dependencies = [ "anyhow", "async-recursion", "dunce", - "hyper 0.14.30", - "hyper-rustls 0.24.2", + "hyper", + "hyper-rustls", + "hyper-util", "log", "md5", - "prost 0.12.6", + "prost", "prost-reflect", - "prost-types 0.12.6", + "prost-types", "serde", "serde_json", "tauri", "tauri-plugin-shell", "tokio", "tokio-stream", - "tonic 0.10.2", + "tonic", "tonic-reflection", "uuid", ] @@ -7916,7 +7616,7 @@ dependencies = [ "dunce", "log", "path-slash", - "prost 0.13.3", + "prost", "rand 0.8.5", "regex", "serde", @@ -7925,7 +7625,7 @@ dependencies = [ "tauri-plugin-shell", "thiserror 2.0.7", "tokio", - "tonic 0.12.1", + "tonic", "tonic-build", "ts-rs", "yaak-models", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 371fb2c2..ba784029 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -38,12 +38,11 @@ cocoa = "0.26.0" openssl-sys = { version = "0.9", features = ["vendored"] } # For Ubuntu installation to work [dependencies] -base64 = "0.22.0" chrono = { version = "0.4.31", features = ["serde"] } datetime = "0.5.2" -eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.13.0" } +eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.14.0" } hex_color = "3.0.0" -http = "1" +http = { version = "1.2.0", default-features = false } log = "0.4.21" rand = "0.8.5" regex = "1.10.2" diff --git a/src-tauri/src/http_request.rs b/src-tauri/src/http_request.rs index e28ff774..b9550413 100644 --- a/src-tauri/src/http_request.rs +++ b/src-tauri/src/http_request.rs @@ -1,10 +1,8 @@ use crate::render::render_http_request; use crate::response_err; use crate::template_callback::PluginTemplateCallback; -use base64::prelude::BASE64_STANDARD; -use base64::Engine; use http::header::{ACCEPT, USER_AGENT}; -use http::{HeaderMap, HeaderName, HeaderValue}; +use http::{HeaderMap, HeaderName, HeaderValue, Uri}; use log::{debug, error, warn}; use mime_guess::Mime; use reqwest::redirect::Policy; @@ -32,7 +30,8 @@ use yaak_models::queries::{ get_base_environment, get_http_response, get_or_create_settings, get_workspace, update_response_if_id, upsert_cookie_jar, UpdateSource, }; -use yaak_plugins::events::{RenderPurpose, WindowContext}; +use yaak_plugins::events::{CallHttpAuthenticationRequest, HttpHeader, RenderPurpose, WindowContext}; +use yaak_plugins::manager::PluginManager; pub async fn send_http_request( window: &WebviewWindow, @@ -42,6 +41,7 @@ pub async fn send_http_request( cookie_jar: Option, cancelled_rx: &mut Receiver, ) -> Result { + let plugin_manager = window.state::(); let workspace = get_workspace(window, &request.workspace_id).await.expect("Failed to get Workspace"); let base_environment = get_base_environment(window, &request.workspace_id) @@ -160,7 +160,7 @@ pub async fn send_http_request( query_params.push((p.name, p.value)); } - let uri = match http::Uri::from_str(url_string.as_str()) { + let uri = match Uri::from_str(url_string.as_str()) { Ok(u) => u, Err(e) => { return Ok(response_err( @@ -234,29 +234,6 @@ pub async fn send_http_request( headers.insert(header_name, header_value); } - if let Some(b) = &rendered_request.authentication_type { - let empty_value = &serde_json::to_value("").unwrap(); - let a = rendered_request.authentication; - - if b == "basic" { - let username = a.get("username").unwrap_or(empty_value).as_str().unwrap_or_default(); - let password = a.get("password").unwrap_or(empty_value).as_str().unwrap_or_default(); - - let auth = format!("{username}:{password}"); - let encoded = BASE64_STANDARD.encode(auth); - headers.insert( - "Authorization", - HeaderValue::from_str(&format!("Basic {}", encoded)).unwrap(), - ); - } else if b == "bearer" { - let token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or_default(); - headers.insert( - "Authorization", - HeaderValue::from_str(&format!("Bearer {token}")).unwrap(), - ); - } - } - let request_body = rendered_request.body; if let Some(body_type) = &rendered_request.body_type { if body_type == "graphql" { @@ -383,7 +360,7 @@ pub async fn send_http_request( // Add headers last, because previous steps may modify them request_builder = request_builder.headers(headers); - let sendable_req = match request_builder.build() { + let mut sendable_req = match request_builder.build() { Ok(r) => r, Err(e) => { warn!("Failed to build request builder {e:?}"); @@ -391,6 +368,56 @@ pub async fn send_http_request( } }; + // Apply authentication + + // Map legacy auth name values from before they were plugins + let auth_plugin_name = match request.authentication_type.clone() { + Some(s) if s == "basic" => Some("@yaakapp/auth-basic".to_string()), + Some(s) if s == "bearer" => Some("@yaakapp/auth-bearer".to_string()), + _ => request.authentication_type.to_owned(), + }; + if let Some(plugin_name) = auth_plugin_name { + let req = CallHttpAuthenticationRequest { + config: serde_json::to_value(&request.authentication) + .unwrap() + .as_object() + .unwrap() + .to_owned(), + method: sendable_req.method().to_string(), + url: sendable_req.url().to_string(), + headers: sendable_req + .headers() + .iter() + .map(|(name, value)| HttpHeader { + name: name.to_string(), + value: value.to_str().unwrap_or_default().to_string(), + }) + .collect(), + }; + let plugin_result = + match plugin_manager.call_http_authentication(window, &plugin_name, req).await { + Ok(r) => r, + Err(e) => { + return Ok(response_err(&*response.lock().await, e.to_string(), window).await); + } + }; + + { + let url = sendable_req.url_mut(); + *url = Url::parse(&plugin_result.url).unwrap(); + } + + { + let headers = sendable_req.headers_mut(); + for header in plugin_result.headers { + headers.insert( + HeaderName::from_str(&header.name).unwrap(), + HeaderValue::from_str(&header.value).unwrap(), + ); + } + }; + } + let (resp_tx, resp_rx) = oneshot::channel::>(); let (done_tx, done_rx) = oneshot::channel::(); diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 79cabb85..83285af5 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -9,8 +9,6 @@ use crate::render::{render_grpc_request, render_http_request, render_json_value, use crate::template_callback::PluginTemplateCallback; use crate::updates::{UpdateMode, YaakUpdater}; use crate::window_menu::app_menu; -use base64::prelude::BASE64_STANDARD; -use base64::Engine; use chrono::Utc; use eventsource_client::{EventParser, SSE}; use log::{debug, error, info, warn}; @@ -65,11 +63,11 @@ use yaak_models::queries::{ upsert_workspace_meta, BatchUpsertResult, UpdateSource, }; use yaak_plugins::events::{ - BootResponse, CallHttpRequestActionRequest, FilterResponse, FindHttpResponsesResponse, - GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, Icon, - InternalEvent, InternalEventPayload, PromptTextResponse, RenderHttpRequestResponse, - RenderPurpose, SendHttpRequestResponse, ShowToastRequest, TemplateRenderResponse, - WindowContext, + BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse, + FindHttpResponsesResponse, GetHttpAuthenticationResponse, GetHttpRequestActionsResponse, + GetHttpRequestByIdResponse, GetTemplateFunctionsResponse, HttpHeader, Icon, InternalEvent, + InternalEventPayload, PromptTextResponse, RenderHttpRequestResponse, RenderPurpose, + SendHttpRequestResponse, ShowToastRequest, TemplateRenderResponse, WindowContext, }; use yaak_plugins::manager::PluginManager; use yaak_plugins::plugin_handle::PluginHandle; @@ -154,7 +152,7 @@ async fn cmd_render_template( RenderPurpose::Preview, ), ) - .await; + .await; Ok(rendered) } @@ -198,6 +196,7 @@ async fn cmd_grpc_go( environment_id: Option<&str>, proto_files: Vec, window: WebviewWindow, + plugin_manager: State<'_, PluginManager>, grpc_handle: State<'_, Mutex>, ) -> Result { let environment = match environment_id { @@ -210,7 +209,7 @@ async fn cmd_grpc_go( .ok_or("Failed to find GRPC request")?; let base_environment = get_base_environment(&window, &req.workspace_id).await.map_err(|e| e.to_string())?; - let req = render_grpc_request( + let mut req = render_grpc_request( &req, &base_environment, environment.as_ref(), @@ -220,7 +219,7 @@ async fn cmd_grpc_go( RenderPurpose::Send, ), ) - .await; + .await; let mut metadata = BTreeMap::new(); // Add the rest of metadata @@ -236,21 +235,37 @@ async fn cmd_grpc_go( metadata.insert(h.name, h.value); } - if let Some(b) = &req.authentication_type { - let req = req.clone(); - let empty_value = &serde_json::to_value("").unwrap(); - let a = req.authentication; + // Map legacy auth name values from before they were plugins + let auth_plugin_name = match req.authentication_type.clone() { + Some(s) if s == "basic" => Some("@yaakapp/auth-basic".to_string()), + Some(s) if s == "bearer" => Some("@yaakapp/auth-bearer".to_string()), + _ => req.authentication_type.to_owned(), + }; + if let Some(plugin_name) = auth_plugin_name { + let plugin_req = CallHttpAuthenticationRequest { + config: serde_json::to_value(&req.authentication) + .unwrap() + .as_object() + .unwrap() + .to_owned(), + method: "POST".to_string(), + url: req.url.clone(), + headers: metadata + .iter() + .map(|(name, value)| HttpHeader { + name: name.to_string(), + value: value.to_string(), + }) + .collect(), + }; + let plugin_result = plugin_manager + .call_http_authentication(&window, &plugin_name, plugin_req) + .await + .map_err(|e| e.to_string())?; - if b == "basic" { - let username = a.get("username").unwrap_or(empty_value).as_str().unwrap_or(""); - let password = a.get("password").unwrap_or(empty_value).as_str().unwrap_or(""); - - let auth = format!("{username}:{password}"); - let encoded = BASE64_STANDARD.encode(auth); - metadata.insert("Authorization".to_string(), format!("Basic {}", encoded)); - } else if b == "bearer" { - let token = a.get("token").unwrap_or(empty_value).as_str().unwrap_or(""); - metadata.insert("Authorization".to_string(), format!("Bearer {token}")); + req.url = plugin_result.url; + for header in plugin_result.headers { + metadata.insert(header.name, header.value); } } @@ -269,8 +284,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .map_err(|e| e.to_string())? + .await + .map_err(|e| e.to_string())? }; let conn_id = conn.id.clone(); @@ -322,8 +337,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .map_err(|e| e.to_string())?; + .await + .map_err(|e| e.to_string())?; return Ok(conn_id); } }; @@ -378,7 +393,7 @@ async fn cmd_grpc_go( RenderPurpose::Send, ), ) - .await + .await }) }) }; @@ -396,8 +411,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); }); return; } @@ -413,8 +428,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); }); } Ok(IncomingMsg::Commit) => { @@ -446,7 +461,7 @@ async fn cmd_grpc_go( RenderPurpose::Send, ), ) - .await; + .await; upsert_grpc_event( &window, @@ -458,8 +473,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); async move { let (maybe_stream, maybe_msg) = @@ -497,8 +512,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); } match maybe_msg { @@ -512,14 +527,14 @@ async fn cmd_grpc_go( } else { "Received response with metadata" } - .to_string(), + .to_string(), event_type: GrpcEventType::Info, ..base_event.clone() }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); upsert_grpc_event( &window, &GrpcEvent { @@ -529,8 +544,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); upsert_grpc_event( &window, &GrpcEvent { @@ -541,8 +556,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); } Some(Err(e)) => { upsert_grpc_event( @@ -566,8 +581,8 @@ async fn cmd_grpc_go( }), &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); } None => { // Server streaming doesn't return the initial message @@ -585,14 +600,14 @@ async fn cmd_grpc_go( } else { "Received response with metadata" } - .to_string(), + .to_string(), event_type: GrpcEventType::Info, ..base_event.clone() }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); stream.into_inner() } Some(Err(e)) => { @@ -618,8 +633,8 @@ async fn cmd_grpc_go( }), &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); return; } None => return, @@ -638,8 +653,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); } Ok(None) => { let trailers = @@ -655,8 +670,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); break; } Err(status) => { @@ -671,8 +686,8 @@ async fn cmd_grpc_go( }, &UpdateSource::Window, ) - .await - .unwrap(); + .await + .unwrap(); } } } @@ -930,8 +945,8 @@ async fn cmd_import_data( grpc_requests, &UpdateSource::Import, ) - .await - .map_err(|e| e.to_string())?; + .await + .map_err(|e| e.to_string())?; analytics::track_event( &window, @@ -939,7 +954,7 @@ async fn cmd_import_data( AnalyticsAction::Import, Some(json!({ "plugin": plugin_name })), ) - .await; + .await; Ok(upserted) } @@ -960,6 +975,14 @@ async fn cmd_template_functions( plugin_manager.get_template_functions(&window).await.map_err(|e| e.to_string()) } +#[tauri::command] +async fn cmd_get_http_authentication( + window: WebviewWindow, + plugin_manager: State<'_, PluginManager>, +) -> Result, String> { + plugin_manager.get_http_authentication(&window).await.map_err(|e| e.to_string()) +} + #[tauri::command] async fn cmd_call_http_request_action( window: WebviewWindow, @@ -985,7 +1008,7 @@ async fn cmd_curl_to_request( AnalyticsAction::Import, Some(json!({ "plugin": plugin_name })), ) - .await; + .await; import_result.resources.http_requests.get(0).ok_or("No curl command found".to_string()).map( |r| { @@ -1170,8 +1193,8 @@ async fn cmd_install_plugin( }, &UpdateSource::Window, ) - .await - .map_err(|e| e.to_string())?; + .await + .map_err(|e| e.to_string())?; Ok(plugin) } @@ -1222,8 +1245,8 @@ async fn cmd_create_cookie_jar( }, &UpdateSource::Window, ) - .await - .map_err(|e| e.to_string()) + .await + .map_err(|e| e.to_string()) } #[tauri::command] @@ -1245,8 +1268,8 @@ async fn cmd_create_environment( }, &UpdateSource::Window, ) - .await - .map_err(|e| e.to_string()) + .await + .map_err(|e| e.to_string()) } #[tauri::command] @@ -1268,8 +1291,8 @@ async fn cmd_create_grpc_request( }, &UpdateSource::Window, ) - .await - .map_err(|e| e.to_string()) + .await + .map_err(|e| e.to_string()) } #[tauri::command] @@ -1508,8 +1531,8 @@ async fn cmd_list_cookie_jars( }, &UpdateSource::Window, ) - .await - .expect("Failed to create CookieJar"); + .await + .expect("Failed to create CookieJar"); Ok(vec![cookie_jar]) } else { Ok(cookie_jars) @@ -1598,8 +1621,8 @@ async fn cmd_list_workspaces(window: WebviewWindow) -> Result, St }, &UpdateSource::Window, ) - .await - .expect("Failed to create Workspace"); + .await + .expect("Failed to create Workspace"); Ok(vec![workspace]) } else { Ok(workspaces) @@ -1853,6 +1876,7 @@ pub fn run() { cmd_get_environment, cmd_get_folder, cmd_get_grpc_request, + cmd_get_http_authentication, cmd_get_http_request, cmd_get_key_value, cmd_get_settings, @@ -1989,7 +2013,7 @@ fn create_main_window(handle: &AppHandle, url: &str) -> WebviewWindow { Some(_) => counter += 1, } } - .expect("Failed to generate label for new window"); + .expect("Failed to generate label for new window"); let config = CreateWindowConfig { url, @@ -2224,8 +2248,8 @@ async fn handle_plugin_event( req.request_id.as_str(), req.limit.map(|l| l as i64), ) - .await - .unwrap_or_default(); + .await + .unwrap_or_default(); Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse { http_responses, })) @@ -2254,7 +2278,7 @@ async fn handle_plugin_event( environment.as_ref(), &cb, ) - .await; + .await; Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse { http_request, })) @@ -2275,7 +2299,7 @@ async fn handle_plugin_event( render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await; Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data })) } - InternalEventPayload::ReloadResponse => { + InternalEventPayload::ReloadResponse(_) => { let window = get_window_from_window_context(app_handle, &window_context) .expect("Failed to find window for plugin reload"); let plugins = list_plugins(app_handle).await.unwrap(); @@ -2313,8 +2337,8 @@ async fn handle_plugin_event( req.http_request.id.as_str(), &UpdateSource::Plugin, ) - .await - .unwrap(); + .await + .unwrap(); let result = send_http_request( &window, @@ -2324,7 +2348,7 @@ async fn handle_plugin_event( cookie_jar, &mut tokio::sync::watch::channel(false).1, // No-op cancel channel ) - .await; + .await; let http_response = match result { Ok(r) => r, diff --git a/src-tauri/src/render.rs b/src-tauri/src/render.rs index 1cfd2da7..cce2323c 100644 --- a/src-tauri/src/render.rs +++ b/src-tauri/src/render.rs @@ -312,7 +312,7 @@ mod placeholder_tests { name: ":foo".into(), value: "xxx".into(), enabled: true, - id: "p1".into(), + id: None, }; assert_eq!( replace_path_placeholder(&p, "https://example.com/:foo/bar"), @@ -326,7 +326,7 @@ mod placeholder_tests { name: ":foo".into(), value: "xxx".into(), enabled: true, - id: "p1".into(), + id: None, }; assert_eq!( replace_path_placeholder(&p, "https://example.com/:foo"), @@ -340,7 +340,7 @@ mod placeholder_tests { name: ":foo".into(), value: "xxx".into(), enabled: true, - id: "p1".into(), + id: None, }; assert_eq!( replace_path_placeholder(&p, "https://example.com/:foo?:foo"), @@ -354,7 +354,7 @@ mod placeholder_tests { enabled: true, name: "".to_string(), value: "".to_string(), - id: "p1".into(), + id: None, }; assert_eq!( replace_path_placeholder(&p, "https://example.com/:missing"), @@ -368,7 +368,7 @@ mod placeholder_tests { enabled: false, name: ":foo".to_string(), value: "xxx".to_string(), - id: "p1".into(), + id: None, }; assert_eq!( replace_path_placeholder(&p, "https://example.com/:foo"), @@ -382,7 +382,7 @@ mod placeholder_tests { name: ":foo".into(), value: "xxx".into(), enabled: true, - id: "p1".into(), + id: None, }; assert_eq!( replace_path_placeholder(&p, "https://example.com/:foooo"), @@ -396,7 +396,7 @@ mod placeholder_tests { name: ":foo".into(), value: "Hello World".into(), enabled: true, - id: "p1".into(), + id: None, }; assert_eq!( replace_path_placeholder(&p, "https://example.com/:foo"), @@ -413,13 +413,13 @@ mod placeholder_tests { name: "b".to_string(), value: "bbb".to_string(), enabled: true, - id: "p1".into(), + id: None, }, HttpUrlParameter { name: ":a".to_string(), value: "aaa".to_string(), enabled: true, - id: "p2".into(), + id: None, }, ], ..Default::default() diff --git a/src-tauri/src/template_callback.rs b/src-tauri/src/template_callback.rs index d05037a0..837578ef 100644 --- a/src-tauri/src/template_callback.rs +++ b/src-tauri/src/template_callback.rs @@ -1,6 +1,6 @@ use std::collections::HashMap; use tauri::{AppHandle, Manager, Runtime}; -use yaak_plugins::events::{RenderPurpose, TemplateFunctionArg, WindowContext}; +use yaak_plugins::events::{FormInput, RenderPurpose, WindowContext}; use yaak_plugins::manager::PluginManager; use yaak_templates::TemplateCallback; @@ -48,11 +48,11 @@ impl TemplateCallback for PluginTemplateCallback { // Fill in default values for all args for a_def in function.args { let base = match a_def { - TemplateFunctionArg::Text(a) => a.base, - TemplateFunctionArg::Select(a) => a.base, - TemplateFunctionArg::Checkbox(a) => a.base, - TemplateFunctionArg::File(a) => a.base, - TemplateFunctionArg::HttpRequest(a) => a.base, + FormInput::Text(a) => a.base, + FormInput::Select(a) => a.base, + FormInput::Checkbox(a) => a.base, + FormInput::File(a) => a.base, + FormInput::HttpRequest(a) => a.base, }; if let None = args_with_defaults.get(base.name.as_str()) { args_with_defaults.insert(base.name, base.default_value.unwrap_or_default()); diff --git a/src-tauri/vendored/plugins/auth-basic/build/index.js b/src-tauri/vendored/plugins/auth-basic/build/index.js new file mode 100644 index 00000000..9c603389 --- /dev/null +++ b/src-tauri/vendored/plugins/auth-basic/build/index.js @@ -0,0 +1,55 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/index.ts +var src_exports = {}; +__export(src_exports, { + plugin: () => plugin +}); +module.exports = __toCommonJS(src_exports); +var plugin = { + authentication: { + name: "Basic", + config: [{ + type: "text", + name: "username", + label: "Username", + optional: true + }, { + type: "text", + name: "password", + label: "Password", + optional: true + }], + async onApply(_ctx, args) { + const { username, password } = args.config; + return { + url: args.url, + headers: [{ + name: "Authorization", + value: "Basic " + Buffer.from(`${username}:${password}`).toString("base64") + }] + }; + } + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + plugin +}); diff --git a/src-tauri/vendored/plugins/auth-basic/package.json b/src-tauri/vendored/plugins/auth-basic/package.json new file mode 100644 index 00000000..56444782 --- /dev/null +++ b/src-tauri/vendored/plugins/auth-basic/package.json @@ -0,0 +1,9 @@ +{ + "name": "@yaakapp/auth-basic", + "private": true, + "version": "0.0.1", + "scripts": { + "build": "yaakcli build ./src/index.ts", + "dev": "yaakcli dev ./src/index.js" + } +} diff --git a/src-tauri/vendored/plugins/auth-bearer/build/index.js b/src-tauri/vendored/plugins/auth-bearer/build/index.js new file mode 100644 index 00000000..8eb0089f --- /dev/null +++ b/src-tauri/vendored/plugins/auth-bearer/build/index.js @@ -0,0 +1,50 @@ +"use strict"; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// src/index.ts +var src_exports = {}; +__export(src_exports, { + plugin: () => plugin +}); +module.exports = __toCommonJS(src_exports); +var plugin = { + authentication: { + name: "Bearer", + config: [{ + type: "text", + name: "token", + label: "Token", + optional: true + }], + async onApply(_ctx, args) { + const { token } = args.config; + return { + url: args.url, + headers: [{ + name: "Authorization", + value: `Bearer ${token}`.trim() + }] + }; + } + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + plugin +}); diff --git a/src-tauri/vendored/plugins/auth-bearer/package.json b/src-tauri/vendored/plugins/auth-bearer/package.json new file mode 100644 index 00000000..03cf8662 --- /dev/null +++ b/src-tauri/vendored/plugins/auth-bearer/package.json @@ -0,0 +1,9 @@ +{ + "name": "@yaakapp/auth-bearer", + "private": true, + "version": "0.0.1", + "scripts": { + "build": "yaakcli build ./src/index.ts", + "dev": "yaakcli dev ./src/index.js" + } +} diff --git a/src-tauri/vendored/plugins/auth-jwt/build/index.js b/src-tauri/vendored/plugins/auth-jwt/build/index.js new file mode 100644 index 00000000..2b0a2edc --- /dev/null +++ b/src-tauri/vendored/plugins/auth-jwt/build/index.js @@ -0,0 +1,3822 @@ +"use strict"; +var __create = Object.create; +var __defProp = Object.defineProperty; +var __getOwnPropDesc = Object.getOwnPropertyDescriptor; +var __getOwnPropNames = Object.getOwnPropertyNames; +var __getProtoOf = Object.getPrototypeOf; +var __hasOwnProp = Object.prototype.hasOwnProperty; +var __commonJS = (cb, mod) => function __require() { + return mod || (0, cb[__getOwnPropNames(cb)[0]])((mod = { exports: {} }).exports, mod), mod.exports; +}; +var __export = (target, all) => { + for (var name in all) + __defProp(target, name, { get: all[name], enumerable: true }); +}; +var __copyProps = (to, from, except, desc) => { + if (from && typeof from === "object" || typeof from === "function") { + for (let key of __getOwnPropNames(from)) + if (!__hasOwnProp.call(to, key) && key !== except) + __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); + } + return to; +}; +var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps( + // If the importer is in node compatibility mode or this is not an ESM + // file that has been converted to a CommonJS file using a Babel- + // compatible transform (i.e. "__esModule" has not been set), then set + // "default" to the CommonJS "module.exports" for node compatibility. + isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target, + mod +)); +var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); + +// ../../node_modules/safe-buffer/index.js +var require_safe_buffer = __commonJS({ + "../../node_modules/safe-buffer/index.js"(exports2, module2) { + var buffer = require("buffer"); + var Buffer2 = buffer.Buffer; + function copyProps(src, dst) { + for (var key in src) { + dst[key] = src[key]; + } + } + if (Buffer2.from && Buffer2.alloc && Buffer2.allocUnsafe && Buffer2.allocUnsafeSlow) { + module2.exports = buffer; + } else { + copyProps(buffer, exports2); + exports2.Buffer = SafeBuffer; + } + function SafeBuffer(arg, encodingOrOffset, length) { + return Buffer2(arg, encodingOrOffset, length); + } + SafeBuffer.prototype = Object.create(Buffer2.prototype); + copyProps(Buffer2, SafeBuffer); + SafeBuffer.from = function(arg, encodingOrOffset, length) { + if (typeof arg === "number") { + throw new TypeError("Argument must not be a number"); + } + return Buffer2(arg, encodingOrOffset, length); + }; + SafeBuffer.alloc = function(size, fill, encoding) { + if (typeof size !== "number") { + throw new TypeError("Argument must be a number"); + } + var buf = Buffer2(size); + if (fill !== void 0) { + if (typeof encoding === "string") { + buf.fill(fill, encoding); + } else { + buf.fill(fill); + } + } else { + buf.fill(0); + } + return buf; + }; + SafeBuffer.allocUnsafe = function(size) { + if (typeof size !== "number") { + throw new TypeError("Argument must be a number"); + } + return Buffer2(size); + }; + SafeBuffer.allocUnsafeSlow = function(size) { + if (typeof size !== "number") { + throw new TypeError("Argument must be a number"); + } + return buffer.SlowBuffer(size); + }; + } +}); + +// ../../node_modules/jws/lib/data-stream.js +var require_data_stream = __commonJS({ + "../../node_modules/jws/lib/data-stream.js"(exports2, module2) { + var Buffer2 = require_safe_buffer().Buffer; + var Stream = require("stream"); + var util = require("util"); + function DataStream(data) { + this.buffer = null; + this.writable = true; + this.readable = true; + if (!data) { + this.buffer = Buffer2.alloc(0); + return this; + } + if (typeof data.pipe === "function") { + this.buffer = Buffer2.alloc(0); + data.pipe(this); + return this; + } + if (data.length || typeof data === "object") { + this.buffer = data; + this.writable = false; + process.nextTick(function() { + this.emit("end", data); + this.readable = false; + this.emit("close"); + }.bind(this)); + return this; + } + throw new TypeError("Unexpected data type (" + typeof data + ")"); + } + util.inherits(DataStream, Stream); + DataStream.prototype.write = function write(data) { + this.buffer = Buffer2.concat([this.buffer, Buffer2.from(data)]); + this.emit("data", data); + }; + DataStream.prototype.end = function end(data) { + if (data) + this.write(data); + this.emit("end", data); + this.emit("close"); + this.writable = false; + this.readable = false; + }; + module2.exports = DataStream; + } +}); + +// ../../node_modules/buffer-equal-constant-time/index.js +var require_buffer_equal_constant_time = __commonJS({ + "../../node_modules/buffer-equal-constant-time/index.js"(exports2, module2) { + "use strict"; + var Buffer2 = require("buffer").Buffer; + var SlowBuffer = require("buffer").SlowBuffer; + module2.exports = bufferEq; + function bufferEq(a, b) { + if (!Buffer2.isBuffer(a) || !Buffer2.isBuffer(b)) { + return false; + } + if (a.length !== b.length) { + return false; + } + var c = 0; + for (var i = 0; i < a.length; i++) { + c |= a[i] ^ b[i]; + } + return c === 0; + } + bufferEq.install = function() { + Buffer2.prototype.equal = SlowBuffer.prototype.equal = function equal(that) { + return bufferEq(this, that); + }; + }; + var origBufEqual = Buffer2.prototype.equal; + var origSlowBufEqual = SlowBuffer.prototype.equal; + bufferEq.restore = function() { + Buffer2.prototype.equal = origBufEqual; + SlowBuffer.prototype.equal = origSlowBufEqual; + }; + } +}); + +// ../../node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js +var require_param_bytes_for_alg = __commonJS({ + "../../node_modules/ecdsa-sig-formatter/src/param-bytes-for-alg.js"(exports2, module2) { + "use strict"; + function getParamSize(keySize) { + var result = (keySize / 8 | 0) + (keySize % 8 === 0 ? 0 : 1); + return result; + } + var paramBytesForAlg = { + ES256: getParamSize(256), + ES384: getParamSize(384), + ES512: getParamSize(521) + }; + function getParamBytesForAlg(alg) { + var paramBytes = paramBytesForAlg[alg]; + if (paramBytes) { + return paramBytes; + } + throw new Error('Unknown algorithm "' + alg + '"'); + } + module2.exports = getParamBytesForAlg; + } +}); + +// ../../node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js +var require_ecdsa_sig_formatter = __commonJS({ + "../../node_modules/ecdsa-sig-formatter/src/ecdsa-sig-formatter.js"(exports2, module2) { + "use strict"; + var Buffer2 = require_safe_buffer().Buffer; + var getParamBytesForAlg = require_param_bytes_for_alg(); + var MAX_OCTET = 128; + var CLASS_UNIVERSAL = 0; + var PRIMITIVE_BIT = 32; + var TAG_SEQ = 16; + var TAG_INT = 2; + var ENCODED_TAG_SEQ = TAG_SEQ | PRIMITIVE_BIT | CLASS_UNIVERSAL << 6; + var ENCODED_TAG_INT = TAG_INT | CLASS_UNIVERSAL << 6; + function base64Url(base64) { + return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); + } + function signatureAsBuffer(signature) { + if (Buffer2.isBuffer(signature)) { + return signature; + } else if ("string" === typeof signature) { + return Buffer2.from(signature, "base64"); + } + throw new TypeError("ECDSA signature must be a Base64 string or a Buffer"); + } + function derToJose(signature, alg) { + signature = signatureAsBuffer(signature); + var paramBytes = getParamBytesForAlg(alg); + var maxEncodedParamLength = paramBytes + 1; + var inputLength = signature.length; + var offset = 0; + if (signature[offset++] !== ENCODED_TAG_SEQ) { + throw new Error('Could not find expected "seq"'); + } + var seqLength = signature[offset++]; + if (seqLength === (MAX_OCTET | 1)) { + seqLength = signature[offset++]; + } + if (inputLength - offset < seqLength) { + throw new Error('"seq" specified length of "' + seqLength + '", only "' + (inputLength - offset) + '" remaining'); + } + if (signature[offset++] !== ENCODED_TAG_INT) { + throw new Error('Could not find expected "int" for "r"'); + } + var rLength = signature[offset++]; + if (inputLength - offset - 2 < rLength) { + throw new Error('"r" specified length of "' + rLength + '", only "' + (inputLength - offset - 2) + '" available'); + } + if (maxEncodedParamLength < rLength) { + throw new Error('"r" specified length of "' + rLength + '", max of "' + maxEncodedParamLength + '" is acceptable'); + } + var rOffset = offset; + offset += rLength; + if (signature[offset++] !== ENCODED_TAG_INT) { + throw new Error('Could not find expected "int" for "s"'); + } + var sLength = signature[offset++]; + if (inputLength - offset !== sLength) { + throw new Error('"s" specified length of "' + sLength + '", expected "' + (inputLength - offset) + '"'); + } + if (maxEncodedParamLength < sLength) { + throw new Error('"s" specified length of "' + sLength + '", max of "' + maxEncodedParamLength + '" is acceptable'); + } + var sOffset = offset; + offset += sLength; + if (offset !== inputLength) { + throw new Error('Expected to consume entire buffer, but "' + (inputLength - offset) + '" bytes remain'); + } + var rPadding = paramBytes - rLength, sPadding = paramBytes - sLength; + var dst = Buffer2.allocUnsafe(rPadding + rLength + sPadding + sLength); + for (offset = 0; offset < rPadding; ++offset) { + dst[offset] = 0; + } + signature.copy(dst, offset, rOffset + Math.max(-rPadding, 0), rOffset + rLength); + offset = paramBytes; + for (var o = offset; offset < o + sPadding; ++offset) { + dst[offset] = 0; + } + signature.copy(dst, offset, sOffset + Math.max(-sPadding, 0), sOffset + sLength); + dst = dst.toString("base64"); + dst = base64Url(dst); + return dst; + } + function countPadding(buf, start, stop) { + var padding = 0; + while (start + padding < stop && buf[start + padding] === 0) { + ++padding; + } + var needsSign = buf[start + padding] >= MAX_OCTET; + if (needsSign) { + --padding; + } + return padding; + } + function joseToDer(signature, alg) { + signature = signatureAsBuffer(signature); + var paramBytes = getParamBytesForAlg(alg); + var signatureBytes = signature.length; + if (signatureBytes !== paramBytes * 2) { + throw new TypeError('"' + alg + '" signatures must be "' + paramBytes * 2 + '" bytes, saw "' + signatureBytes + '"'); + } + var rPadding = countPadding(signature, 0, paramBytes); + var sPadding = countPadding(signature, paramBytes, signature.length); + var rLength = paramBytes - rPadding; + var sLength = paramBytes - sPadding; + var rsBytes = 1 + 1 + rLength + 1 + 1 + sLength; + var shortLength = rsBytes < MAX_OCTET; + var dst = Buffer2.allocUnsafe((shortLength ? 2 : 3) + rsBytes); + var offset = 0; + dst[offset++] = ENCODED_TAG_SEQ; + if (shortLength) { + dst[offset++] = rsBytes; + } else { + dst[offset++] = MAX_OCTET | 1; + dst[offset++] = rsBytes & 255; + } + dst[offset++] = ENCODED_TAG_INT; + dst[offset++] = rLength; + if (rPadding < 0) { + dst[offset++] = 0; + offset += signature.copy(dst, offset, 0, paramBytes); + } else { + offset += signature.copy(dst, offset, rPadding, paramBytes); + } + dst[offset++] = ENCODED_TAG_INT; + dst[offset++] = sLength; + if (sPadding < 0) { + dst[offset++] = 0; + signature.copy(dst, offset, paramBytes); + } else { + signature.copy(dst, offset, paramBytes + sPadding); + } + return dst; + } + module2.exports = { + derToJose, + joseToDer + }; + } +}); + +// ../../node_modules/jwa/index.js +var require_jwa = __commonJS({ + "../../node_modules/jwa/index.js"(exports2, module2) { + var bufferEqual = require_buffer_equal_constant_time(); + var Buffer2 = require_safe_buffer().Buffer; + var crypto = require("crypto"); + var formatEcdsa = require_ecdsa_sig_formatter(); + var util = require("util"); + var MSG_INVALID_ALGORITHM = '"%s" is not a valid algorithm.\n Supported algorithms are:\n "HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "PS256", "PS384", "PS512", "ES256", "ES384", "ES512" and "none".'; + var MSG_INVALID_SECRET = "secret must be a string or buffer"; + var MSG_INVALID_VERIFIER_KEY = "key must be a string or a buffer"; + var MSG_INVALID_SIGNER_KEY = "key must be a string, a buffer or an object"; + var supportsKeyObjects = typeof crypto.createPublicKey === "function"; + if (supportsKeyObjects) { + MSG_INVALID_VERIFIER_KEY += " or a KeyObject"; + MSG_INVALID_SECRET += "or a KeyObject"; + } + function checkIsPublicKey(key) { + if (Buffer2.isBuffer(key)) { + return; + } + if (typeof key === "string") { + return; + } + if (!supportsKeyObjects) { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + if (typeof key !== "object") { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + if (typeof key.type !== "string") { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + if (typeof key.asymmetricKeyType !== "string") { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + if (typeof key.export !== "function") { + throw typeError(MSG_INVALID_VERIFIER_KEY); + } + } + function checkIsPrivateKey(key) { + if (Buffer2.isBuffer(key)) { + return; + } + if (typeof key === "string") { + return; + } + if (typeof key === "object") { + return; + } + throw typeError(MSG_INVALID_SIGNER_KEY); + } + function checkIsSecretKey(key) { + if (Buffer2.isBuffer(key)) { + return; + } + if (typeof key === "string") { + return key; + } + if (!supportsKeyObjects) { + throw typeError(MSG_INVALID_SECRET); + } + if (typeof key !== "object") { + throw typeError(MSG_INVALID_SECRET); + } + if (key.type !== "secret") { + throw typeError(MSG_INVALID_SECRET); + } + if (typeof key.export !== "function") { + throw typeError(MSG_INVALID_SECRET); + } + } + function fromBase64(base64) { + return base64.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); + } + function toBase64(base64url) { + base64url = base64url.toString(); + var padding = 4 - base64url.length % 4; + if (padding !== 4) { + for (var i = 0; i < padding; ++i) { + base64url += "="; + } + } + return base64url.replace(/\-/g, "+").replace(/_/g, "/"); + } + function typeError(template) { + var args = [].slice.call(arguments, 1); + var errMsg = util.format.bind(util, template).apply(null, args); + return new TypeError(errMsg); + } + function bufferOrString(obj) { + return Buffer2.isBuffer(obj) || typeof obj === "string"; + } + function normalizeInput(thing) { + if (!bufferOrString(thing)) + thing = JSON.stringify(thing); + return thing; + } + function createHmacSigner(bits) { + return function sign(thing, secret) { + checkIsSecretKey(secret); + thing = normalizeInput(thing); + var hmac = crypto.createHmac("sha" + bits, secret); + var sig = (hmac.update(thing), hmac.digest("base64")); + return fromBase64(sig); + }; + } + function createHmacVerifier(bits) { + return function verify(thing, signature, secret) { + var computedSig = createHmacSigner(bits)(thing, secret); + return bufferEqual(Buffer2.from(signature), Buffer2.from(computedSig)); + }; + } + function createKeySigner(bits) { + return function sign(thing, privateKey) { + checkIsPrivateKey(privateKey); + thing = normalizeInput(thing); + var signer = crypto.createSign("RSA-SHA" + bits); + var sig = (signer.update(thing), signer.sign(privateKey, "base64")); + return fromBase64(sig); + }; + } + function createKeyVerifier(bits) { + return function verify(thing, signature, publicKey) { + checkIsPublicKey(publicKey); + thing = normalizeInput(thing); + signature = toBase64(signature); + var verifier = crypto.createVerify("RSA-SHA" + bits); + verifier.update(thing); + return verifier.verify(publicKey, signature, "base64"); + }; + } + function createPSSKeySigner(bits) { + return function sign(thing, privateKey) { + checkIsPrivateKey(privateKey); + thing = normalizeInput(thing); + var signer = crypto.createSign("RSA-SHA" + bits); + var sig = (signer.update(thing), signer.sign({ + key: privateKey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST + }, "base64")); + return fromBase64(sig); + }; + } + function createPSSKeyVerifier(bits) { + return function verify(thing, signature, publicKey) { + checkIsPublicKey(publicKey); + thing = normalizeInput(thing); + signature = toBase64(signature); + var verifier = crypto.createVerify("RSA-SHA" + bits); + verifier.update(thing); + return verifier.verify({ + key: publicKey, + padding: crypto.constants.RSA_PKCS1_PSS_PADDING, + saltLength: crypto.constants.RSA_PSS_SALTLEN_DIGEST + }, signature, "base64"); + }; + } + function createECDSASigner(bits) { + var inner = createKeySigner(bits); + return function sign() { + var signature = inner.apply(null, arguments); + signature = formatEcdsa.derToJose(signature, "ES" + bits); + return signature; + }; + } + function createECDSAVerifer(bits) { + var inner = createKeyVerifier(bits); + return function verify(thing, signature, publicKey) { + signature = formatEcdsa.joseToDer(signature, "ES" + bits).toString("base64"); + var result = inner(thing, signature, publicKey); + return result; + }; + } + function createNoneSigner() { + return function sign() { + return ""; + }; + } + function createNoneVerifier() { + return function verify(thing, signature) { + return signature === ""; + }; + } + module2.exports = function jwa(algorithm) { + var signerFactories = { + hs: createHmacSigner, + rs: createKeySigner, + ps: createPSSKeySigner, + es: createECDSASigner, + none: createNoneSigner + }; + var verifierFactories = { + hs: createHmacVerifier, + rs: createKeyVerifier, + ps: createPSSKeyVerifier, + es: createECDSAVerifer, + none: createNoneVerifier + }; + var match = algorithm.match(/^(RS|PS|ES|HS)(256|384|512)$|^(none)$/i); + if (!match) + throw typeError(MSG_INVALID_ALGORITHM, algorithm); + var algo = (match[1] || match[3]).toLowerCase(); + var bits = match[2]; + return { + sign: signerFactories[algo](bits), + verify: verifierFactories[algo](bits) + }; + }; + } +}); + +// ../../node_modules/jws/lib/tostring.js +var require_tostring = __commonJS({ + "../../node_modules/jws/lib/tostring.js"(exports2, module2) { + var Buffer2 = require("buffer").Buffer; + module2.exports = function toString(obj) { + if (typeof obj === "string") + return obj; + if (typeof obj === "number" || Buffer2.isBuffer(obj)) + return obj.toString(); + return JSON.stringify(obj); + }; + } +}); + +// ../../node_modules/jws/lib/sign-stream.js +var require_sign_stream = __commonJS({ + "../../node_modules/jws/lib/sign-stream.js"(exports2, module2) { + var Buffer2 = require_safe_buffer().Buffer; + var DataStream = require_data_stream(); + var jwa = require_jwa(); + var Stream = require("stream"); + var toString = require_tostring(); + var util = require("util"); + function base64url(string, encoding) { + return Buffer2.from(string, encoding).toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_"); + } + function jwsSecuredInput(header, payload, encoding) { + encoding = encoding || "utf8"; + var encodedHeader = base64url(toString(header), "binary"); + var encodedPayload = base64url(toString(payload), encoding); + return util.format("%s.%s", encodedHeader, encodedPayload); + } + function jwsSign(opts) { + var header = opts.header; + var payload = opts.payload; + var secretOrKey = opts.secret || opts.privateKey; + var encoding = opts.encoding; + var algo = jwa(header.alg); + var securedInput = jwsSecuredInput(header, payload, encoding); + var signature = algo.sign(securedInput, secretOrKey); + return util.format("%s.%s", securedInput, signature); + } + function SignStream(opts) { + var secret = opts.secret || opts.privateKey || opts.key; + var secretStream = new DataStream(secret); + this.readable = true; + this.header = opts.header; + this.encoding = opts.encoding; + this.secret = this.privateKey = this.key = secretStream; + this.payload = new DataStream(opts.payload); + this.secret.once("close", function() { + if (!this.payload.writable && this.readable) + this.sign(); + }.bind(this)); + this.payload.once("close", function() { + if (!this.secret.writable && this.readable) + this.sign(); + }.bind(this)); + } + util.inherits(SignStream, Stream); + SignStream.prototype.sign = function sign() { + try { + var signature = jwsSign({ + header: this.header, + payload: this.payload.buffer, + secret: this.secret.buffer, + encoding: this.encoding + }); + this.emit("done", signature); + this.emit("data", signature); + this.emit("end"); + this.readable = false; + return signature; + } catch (e) { + this.readable = false; + this.emit("error", e); + this.emit("close"); + } + }; + SignStream.sign = jwsSign; + module2.exports = SignStream; + } +}); + +// ../../node_modules/jws/lib/verify-stream.js +var require_verify_stream = __commonJS({ + "../../node_modules/jws/lib/verify-stream.js"(exports2, module2) { + var Buffer2 = require_safe_buffer().Buffer; + var DataStream = require_data_stream(); + var jwa = require_jwa(); + var Stream = require("stream"); + var toString = require_tostring(); + var util = require("util"); + var JWS_REGEX = /^[a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$/; + function isObject(thing) { + return Object.prototype.toString.call(thing) === "[object Object]"; + } + function safeJsonParse(thing) { + if (isObject(thing)) + return thing; + try { + return JSON.parse(thing); + } catch (e) { + return void 0; + } + } + function headerFromJWS(jwsSig) { + var encodedHeader = jwsSig.split(".", 1)[0]; + return safeJsonParse(Buffer2.from(encodedHeader, "base64").toString("binary")); + } + function securedInputFromJWS(jwsSig) { + return jwsSig.split(".", 2).join("."); + } + function signatureFromJWS(jwsSig) { + return jwsSig.split(".")[2]; + } + function payloadFromJWS(jwsSig, encoding) { + encoding = encoding || "utf8"; + var payload = jwsSig.split(".")[1]; + return Buffer2.from(payload, "base64").toString(encoding); + } + function isValidJws(string) { + return JWS_REGEX.test(string) && !!headerFromJWS(string); + } + function jwsVerify(jwsSig, algorithm, secretOrKey) { + if (!algorithm) { + var err = new Error("Missing algorithm parameter for jws.verify"); + err.code = "MISSING_ALGORITHM"; + throw err; + } + jwsSig = toString(jwsSig); + var signature = signatureFromJWS(jwsSig); + var securedInput = securedInputFromJWS(jwsSig); + var algo = jwa(algorithm); + return algo.verify(securedInput, signature, secretOrKey); + } + function jwsDecode(jwsSig, opts) { + opts = opts || {}; + jwsSig = toString(jwsSig); + if (!isValidJws(jwsSig)) + return null; + var header = headerFromJWS(jwsSig); + if (!header) + return null; + var payload = payloadFromJWS(jwsSig); + if (header.typ === "JWT" || opts.json) + payload = JSON.parse(payload, opts.encoding); + return { + header, + payload, + signature: signatureFromJWS(jwsSig) + }; + } + function VerifyStream(opts) { + opts = opts || {}; + var secretOrKey = opts.secret || opts.publicKey || opts.key; + var secretStream = new DataStream(secretOrKey); + this.readable = true; + this.algorithm = opts.algorithm; + this.encoding = opts.encoding; + this.secret = this.publicKey = this.key = secretStream; + this.signature = new DataStream(opts.signature); + this.secret.once("close", function() { + if (!this.signature.writable && this.readable) + this.verify(); + }.bind(this)); + this.signature.once("close", function() { + if (!this.secret.writable && this.readable) + this.verify(); + }.bind(this)); + } + util.inherits(VerifyStream, Stream); + VerifyStream.prototype.verify = function verify() { + try { + var valid = jwsVerify(this.signature.buffer, this.algorithm, this.key.buffer); + var obj = jwsDecode(this.signature.buffer, this.encoding); + this.emit("done", valid, obj); + this.emit("data", valid); + this.emit("end"); + this.readable = false; + return valid; + } catch (e) { + this.readable = false; + this.emit("error", e); + this.emit("close"); + } + }; + VerifyStream.decode = jwsDecode; + VerifyStream.isValid = isValidJws; + VerifyStream.verify = jwsVerify; + module2.exports = VerifyStream; + } +}); + +// ../../node_modules/jws/index.js +var require_jws = __commonJS({ + "../../node_modules/jws/index.js"(exports2) { + var SignStream = require_sign_stream(); + var VerifyStream = require_verify_stream(); + var ALGORITHMS = [ + "HS256", + "HS384", + "HS512", + "RS256", + "RS384", + "RS512", + "PS256", + "PS384", + "PS512", + "ES256", + "ES384", + "ES512" + ]; + exports2.ALGORITHMS = ALGORITHMS; + exports2.sign = SignStream.sign; + exports2.verify = VerifyStream.verify; + exports2.decode = VerifyStream.decode; + exports2.isValid = VerifyStream.isValid; + exports2.createSign = function createSign(opts) { + return new SignStream(opts); + }; + exports2.createVerify = function createVerify(opts) { + return new VerifyStream(opts); + }; + } +}); + +// ../../node_modules/jsonwebtoken/decode.js +var require_decode = __commonJS({ + "../../node_modules/jsonwebtoken/decode.js"(exports2, module2) { + var jws = require_jws(); + module2.exports = function(jwt2, options) { + options = options || {}; + var decoded = jws.decode(jwt2, options); + if (!decoded) { + return null; + } + var payload = decoded.payload; + if (typeof payload === "string") { + try { + var obj = JSON.parse(payload); + if (obj !== null && typeof obj === "object") { + payload = obj; + } + } catch (e) { + } + } + if (options.complete === true) { + return { + header: decoded.header, + payload, + signature: decoded.signature + }; + } + return payload; + }; + } +}); + +// ../../node_modules/jsonwebtoken/lib/JsonWebTokenError.js +var require_JsonWebTokenError = __commonJS({ + "../../node_modules/jsonwebtoken/lib/JsonWebTokenError.js"(exports2, module2) { + var JsonWebTokenError = function(message, error) { + Error.call(this, message); + if (Error.captureStackTrace) { + Error.captureStackTrace(this, this.constructor); + } + this.name = "JsonWebTokenError"; + this.message = message; + if (error) this.inner = error; + }; + JsonWebTokenError.prototype = Object.create(Error.prototype); + JsonWebTokenError.prototype.constructor = JsonWebTokenError; + module2.exports = JsonWebTokenError; + } +}); + +// ../../node_modules/jsonwebtoken/lib/NotBeforeError.js +var require_NotBeforeError = __commonJS({ + "../../node_modules/jsonwebtoken/lib/NotBeforeError.js"(exports2, module2) { + var JsonWebTokenError = require_JsonWebTokenError(); + var NotBeforeError = function(message, date) { + JsonWebTokenError.call(this, message); + this.name = "NotBeforeError"; + this.date = date; + }; + NotBeforeError.prototype = Object.create(JsonWebTokenError.prototype); + NotBeforeError.prototype.constructor = NotBeforeError; + module2.exports = NotBeforeError; + } +}); + +// ../../node_modules/jsonwebtoken/lib/TokenExpiredError.js +var require_TokenExpiredError = __commonJS({ + "../../node_modules/jsonwebtoken/lib/TokenExpiredError.js"(exports2, module2) { + var JsonWebTokenError = require_JsonWebTokenError(); + var TokenExpiredError = function(message, expiredAt) { + JsonWebTokenError.call(this, message); + this.name = "TokenExpiredError"; + this.expiredAt = expiredAt; + }; + TokenExpiredError.prototype = Object.create(JsonWebTokenError.prototype); + TokenExpiredError.prototype.constructor = TokenExpiredError; + module2.exports = TokenExpiredError; + } +}); + +// ../../node_modules/ms/index.js +var require_ms = __commonJS({ + "../../node_modules/ms/index.js"(exports2, module2) { + var s = 1e3; + var m = s * 60; + var h = m * 60; + var d = h * 24; + var w = d * 7; + var y = d * 365.25; + module2.exports = function(val, options) { + options = options || {}; + var type = typeof val; + if (type === "string" && val.length > 0) { + return parse(val); + } else if (type === "number" && isFinite(val)) { + return options.long ? fmtLong(val) : fmtShort(val); + } + throw new Error( + "val is not a non-empty string or a valid number. val=" + JSON.stringify(val) + ); + }; + function parse(str) { + str = String(str); + if (str.length > 100) { + return; + } + var match = /^(-?(?:\d+)?\.?\d+) *(milliseconds?|msecs?|ms|seconds?|secs?|s|minutes?|mins?|m|hours?|hrs?|h|days?|d|weeks?|w|years?|yrs?|y)?$/i.exec( + str + ); + if (!match) { + return; + } + var n = parseFloat(match[1]); + var type = (match[2] || "ms").toLowerCase(); + switch (type) { + case "years": + case "year": + case "yrs": + case "yr": + case "y": + return n * y; + case "weeks": + case "week": + case "w": + return n * w; + case "days": + case "day": + case "d": + return n * d; + case "hours": + case "hour": + case "hrs": + case "hr": + case "h": + return n * h; + case "minutes": + case "minute": + case "mins": + case "min": + case "m": + return n * m; + case "seconds": + case "second": + case "secs": + case "sec": + case "s": + return n * s; + case "milliseconds": + case "millisecond": + case "msecs": + case "msec": + case "ms": + return n; + default: + return void 0; + } + } + function fmtShort(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return Math.round(ms / d) + "d"; + } + if (msAbs >= h) { + return Math.round(ms / h) + "h"; + } + if (msAbs >= m) { + return Math.round(ms / m) + "m"; + } + if (msAbs >= s) { + return Math.round(ms / s) + "s"; + } + return ms + "ms"; + } + function fmtLong(ms) { + var msAbs = Math.abs(ms); + if (msAbs >= d) { + return plural(ms, msAbs, d, "day"); + } + if (msAbs >= h) { + return plural(ms, msAbs, h, "hour"); + } + if (msAbs >= m) { + return plural(ms, msAbs, m, "minute"); + } + if (msAbs >= s) { + return plural(ms, msAbs, s, "second"); + } + return ms + " ms"; + } + function plural(ms, msAbs, n, name) { + var isPlural = msAbs >= n * 1.5; + return Math.round(ms / n) + " " + name + (isPlural ? "s" : ""); + } + } +}); + +// ../../node_modules/jsonwebtoken/lib/timespan.js +var require_timespan = __commonJS({ + "../../node_modules/jsonwebtoken/lib/timespan.js"(exports2, module2) { + var ms = require_ms(); + module2.exports = function(time, iat) { + var timestamp = iat || Math.floor(Date.now() / 1e3); + if (typeof time === "string") { + var milliseconds = ms(time); + if (typeof milliseconds === "undefined") { + return; + } + return Math.floor(timestamp + milliseconds / 1e3); + } else if (typeof time === "number") { + return timestamp + time; + } else { + return; + } + }; + } +}); + +// ../../node_modules/semver/internal/constants.js +var require_constants = __commonJS({ + "../../node_modules/semver/internal/constants.js"(exports2, module2) { + var SEMVER_SPEC_VERSION = "2.0.0"; + var MAX_LENGTH = 256; + var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER || /* istanbul ignore next */ + 9007199254740991; + var MAX_SAFE_COMPONENT_LENGTH = 16; + var MAX_SAFE_BUILD_LENGTH = MAX_LENGTH - 6; + var RELEASE_TYPES = [ + "major", + "premajor", + "minor", + "preminor", + "patch", + "prepatch", + "prerelease" + ]; + module2.exports = { + MAX_LENGTH, + MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, + MAX_SAFE_INTEGER, + RELEASE_TYPES, + SEMVER_SPEC_VERSION, + FLAG_INCLUDE_PRERELEASE: 1, + FLAG_LOOSE: 2 + }; + } +}); + +// ../../node_modules/semver/internal/debug.js +var require_debug = __commonJS({ + "../../node_modules/semver/internal/debug.js"(exports2, module2) { + var debug = typeof process === "object" && process.env && process.env.NODE_DEBUG && /\bsemver\b/i.test(process.env.NODE_DEBUG) ? (...args) => console.error("SEMVER", ...args) : () => { + }; + module2.exports = debug; + } +}); + +// ../../node_modules/semver/internal/re.js +var require_re = __commonJS({ + "../../node_modules/semver/internal/re.js"(exports2, module2) { + var { + MAX_SAFE_COMPONENT_LENGTH, + MAX_SAFE_BUILD_LENGTH, + MAX_LENGTH + } = require_constants(); + var debug = require_debug(); + exports2 = module2.exports = {}; + var re = exports2.re = []; + var safeRe = exports2.safeRe = []; + var src = exports2.src = []; + var t = exports2.t = {}; + var R = 0; + var LETTERDASHNUMBER = "[a-zA-Z0-9-]"; + var safeRegexReplacements = [ + ["\\s", 1], + ["\\d", MAX_LENGTH], + [LETTERDASHNUMBER, MAX_SAFE_BUILD_LENGTH] + ]; + var makeSafeRegex = (value) => { + for (const [token, max] of safeRegexReplacements) { + value = value.split(`${token}*`).join(`${token}{0,${max}}`).split(`${token}+`).join(`${token}{1,${max}}`); + } + return value; + }; + var createToken = (name, value, isGlobal) => { + const safe = makeSafeRegex(value); + const index = R++; + debug(name, index, value); + t[name] = index; + src[index] = value; + re[index] = new RegExp(value, isGlobal ? "g" : void 0); + safeRe[index] = new RegExp(safe, isGlobal ? "g" : void 0); + }; + createToken("NUMERICIDENTIFIER", "0|[1-9]\\d*"); + createToken("NUMERICIDENTIFIERLOOSE", "\\d+"); + createToken("NONNUMERICIDENTIFIER", `\\d*[a-zA-Z-]${LETTERDASHNUMBER}*`); + createToken("MAINVERSION", `(${src[t.NUMERICIDENTIFIER]})\\.(${src[t.NUMERICIDENTIFIER]})\\.(${src[t.NUMERICIDENTIFIER]})`); + createToken("MAINVERSIONLOOSE", `(${src[t.NUMERICIDENTIFIERLOOSE]})\\.(${src[t.NUMERICIDENTIFIERLOOSE]})\\.(${src[t.NUMERICIDENTIFIERLOOSE]})`); + createToken("PRERELEASEIDENTIFIER", `(?:${src[t.NUMERICIDENTIFIER]}|${src[t.NONNUMERICIDENTIFIER]})`); + createToken("PRERELEASEIDENTIFIERLOOSE", `(?:${src[t.NUMERICIDENTIFIERLOOSE]}|${src[t.NONNUMERICIDENTIFIER]})`); + createToken("PRERELEASE", `(?:-(${src[t.PRERELEASEIDENTIFIER]}(?:\\.${src[t.PRERELEASEIDENTIFIER]})*))`); + createToken("PRERELEASELOOSE", `(?:-?(${src[t.PRERELEASEIDENTIFIERLOOSE]}(?:\\.${src[t.PRERELEASEIDENTIFIERLOOSE]})*))`); + createToken("BUILDIDENTIFIER", `${LETTERDASHNUMBER}+`); + createToken("BUILD", `(?:\\+(${src[t.BUILDIDENTIFIER]}(?:\\.${src[t.BUILDIDENTIFIER]})*))`); + createToken("FULLPLAIN", `v?${src[t.MAINVERSION]}${src[t.PRERELEASE]}?${src[t.BUILD]}?`); + createToken("FULL", `^${src[t.FULLPLAIN]}$`); + createToken("LOOSEPLAIN", `[v=\\s]*${src[t.MAINVERSIONLOOSE]}${src[t.PRERELEASELOOSE]}?${src[t.BUILD]}?`); + createToken("LOOSE", `^${src[t.LOOSEPLAIN]}$`); + createToken("GTLT", "((?:<|>)?=?)"); + createToken("XRANGEIDENTIFIERLOOSE", `${src[t.NUMERICIDENTIFIERLOOSE]}|x|X|\\*`); + createToken("XRANGEIDENTIFIER", `${src[t.NUMERICIDENTIFIER]}|x|X|\\*`); + createToken("XRANGEPLAIN", `[v=\\s]*(${src[t.XRANGEIDENTIFIER]})(?:\\.(${src[t.XRANGEIDENTIFIER]})(?:\\.(${src[t.XRANGEIDENTIFIER]})(?:${src[t.PRERELEASE]})?${src[t.BUILD]}?)?)?`); + createToken("XRANGEPLAINLOOSE", `[v=\\s]*(${src[t.XRANGEIDENTIFIERLOOSE]})(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})(?:\\.(${src[t.XRANGEIDENTIFIERLOOSE]})(?:${src[t.PRERELEASELOOSE]})?${src[t.BUILD]}?)?)?`); + createToken("XRANGE", `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAIN]}$`); + createToken("XRANGELOOSE", `^${src[t.GTLT]}\\s*${src[t.XRANGEPLAINLOOSE]}$`); + createToken("COERCEPLAIN", `${"(^|[^\\d])(\\d{1,"}${MAX_SAFE_COMPONENT_LENGTH}})(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?(?:\\.(\\d{1,${MAX_SAFE_COMPONENT_LENGTH}}))?`); + createToken("COERCE", `${src[t.COERCEPLAIN]}(?:$|[^\\d])`); + createToken("COERCEFULL", src[t.COERCEPLAIN] + `(?:${src[t.PRERELEASE]})?(?:${src[t.BUILD]})?(?:$|[^\\d])`); + createToken("COERCERTL", src[t.COERCE], true); + createToken("COERCERTLFULL", src[t.COERCEFULL], true); + createToken("LONETILDE", "(?:~>?)"); + createToken("TILDETRIM", `(\\s*)${src[t.LONETILDE]}\\s+`, true); + exports2.tildeTrimReplace = "$1~"; + createToken("TILDE", `^${src[t.LONETILDE]}${src[t.XRANGEPLAIN]}$`); + createToken("TILDELOOSE", `^${src[t.LONETILDE]}${src[t.XRANGEPLAINLOOSE]}$`); + createToken("LONECARET", "(?:\\^)"); + createToken("CARETTRIM", `(\\s*)${src[t.LONECARET]}\\s+`, true); + exports2.caretTrimReplace = "$1^"; + createToken("CARET", `^${src[t.LONECARET]}${src[t.XRANGEPLAIN]}$`); + createToken("CARETLOOSE", `^${src[t.LONECARET]}${src[t.XRANGEPLAINLOOSE]}$`); + createToken("COMPARATORLOOSE", `^${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]})$|^$`); + createToken("COMPARATOR", `^${src[t.GTLT]}\\s*(${src[t.FULLPLAIN]})$|^$`); + createToken("COMPARATORTRIM", `(\\s*)${src[t.GTLT]}\\s*(${src[t.LOOSEPLAIN]}|${src[t.XRANGEPLAIN]})`, true); + exports2.comparatorTrimReplace = "$1$2$3"; + createToken("HYPHENRANGE", `^\\s*(${src[t.XRANGEPLAIN]})\\s+-\\s+(${src[t.XRANGEPLAIN]})\\s*$`); + createToken("HYPHENRANGELOOSE", `^\\s*(${src[t.XRANGEPLAINLOOSE]})\\s+-\\s+(${src[t.XRANGEPLAINLOOSE]})\\s*$`); + createToken("STAR", "(<|>)?=?\\s*\\*"); + createToken("GTE0", "^\\s*>=\\s*0\\.0\\.0\\s*$"); + createToken("GTE0PRE", "^\\s*>=\\s*0\\.0\\.0-0\\s*$"); + } +}); + +// ../../node_modules/semver/internal/parse-options.js +var require_parse_options = __commonJS({ + "../../node_modules/semver/internal/parse-options.js"(exports2, module2) { + var looseOption = Object.freeze({ loose: true }); + var emptyOpts = Object.freeze({}); + var parseOptions = (options) => { + if (!options) { + return emptyOpts; + } + if (typeof options !== "object") { + return looseOption; + } + return options; + }; + module2.exports = parseOptions; + } +}); + +// ../../node_modules/semver/internal/identifiers.js +var require_identifiers = __commonJS({ + "../../node_modules/semver/internal/identifiers.js"(exports2, module2) { + var numeric = /^[0-9]+$/; + var compareIdentifiers = (a, b) => { + const anum = numeric.test(a); + const bnum = numeric.test(b); + if (anum && bnum) { + a = +a; + b = +b; + } + return a === b ? 0 : anum && !bnum ? -1 : bnum && !anum ? 1 : a < b ? -1 : 1; + }; + var rcompareIdentifiers = (a, b) => compareIdentifiers(b, a); + module2.exports = { + compareIdentifiers, + rcompareIdentifiers + }; + } +}); + +// ../../node_modules/semver/classes/semver.js +var require_semver = __commonJS({ + "../../node_modules/semver/classes/semver.js"(exports2, module2) { + var debug = require_debug(); + var { MAX_LENGTH, MAX_SAFE_INTEGER } = require_constants(); + var { safeRe: re, t } = require_re(); + var parseOptions = require_parse_options(); + var { compareIdentifiers } = require_identifiers(); + var SemVer = class _SemVer { + constructor(version, options) { + options = parseOptions(options); + if (version instanceof _SemVer) { + if (version.loose === !!options.loose && version.includePrerelease === !!options.includePrerelease) { + return version; + } else { + version = version.version; + } + } else if (typeof version !== "string") { + throw new TypeError(`Invalid version. Must be a string. Got type "${typeof version}".`); + } + if (version.length > MAX_LENGTH) { + throw new TypeError( + `version is longer than ${MAX_LENGTH} characters` + ); + } + debug("SemVer", version, options); + this.options = options; + this.loose = !!options.loose; + this.includePrerelease = !!options.includePrerelease; + const m = version.trim().match(options.loose ? re[t.LOOSE] : re[t.FULL]); + if (!m) { + throw new TypeError(`Invalid Version: ${version}`); + } + this.raw = version; + this.major = +m[1]; + this.minor = +m[2]; + this.patch = +m[3]; + if (this.major > MAX_SAFE_INTEGER || this.major < 0) { + throw new TypeError("Invalid major version"); + } + if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) { + throw new TypeError("Invalid minor version"); + } + if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) { + throw new TypeError("Invalid patch version"); + } + if (!m[4]) { + this.prerelease = []; + } else { + this.prerelease = m[4].split(".").map((id) => { + if (/^[0-9]+$/.test(id)) { + const num = +id; + if (num >= 0 && num < MAX_SAFE_INTEGER) { + return num; + } + } + return id; + }); + } + this.build = m[5] ? m[5].split(".") : []; + this.format(); + } + format() { + this.version = `${this.major}.${this.minor}.${this.patch}`; + if (this.prerelease.length) { + this.version += `-${this.prerelease.join(".")}`; + } + return this.version; + } + toString() { + return this.version; + } + compare(other) { + debug("SemVer.compare", this.version, this.options, other); + if (!(other instanceof _SemVer)) { + if (typeof other === "string" && other === this.version) { + return 0; + } + other = new _SemVer(other, this.options); + } + if (other.version === this.version) { + return 0; + } + return this.compareMain(other) || this.comparePre(other); + } + compareMain(other) { + if (!(other instanceof _SemVer)) { + other = new _SemVer(other, this.options); + } + return compareIdentifiers(this.major, other.major) || compareIdentifiers(this.minor, other.minor) || compareIdentifiers(this.patch, other.patch); + } + comparePre(other) { + if (!(other instanceof _SemVer)) { + other = new _SemVer(other, this.options); + } + if (this.prerelease.length && !other.prerelease.length) { + return -1; + } else if (!this.prerelease.length && other.prerelease.length) { + return 1; + } else if (!this.prerelease.length && !other.prerelease.length) { + return 0; + } + let i = 0; + do { + const a = this.prerelease[i]; + const b = other.prerelease[i]; + debug("prerelease compare", i, a, b); + if (a === void 0 && b === void 0) { + return 0; + } else if (b === void 0) { + return 1; + } else if (a === void 0) { + return -1; + } else if (a === b) { + continue; + } else { + return compareIdentifiers(a, b); + } + } while (++i); + } + compareBuild(other) { + if (!(other instanceof _SemVer)) { + other = new _SemVer(other, this.options); + } + let i = 0; + do { + const a = this.build[i]; + const b = other.build[i]; + debug("build compare", i, a, b); + if (a === void 0 && b === void 0) { + return 0; + } else if (b === void 0) { + return 1; + } else if (a === void 0) { + return -1; + } else if (a === b) { + continue; + } else { + return compareIdentifiers(a, b); + } + } while (++i); + } + // preminor will bump the version up to the next minor release, and immediately + // down to pre-release. premajor and prepatch work the same way. + inc(release, identifier, identifierBase) { + switch (release) { + case "premajor": + this.prerelease.length = 0; + this.patch = 0; + this.minor = 0; + this.major++; + this.inc("pre", identifier, identifierBase); + break; + case "preminor": + this.prerelease.length = 0; + this.patch = 0; + this.minor++; + this.inc("pre", identifier, identifierBase); + break; + case "prepatch": + this.prerelease.length = 0; + this.inc("patch", identifier, identifierBase); + this.inc("pre", identifier, identifierBase); + break; + case "prerelease": + if (this.prerelease.length === 0) { + this.inc("patch", identifier, identifierBase); + } + this.inc("pre", identifier, identifierBase); + break; + case "major": + if (this.minor !== 0 || this.patch !== 0 || this.prerelease.length === 0) { + this.major++; + } + this.minor = 0; + this.patch = 0; + this.prerelease = []; + break; + case "minor": + if (this.patch !== 0 || this.prerelease.length === 0) { + this.minor++; + } + this.patch = 0; + this.prerelease = []; + break; + case "patch": + if (this.prerelease.length === 0) { + this.patch++; + } + this.prerelease = []; + break; + case "pre": { + const base = Number(identifierBase) ? 1 : 0; + if (!identifier && identifierBase === false) { + throw new Error("invalid increment argument: identifier is empty"); + } + if (this.prerelease.length === 0) { + this.prerelease = [base]; + } else { + let i = this.prerelease.length; + while (--i >= 0) { + if (typeof this.prerelease[i] === "number") { + this.prerelease[i]++; + i = -2; + } + } + if (i === -1) { + if (identifier === this.prerelease.join(".") && identifierBase === false) { + throw new Error("invalid increment argument: identifier already exists"); + } + this.prerelease.push(base); + } + } + if (identifier) { + let prerelease = [identifier, base]; + if (identifierBase === false) { + prerelease = [identifier]; + } + if (compareIdentifiers(this.prerelease[0], identifier) === 0) { + if (isNaN(this.prerelease[1])) { + this.prerelease = prerelease; + } + } else { + this.prerelease = prerelease; + } + } + break; + } + default: + throw new Error(`invalid increment argument: ${release}`); + } + this.raw = this.format(); + if (this.build.length) { + this.raw += `+${this.build.join(".")}`; + } + return this; + } + }; + module2.exports = SemVer; + } +}); + +// ../../node_modules/semver/functions/parse.js +var require_parse = __commonJS({ + "../../node_modules/semver/functions/parse.js"(exports2, module2) { + var SemVer = require_semver(); + var parse = (version, options, throwErrors = false) => { + if (version instanceof SemVer) { + return version; + } + try { + return new SemVer(version, options); + } catch (er) { + if (!throwErrors) { + return null; + } + throw er; + } + }; + module2.exports = parse; + } +}); + +// ../../node_modules/semver/functions/valid.js +var require_valid = __commonJS({ + "../../node_modules/semver/functions/valid.js"(exports2, module2) { + var parse = require_parse(); + var valid = (version, options) => { + const v = parse(version, options); + return v ? v.version : null; + }; + module2.exports = valid; + } +}); + +// ../../node_modules/semver/functions/clean.js +var require_clean = __commonJS({ + "../../node_modules/semver/functions/clean.js"(exports2, module2) { + var parse = require_parse(); + var clean = (version, options) => { + const s = parse(version.trim().replace(/^[=v]+/, ""), options); + return s ? s.version : null; + }; + module2.exports = clean; + } +}); + +// ../../node_modules/semver/functions/inc.js +var require_inc = __commonJS({ + "../../node_modules/semver/functions/inc.js"(exports2, module2) { + var SemVer = require_semver(); + var inc = (version, release, options, identifier, identifierBase) => { + if (typeof options === "string") { + identifierBase = identifier; + identifier = options; + options = void 0; + } + try { + return new SemVer( + version instanceof SemVer ? version.version : version, + options + ).inc(release, identifier, identifierBase).version; + } catch (er) { + return null; + } + }; + module2.exports = inc; + } +}); + +// ../../node_modules/semver/functions/diff.js +var require_diff = __commonJS({ + "../../node_modules/semver/functions/diff.js"(exports2, module2) { + var parse = require_parse(); + var diff = (version1, version2) => { + const v1 = parse(version1, null, true); + const v2 = parse(version2, null, true); + const comparison = v1.compare(v2); + if (comparison === 0) { + return null; + } + const v1Higher = comparison > 0; + const highVersion = v1Higher ? v1 : v2; + const lowVersion = v1Higher ? v2 : v1; + const highHasPre = !!highVersion.prerelease.length; + const lowHasPre = !!lowVersion.prerelease.length; + if (lowHasPre && !highHasPre) { + if (!lowVersion.patch && !lowVersion.minor) { + return "major"; + } + if (highVersion.patch) { + return "patch"; + } + if (highVersion.minor) { + return "minor"; + } + return "major"; + } + const prefix = highHasPre ? "pre" : ""; + if (v1.major !== v2.major) { + return prefix + "major"; + } + if (v1.minor !== v2.minor) { + return prefix + "minor"; + } + if (v1.patch !== v2.patch) { + return prefix + "patch"; + } + return "prerelease"; + }; + module2.exports = diff; + } +}); + +// ../../node_modules/semver/functions/major.js +var require_major = __commonJS({ + "../../node_modules/semver/functions/major.js"(exports2, module2) { + var SemVer = require_semver(); + var major = (a, loose) => new SemVer(a, loose).major; + module2.exports = major; + } +}); + +// ../../node_modules/semver/functions/minor.js +var require_minor = __commonJS({ + "../../node_modules/semver/functions/minor.js"(exports2, module2) { + var SemVer = require_semver(); + var minor = (a, loose) => new SemVer(a, loose).minor; + module2.exports = minor; + } +}); + +// ../../node_modules/semver/functions/patch.js +var require_patch = __commonJS({ + "../../node_modules/semver/functions/patch.js"(exports2, module2) { + var SemVer = require_semver(); + var patch = (a, loose) => new SemVer(a, loose).patch; + module2.exports = patch; + } +}); + +// ../../node_modules/semver/functions/prerelease.js +var require_prerelease = __commonJS({ + "../../node_modules/semver/functions/prerelease.js"(exports2, module2) { + var parse = require_parse(); + var prerelease = (version, options) => { + const parsed = parse(version, options); + return parsed && parsed.prerelease.length ? parsed.prerelease : null; + }; + module2.exports = prerelease; + } +}); + +// ../../node_modules/semver/functions/compare.js +var require_compare = __commonJS({ + "../../node_modules/semver/functions/compare.js"(exports2, module2) { + var SemVer = require_semver(); + var compare = (a, b, loose) => new SemVer(a, loose).compare(new SemVer(b, loose)); + module2.exports = compare; + } +}); + +// ../../node_modules/semver/functions/rcompare.js +var require_rcompare = __commonJS({ + "../../node_modules/semver/functions/rcompare.js"(exports2, module2) { + var compare = require_compare(); + var rcompare = (a, b, loose) => compare(b, a, loose); + module2.exports = rcompare; + } +}); + +// ../../node_modules/semver/functions/compare-loose.js +var require_compare_loose = __commonJS({ + "../../node_modules/semver/functions/compare-loose.js"(exports2, module2) { + var compare = require_compare(); + var compareLoose = (a, b) => compare(a, b, true); + module2.exports = compareLoose; + } +}); + +// ../../node_modules/semver/functions/compare-build.js +var require_compare_build = __commonJS({ + "../../node_modules/semver/functions/compare-build.js"(exports2, module2) { + var SemVer = require_semver(); + var compareBuild = (a, b, loose) => { + const versionA = new SemVer(a, loose); + const versionB = new SemVer(b, loose); + return versionA.compare(versionB) || versionA.compareBuild(versionB); + }; + module2.exports = compareBuild; + } +}); + +// ../../node_modules/semver/functions/sort.js +var require_sort = __commonJS({ + "../../node_modules/semver/functions/sort.js"(exports2, module2) { + var compareBuild = require_compare_build(); + var sort = (list, loose) => list.sort((a, b) => compareBuild(a, b, loose)); + module2.exports = sort; + } +}); + +// ../../node_modules/semver/functions/rsort.js +var require_rsort = __commonJS({ + "../../node_modules/semver/functions/rsort.js"(exports2, module2) { + var compareBuild = require_compare_build(); + var rsort = (list, loose) => list.sort((a, b) => compareBuild(b, a, loose)); + module2.exports = rsort; + } +}); + +// ../../node_modules/semver/functions/gt.js +var require_gt = __commonJS({ + "../../node_modules/semver/functions/gt.js"(exports2, module2) { + var compare = require_compare(); + var gt = (a, b, loose) => compare(a, b, loose) > 0; + module2.exports = gt; + } +}); + +// ../../node_modules/semver/functions/lt.js +var require_lt = __commonJS({ + "../../node_modules/semver/functions/lt.js"(exports2, module2) { + var compare = require_compare(); + var lt = (a, b, loose) => compare(a, b, loose) < 0; + module2.exports = lt; + } +}); + +// ../../node_modules/semver/functions/eq.js +var require_eq = __commonJS({ + "../../node_modules/semver/functions/eq.js"(exports2, module2) { + var compare = require_compare(); + var eq = (a, b, loose) => compare(a, b, loose) === 0; + module2.exports = eq; + } +}); + +// ../../node_modules/semver/functions/neq.js +var require_neq = __commonJS({ + "../../node_modules/semver/functions/neq.js"(exports2, module2) { + var compare = require_compare(); + var neq = (a, b, loose) => compare(a, b, loose) !== 0; + module2.exports = neq; + } +}); + +// ../../node_modules/semver/functions/gte.js +var require_gte = __commonJS({ + "../../node_modules/semver/functions/gte.js"(exports2, module2) { + var compare = require_compare(); + var gte = (a, b, loose) => compare(a, b, loose) >= 0; + module2.exports = gte; + } +}); + +// ../../node_modules/semver/functions/lte.js +var require_lte = __commonJS({ + "../../node_modules/semver/functions/lte.js"(exports2, module2) { + var compare = require_compare(); + var lte = (a, b, loose) => compare(a, b, loose) <= 0; + module2.exports = lte; + } +}); + +// ../../node_modules/semver/functions/cmp.js +var require_cmp = __commonJS({ + "../../node_modules/semver/functions/cmp.js"(exports2, module2) { + var eq = require_eq(); + var neq = require_neq(); + var gt = require_gt(); + var gte = require_gte(); + var lt = require_lt(); + var lte = require_lte(); + var cmp = (a, op, b, loose) => { + switch (op) { + case "===": + if (typeof a === "object") { + a = a.version; + } + if (typeof b === "object") { + b = b.version; + } + return a === b; + case "!==": + if (typeof a === "object") { + a = a.version; + } + if (typeof b === "object") { + b = b.version; + } + return a !== b; + case "": + case "=": + case "==": + return eq(a, b, loose); + case "!=": + return neq(a, b, loose); + case ">": + return gt(a, b, loose); + case ">=": + return gte(a, b, loose); + case "<": + return lt(a, b, loose); + case "<=": + return lte(a, b, loose); + default: + throw new TypeError(`Invalid operator: ${op}`); + } + }; + module2.exports = cmp; + } +}); + +// ../../node_modules/semver/functions/coerce.js +var require_coerce = __commonJS({ + "../../node_modules/semver/functions/coerce.js"(exports2, module2) { + var SemVer = require_semver(); + var parse = require_parse(); + var { safeRe: re, t } = require_re(); + var coerce = (version, options) => { + if (version instanceof SemVer) { + return version; + } + if (typeof version === "number") { + version = String(version); + } + if (typeof version !== "string") { + return null; + } + options = options || {}; + let match = null; + if (!options.rtl) { + match = version.match(options.includePrerelease ? re[t.COERCEFULL] : re[t.COERCE]); + } else { + const coerceRtlRegex = options.includePrerelease ? re[t.COERCERTLFULL] : re[t.COERCERTL]; + let next; + while ((next = coerceRtlRegex.exec(version)) && (!match || match.index + match[0].length !== version.length)) { + if (!match || next.index + next[0].length !== match.index + match[0].length) { + match = next; + } + coerceRtlRegex.lastIndex = next.index + next[1].length + next[2].length; + } + coerceRtlRegex.lastIndex = -1; + } + if (match === null) { + return null; + } + const major = match[2]; + const minor = match[3] || "0"; + const patch = match[4] || "0"; + const prerelease = options.includePrerelease && match[5] ? `-${match[5]}` : ""; + const build = options.includePrerelease && match[6] ? `+${match[6]}` : ""; + return parse(`${major}.${minor}.${patch}${prerelease}${build}`, options); + }; + module2.exports = coerce; + } +}); + +// ../../node_modules/semver/internal/lrucache.js +var require_lrucache = __commonJS({ + "../../node_modules/semver/internal/lrucache.js"(exports2, module2) { + var LRUCache = class { + constructor() { + this.max = 1e3; + this.map = /* @__PURE__ */ new Map(); + } + get(key) { + const value = this.map.get(key); + if (value === void 0) { + return void 0; + } else { + this.map.delete(key); + this.map.set(key, value); + return value; + } + } + delete(key) { + return this.map.delete(key); + } + set(key, value) { + const deleted = this.delete(key); + if (!deleted && value !== void 0) { + if (this.map.size >= this.max) { + const firstKey = this.map.keys().next().value; + this.delete(firstKey); + } + this.map.set(key, value); + } + return this; + } + }; + module2.exports = LRUCache; + } +}); + +// ../../node_modules/semver/classes/range.js +var require_range = __commonJS({ + "../../node_modules/semver/classes/range.js"(exports2, module2) { + var SPACE_CHARACTERS = /\s+/g; + var Range = class _Range { + constructor(range, options) { + options = parseOptions(options); + if (range instanceof _Range) { + if (range.loose === !!options.loose && range.includePrerelease === !!options.includePrerelease) { + return range; + } else { + return new _Range(range.raw, options); + } + } + if (range instanceof Comparator) { + this.raw = range.value; + this.set = [[range]]; + this.formatted = void 0; + return this; + } + this.options = options; + this.loose = !!options.loose; + this.includePrerelease = !!options.includePrerelease; + this.raw = range.trim().replace(SPACE_CHARACTERS, " "); + this.set = this.raw.split("||").map((r) => this.parseRange(r.trim())).filter((c) => c.length); + if (!this.set.length) { + throw new TypeError(`Invalid SemVer Range: ${this.raw}`); + } + if (this.set.length > 1) { + const first = this.set[0]; + this.set = this.set.filter((c) => !isNullSet(c[0])); + if (this.set.length === 0) { + this.set = [first]; + } else if (this.set.length > 1) { + for (const c of this.set) { + if (c.length === 1 && isAny(c[0])) { + this.set = [c]; + break; + } + } + } + } + this.formatted = void 0; + } + get range() { + if (this.formatted === void 0) { + this.formatted = ""; + for (let i = 0; i < this.set.length; i++) { + if (i > 0) { + this.formatted += "||"; + } + const comps = this.set[i]; + for (let k = 0; k < comps.length; k++) { + if (k > 0) { + this.formatted += " "; + } + this.formatted += comps[k].toString().trim(); + } + } + } + return this.formatted; + } + format() { + return this.range; + } + toString() { + return this.range; + } + parseRange(range) { + const memoOpts = (this.options.includePrerelease && FLAG_INCLUDE_PRERELEASE) | (this.options.loose && FLAG_LOOSE); + const memoKey = memoOpts + ":" + range; + const cached = cache.get(memoKey); + if (cached) { + return cached; + } + const loose = this.options.loose; + const hr = loose ? re[t.HYPHENRANGELOOSE] : re[t.HYPHENRANGE]; + range = range.replace(hr, hyphenReplace(this.options.includePrerelease)); + debug("hyphen replace", range); + range = range.replace(re[t.COMPARATORTRIM], comparatorTrimReplace); + debug("comparator trim", range); + range = range.replace(re[t.TILDETRIM], tildeTrimReplace); + debug("tilde trim", range); + range = range.replace(re[t.CARETTRIM], caretTrimReplace); + debug("caret trim", range); + let rangeList = range.split(" ").map((comp) => parseComparator(comp, this.options)).join(" ").split(/\s+/).map((comp) => replaceGTE0(comp, this.options)); + if (loose) { + rangeList = rangeList.filter((comp) => { + debug("loose invalid filter", comp, this.options); + return !!comp.match(re[t.COMPARATORLOOSE]); + }); + } + debug("range list", rangeList); + const rangeMap = /* @__PURE__ */ new Map(); + const comparators = rangeList.map((comp) => new Comparator(comp, this.options)); + for (const comp of comparators) { + if (isNullSet(comp)) { + return [comp]; + } + rangeMap.set(comp.value, comp); + } + if (rangeMap.size > 1 && rangeMap.has("")) { + rangeMap.delete(""); + } + const result = [...rangeMap.values()]; + cache.set(memoKey, result); + return result; + } + intersects(range, options) { + if (!(range instanceof _Range)) { + throw new TypeError("a Range is required"); + } + return this.set.some((thisComparators) => { + return isSatisfiable(thisComparators, options) && range.set.some((rangeComparators) => { + return isSatisfiable(rangeComparators, options) && thisComparators.every((thisComparator) => { + return rangeComparators.every((rangeComparator) => { + return thisComparator.intersects(rangeComparator, options); + }); + }); + }); + }); + } + // if ANY of the sets match ALL of its comparators, then pass + test(version) { + if (!version) { + return false; + } + if (typeof version === "string") { + try { + version = new SemVer(version, this.options); + } catch (er) { + return false; + } + } + for (let i = 0; i < this.set.length; i++) { + if (testSet(this.set[i], version, this.options)) { + return true; + } + } + return false; + } + }; + module2.exports = Range; + var LRU = require_lrucache(); + var cache = new LRU(); + var parseOptions = require_parse_options(); + var Comparator = require_comparator(); + var debug = require_debug(); + var SemVer = require_semver(); + var { + safeRe: re, + t, + comparatorTrimReplace, + tildeTrimReplace, + caretTrimReplace + } = require_re(); + var { FLAG_INCLUDE_PRERELEASE, FLAG_LOOSE } = require_constants(); + var isNullSet = (c) => c.value === "<0.0.0-0"; + var isAny = (c) => c.value === ""; + var isSatisfiable = (comparators, options) => { + let result = true; + const remainingComparators = comparators.slice(); + let testComparator = remainingComparators.pop(); + while (result && remainingComparators.length) { + result = remainingComparators.every((otherComparator) => { + return testComparator.intersects(otherComparator, options); + }); + testComparator = remainingComparators.pop(); + } + return result; + }; + var parseComparator = (comp, options) => { + debug("comp", comp, options); + comp = replaceCarets(comp, options); + debug("caret", comp); + comp = replaceTildes(comp, options); + debug("tildes", comp); + comp = replaceXRanges(comp, options); + debug("xrange", comp); + comp = replaceStars(comp, options); + debug("stars", comp); + return comp; + }; + var isX = (id) => !id || id.toLowerCase() === "x" || id === "*"; + var replaceTildes = (comp, options) => { + return comp.trim().split(/\s+/).map((c) => replaceTilde(c, options)).join(" "); + }; + var replaceTilde = (comp, options) => { + const r = options.loose ? re[t.TILDELOOSE] : re[t.TILDE]; + return comp.replace(r, (_, M, m, p, pr) => { + debug("tilde", comp, _, M, m, p, pr); + let ret; + if (isX(M)) { + ret = ""; + } else if (isX(m)) { + ret = `>=${M}.0.0 <${+M + 1}.0.0-0`; + } else if (isX(p)) { + ret = `>=${M}.${m}.0 <${M}.${+m + 1}.0-0`; + } else if (pr) { + debug("replaceTilde pr", pr); + ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`; + } else { + ret = `>=${M}.${m}.${p} <${M}.${+m + 1}.0-0`; + } + debug("tilde return", ret); + return ret; + }); + }; + var replaceCarets = (comp, options) => { + return comp.trim().split(/\s+/).map((c) => replaceCaret(c, options)).join(" "); + }; + var replaceCaret = (comp, options) => { + debug("caret", comp, options); + const r = options.loose ? re[t.CARETLOOSE] : re[t.CARET]; + const z = options.includePrerelease ? "-0" : ""; + return comp.replace(r, (_, M, m, p, pr) => { + debug("caret", comp, _, M, m, p, pr); + let ret; + if (isX(M)) { + ret = ""; + } else if (isX(m)) { + ret = `>=${M}.0.0${z} <${+M + 1}.0.0-0`; + } else if (isX(p)) { + if (M === "0") { + ret = `>=${M}.${m}.0${z} <${M}.${+m + 1}.0-0`; + } else { + ret = `>=${M}.${m}.0${z} <${+M + 1}.0.0-0`; + } + } else if (pr) { + debug("replaceCaret pr", pr); + if (M === "0") { + if (m === "0") { + ret = `>=${M}.${m}.${p}-${pr} <${M}.${m}.${+p + 1}-0`; + } else { + ret = `>=${M}.${m}.${p}-${pr} <${M}.${+m + 1}.0-0`; + } + } else { + ret = `>=${M}.${m}.${p}-${pr} <${+M + 1}.0.0-0`; + } + } else { + debug("no pr"); + if (M === "0") { + if (m === "0") { + ret = `>=${M}.${m}.${p}${z} <${M}.${m}.${+p + 1}-0`; + } else { + ret = `>=${M}.${m}.${p}${z} <${M}.${+m + 1}.0-0`; + } + } else { + ret = `>=${M}.${m}.${p} <${+M + 1}.0.0-0`; + } + } + debug("caret return", ret); + return ret; + }); + }; + var replaceXRanges = (comp, options) => { + debug("replaceXRanges", comp, options); + return comp.split(/\s+/).map((c) => replaceXRange(c, options)).join(" "); + }; + var replaceXRange = (comp, options) => { + comp = comp.trim(); + const r = options.loose ? re[t.XRANGELOOSE] : re[t.XRANGE]; + return comp.replace(r, (ret, gtlt, M, m, p, pr) => { + debug("xRange", comp, ret, gtlt, M, m, p, pr); + const xM = isX(M); + const xm = xM || isX(m); + const xp = xm || isX(p); + const anyX = xp; + if (gtlt === "=" && anyX) { + gtlt = ""; + } + pr = options.includePrerelease ? "-0" : ""; + if (xM) { + if (gtlt === ">" || gtlt === "<") { + ret = "<0.0.0-0"; + } else { + ret = "*"; + } + } else if (gtlt && anyX) { + if (xm) { + m = 0; + } + p = 0; + if (gtlt === ">") { + gtlt = ">="; + if (xm) { + M = +M + 1; + m = 0; + p = 0; + } else { + m = +m + 1; + p = 0; + } + } else if (gtlt === "<=") { + gtlt = "<"; + if (xm) { + M = +M + 1; + } else { + m = +m + 1; + } + } + if (gtlt === "<") { + pr = "-0"; + } + ret = `${gtlt + M}.${m}.${p}${pr}`; + } else if (xm) { + ret = `>=${M}.0.0${pr} <${+M + 1}.0.0-0`; + } else if (xp) { + ret = `>=${M}.${m}.0${pr} <${M}.${+m + 1}.0-0`; + } + debug("xRange return", ret); + return ret; + }); + }; + var replaceStars = (comp, options) => { + debug("replaceStars", comp, options); + return comp.trim().replace(re[t.STAR], ""); + }; + var replaceGTE0 = (comp, options) => { + debug("replaceGTE0", comp, options); + return comp.trim().replace(re[options.includePrerelease ? t.GTE0PRE : t.GTE0], ""); + }; + var hyphenReplace = (incPr) => ($0, from, fM, fm, fp, fpr, fb, to, tM, tm, tp, tpr) => { + if (isX(fM)) { + from = ""; + } else if (isX(fm)) { + from = `>=${fM}.0.0${incPr ? "-0" : ""}`; + } else if (isX(fp)) { + from = `>=${fM}.${fm}.0${incPr ? "-0" : ""}`; + } else if (fpr) { + from = `>=${from}`; + } else { + from = `>=${from}${incPr ? "-0" : ""}`; + } + if (isX(tM)) { + to = ""; + } else if (isX(tm)) { + to = `<${+tM + 1}.0.0-0`; + } else if (isX(tp)) { + to = `<${tM}.${+tm + 1}.0-0`; + } else if (tpr) { + to = `<=${tM}.${tm}.${tp}-${tpr}`; + } else if (incPr) { + to = `<${tM}.${tm}.${+tp + 1}-0`; + } else { + to = `<=${to}`; + } + return `${from} ${to}`.trim(); + }; + var testSet = (set, version, options) => { + for (let i = 0; i < set.length; i++) { + if (!set[i].test(version)) { + return false; + } + } + if (version.prerelease.length && !options.includePrerelease) { + for (let i = 0; i < set.length; i++) { + debug(set[i].semver); + if (set[i].semver === Comparator.ANY) { + continue; + } + if (set[i].semver.prerelease.length > 0) { + const allowed = set[i].semver; + if (allowed.major === version.major && allowed.minor === version.minor && allowed.patch === version.patch) { + return true; + } + } + } + return false; + } + return true; + }; + } +}); + +// ../../node_modules/semver/classes/comparator.js +var require_comparator = __commonJS({ + "../../node_modules/semver/classes/comparator.js"(exports2, module2) { + var ANY = Symbol("SemVer ANY"); + var Comparator = class _Comparator { + static get ANY() { + return ANY; + } + constructor(comp, options) { + options = parseOptions(options); + if (comp instanceof _Comparator) { + if (comp.loose === !!options.loose) { + return comp; + } else { + comp = comp.value; + } + } + comp = comp.trim().split(/\s+/).join(" "); + debug("comparator", comp, options); + this.options = options; + this.loose = !!options.loose; + this.parse(comp); + if (this.semver === ANY) { + this.value = ""; + } else { + this.value = this.operator + this.semver.version; + } + debug("comp", this); + } + parse(comp) { + const r = this.options.loose ? re[t.COMPARATORLOOSE] : re[t.COMPARATOR]; + const m = comp.match(r); + if (!m) { + throw new TypeError(`Invalid comparator: ${comp}`); + } + this.operator = m[1] !== void 0 ? m[1] : ""; + if (this.operator === "=") { + this.operator = ""; + } + if (!m[2]) { + this.semver = ANY; + } else { + this.semver = new SemVer(m[2], this.options.loose); + } + } + toString() { + return this.value; + } + test(version) { + debug("Comparator.test", version, this.options.loose); + if (this.semver === ANY || version === ANY) { + return true; + } + if (typeof version === "string") { + try { + version = new SemVer(version, this.options); + } catch (er) { + return false; + } + } + return cmp(version, this.operator, this.semver, this.options); + } + intersects(comp, options) { + if (!(comp instanceof _Comparator)) { + throw new TypeError("a Comparator is required"); + } + if (this.operator === "") { + if (this.value === "") { + return true; + } + return new Range(comp.value, options).test(this.value); + } else if (comp.operator === "") { + if (comp.value === "") { + return true; + } + return new Range(this.value, options).test(comp.semver); + } + options = parseOptions(options); + if (options.includePrerelease && (this.value === "<0.0.0-0" || comp.value === "<0.0.0-0")) { + return false; + } + if (!options.includePrerelease && (this.value.startsWith("<0.0.0") || comp.value.startsWith("<0.0.0"))) { + return false; + } + if (this.operator.startsWith(">") && comp.operator.startsWith(">")) { + return true; + } + if (this.operator.startsWith("<") && comp.operator.startsWith("<")) { + return true; + } + if (this.semver.version === comp.semver.version && this.operator.includes("=") && comp.operator.includes("=")) { + return true; + } + if (cmp(this.semver, "<", comp.semver, options) && this.operator.startsWith(">") && comp.operator.startsWith("<")) { + return true; + } + if (cmp(this.semver, ">", comp.semver, options) && this.operator.startsWith("<") && comp.operator.startsWith(">")) { + return true; + } + return false; + } + }; + module2.exports = Comparator; + var parseOptions = require_parse_options(); + var { safeRe: re, t } = require_re(); + var cmp = require_cmp(); + var debug = require_debug(); + var SemVer = require_semver(); + var Range = require_range(); + } +}); + +// ../../node_modules/semver/functions/satisfies.js +var require_satisfies = __commonJS({ + "../../node_modules/semver/functions/satisfies.js"(exports2, module2) { + var Range = require_range(); + var satisfies = (version, range, options) => { + try { + range = new Range(range, options); + } catch (er) { + return false; + } + return range.test(version); + }; + module2.exports = satisfies; + } +}); + +// ../../node_modules/semver/ranges/to-comparators.js +var require_to_comparators = __commonJS({ + "../../node_modules/semver/ranges/to-comparators.js"(exports2, module2) { + var Range = require_range(); + var toComparators = (range, options) => new Range(range, options).set.map((comp) => comp.map((c) => c.value).join(" ").trim().split(" ")); + module2.exports = toComparators; + } +}); + +// ../../node_modules/semver/ranges/max-satisfying.js +var require_max_satisfying = __commonJS({ + "../../node_modules/semver/ranges/max-satisfying.js"(exports2, module2) { + var SemVer = require_semver(); + var Range = require_range(); + var maxSatisfying = (versions, range, options) => { + let max = null; + let maxSV = null; + let rangeObj = null; + try { + rangeObj = new Range(range, options); + } catch (er) { + return null; + } + versions.forEach((v) => { + if (rangeObj.test(v)) { + if (!max || maxSV.compare(v) === -1) { + max = v; + maxSV = new SemVer(max, options); + } + } + }); + return max; + }; + module2.exports = maxSatisfying; + } +}); + +// ../../node_modules/semver/ranges/min-satisfying.js +var require_min_satisfying = __commonJS({ + "../../node_modules/semver/ranges/min-satisfying.js"(exports2, module2) { + var SemVer = require_semver(); + var Range = require_range(); + var minSatisfying = (versions, range, options) => { + let min = null; + let minSV = null; + let rangeObj = null; + try { + rangeObj = new Range(range, options); + } catch (er) { + return null; + } + versions.forEach((v) => { + if (rangeObj.test(v)) { + if (!min || minSV.compare(v) === 1) { + min = v; + minSV = new SemVer(min, options); + } + } + }); + return min; + }; + module2.exports = minSatisfying; + } +}); + +// ../../node_modules/semver/ranges/min-version.js +var require_min_version = __commonJS({ + "../../node_modules/semver/ranges/min-version.js"(exports2, module2) { + var SemVer = require_semver(); + var Range = require_range(); + var gt = require_gt(); + var minVersion = (range, loose) => { + range = new Range(range, loose); + let minver = new SemVer("0.0.0"); + if (range.test(minver)) { + return minver; + } + minver = new SemVer("0.0.0-0"); + if (range.test(minver)) { + return minver; + } + minver = null; + for (let i = 0; i < range.set.length; ++i) { + const comparators = range.set[i]; + let setMin = null; + comparators.forEach((comparator) => { + const compver = new SemVer(comparator.semver.version); + switch (comparator.operator) { + case ">": + if (compver.prerelease.length === 0) { + compver.patch++; + } else { + compver.prerelease.push(0); + } + compver.raw = compver.format(); + case "": + case ">=": + if (!setMin || gt(compver, setMin)) { + setMin = compver; + } + break; + case "<": + case "<=": + break; + default: + throw new Error(`Unexpected operation: ${comparator.operator}`); + } + }); + if (setMin && (!minver || gt(minver, setMin))) { + minver = setMin; + } + } + if (minver && range.test(minver)) { + return minver; + } + return null; + }; + module2.exports = minVersion; + } +}); + +// ../../node_modules/semver/ranges/valid.js +var require_valid2 = __commonJS({ + "../../node_modules/semver/ranges/valid.js"(exports2, module2) { + var Range = require_range(); + var validRange = (range, options) => { + try { + return new Range(range, options).range || "*"; + } catch (er) { + return null; + } + }; + module2.exports = validRange; + } +}); + +// ../../node_modules/semver/ranges/outside.js +var require_outside = __commonJS({ + "../../node_modules/semver/ranges/outside.js"(exports2, module2) { + var SemVer = require_semver(); + var Comparator = require_comparator(); + var { ANY } = Comparator; + var Range = require_range(); + var satisfies = require_satisfies(); + var gt = require_gt(); + var lt = require_lt(); + var lte = require_lte(); + var gte = require_gte(); + var outside = (version, range, hilo, options) => { + version = new SemVer(version, options); + range = new Range(range, options); + let gtfn, ltefn, ltfn, comp, ecomp; + switch (hilo) { + case ">": + gtfn = gt; + ltefn = lte; + ltfn = lt; + comp = ">"; + ecomp = ">="; + break; + case "<": + gtfn = lt; + ltefn = gte; + ltfn = gt; + comp = "<"; + ecomp = "<="; + break; + default: + throw new TypeError('Must provide a hilo val of "<" or ">"'); + } + if (satisfies(version, range, options)) { + return false; + } + for (let i = 0; i < range.set.length; ++i) { + const comparators = range.set[i]; + let high = null; + let low = null; + comparators.forEach((comparator) => { + if (comparator.semver === ANY) { + comparator = new Comparator(">=0.0.0"); + } + high = high || comparator; + low = low || comparator; + if (gtfn(comparator.semver, high.semver, options)) { + high = comparator; + } else if (ltfn(comparator.semver, low.semver, options)) { + low = comparator; + } + }); + if (high.operator === comp || high.operator === ecomp) { + return false; + } + if ((!low.operator || low.operator === comp) && ltefn(version, low.semver)) { + return false; + } else if (low.operator === ecomp && ltfn(version, low.semver)) { + return false; + } + } + return true; + }; + module2.exports = outside; + } +}); + +// ../../node_modules/semver/ranges/gtr.js +var require_gtr = __commonJS({ + "../../node_modules/semver/ranges/gtr.js"(exports2, module2) { + var outside = require_outside(); + var gtr = (version, range, options) => outside(version, range, ">", options); + module2.exports = gtr; + } +}); + +// ../../node_modules/semver/ranges/ltr.js +var require_ltr = __commonJS({ + "../../node_modules/semver/ranges/ltr.js"(exports2, module2) { + var outside = require_outside(); + var ltr = (version, range, options) => outside(version, range, "<", options); + module2.exports = ltr; + } +}); + +// ../../node_modules/semver/ranges/intersects.js +var require_intersects = __commonJS({ + "../../node_modules/semver/ranges/intersects.js"(exports2, module2) { + var Range = require_range(); + var intersects = (r1, r2, options) => { + r1 = new Range(r1, options); + r2 = new Range(r2, options); + return r1.intersects(r2, options); + }; + module2.exports = intersects; + } +}); + +// ../../node_modules/semver/ranges/simplify.js +var require_simplify = __commonJS({ + "../../node_modules/semver/ranges/simplify.js"(exports2, module2) { + var satisfies = require_satisfies(); + var compare = require_compare(); + module2.exports = (versions, range, options) => { + const set = []; + let first = null; + let prev = null; + const v = versions.sort((a, b) => compare(a, b, options)); + for (const version of v) { + const included = satisfies(version, range, options); + if (included) { + prev = version; + if (!first) { + first = version; + } + } else { + if (prev) { + set.push([first, prev]); + } + prev = null; + first = null; + } + } + if (first) { + set.push([first, null]); + } + const ranges = []; + for (const [min, max] of set) { + if (min === max) { + ranges.push(min); + } else if (!max && min === v[0]) { + ranges.push("*"); + } else if (!max) { + ranges.push(`>=${min}`); + } else if (min === v[0]) { + ranges.push(`<=${max}`); + } else { + ranges.push(`${min} - ${max}`); + } + } + const simplified = ranges.join(" || "); + const original = typeof range.raw === "string" ? range.raw : String(range); + return simplified.length < original.length ? simplified : range; + }; + } +}); + +// ../../node_modules/semver/ranges/subset.js +var require_subset = __commonJS({ + "../../node_modules/semver/ranges/subset.js"(exports2, module2) { + var Range = require_range(); + var Comparator = require_comparator(); + var { ANY } = Comparator; + var satisfies = require_satisfies(); + var compare = require_compare(); + var subset = (sub, dom, options = {}) => { + if (sub === dom) { + return true; + } + sub = new Range(sub, options); + dom = new Range(dom, options); + let sawNonNull = false; + OUTER: for (const simpleSub of sub.set) { + for (const simpleDom of dom.set) { + const isSub = simpleSubset(simpleSub, simpleDom, options); + sawNonNull = sawNonNull || isSub !== null; + if (isSub) { + continue OUTER; + } + } + if (sawNonNull) { + return false; + } + } + return true; + }; + var minimumVersionWithPreRelease = [new Comparator(">=0.0.0-0")]; + var minimumVersion = [new Comparator(">=0.0.0")]; + var simpleSubset = (sub, dom, options) => { + if (sub === dom) { + return true; + } + if (sub.length === 1 && sub[0].semver === ANY) { + if (dom.length === 1 && dom[0].semver === ANY) { + return true; + } else if (options.includePrerelease) { + sub = minimumVersionWithPreRelease; + } else { + sub = minimumVersion; + } + } + if (dom.length === 1 && dom[0].semver === ANY) { + if (options.includePrerelease) { + return true; + } else { + dom = minimumVersion; + } + } + const eqSet = /* @__PURE__ */ new Set(); + let gt, lt; + for (const c of sub) { + if (c.operator === ">" || c.operator === ">=") { + gt = higherGT(gt, c, options); + } else if (c.operator === "<" || c.operator === "<=") { + lt = lowerLT(lt, c, options); + } else { + eqSet.add(c.semver); + } + } + if (eqSet.size > 1) { + return null; + } + let gtltComp; + if (gt && lt) { + gtltComp = compare(gt.semver, lt.semver, options); + if (gtltComp > 0) { + return null; + } else if (gtltComp === 0 && (gt.operator !== ">=" || lt.operator !== "<=")) { + return null; + } + } + for (const eq of eqSet) { + if (gt && !satisfies(eq, String(gt), options)) { + return null; + } + if (lt && !satisfies(eq, String(lt), options)) { + return null; + } + for (const c of dom) { + if (!satisfies(eq, String(c), options)) { + return false; + } + } + return true; + } + let higher, lower; + let hasDomLT, hasDomGT; + let needDomLTPre = lt && !options.includePrerelease && lt.semver.prerelease.length ? lt.semver : false; + let needDomGTPre = gt && !options.includePrerelease && gt.semver.prerelease.length ? gt.semver : false; + if (needDomLTPre && needDomLTPre.prerelease.length === 1 && lt.operator === "<" && needDomLTPre.prerelease[0] === 0) { + needDomLTPre = false; + } + for (const c of dom) { + hasDomGT = hasDomGT || c.operator === ">" || c.operator === ">="; + hasDomLT = hasDomLT || c.operator === "<" || c.operator === "<="; + if (gt) { + if (needDomGTPre) { + if (c.semver.prerelease && c.semver.prerelease.length && c.semver.major === needDomGTPre.major && c.semver.minor === needDomGTPre.minor && c.semver.patch === needDomGTPre.patch) { + needDomGTPre = false; + } + } + if (c.operator === ">" || c.operator === ">=") { + higher = higherGT(gt, c, options); + if (higher === c && higher !== gt) { + return false; + } + } else if (gt.operator === ">=" && !satisfies(gt.semver, String(c), options)) { + return false; + } + } + if (lt) { + if (needDomLTPre) { + if (c.semver.prerelease && c.semver.prerelease.length && c.semver.major === needDomLTPre.major && c.semver.minor === needDomLTPre.minor && c.semver.patch === needDomLTPre.patch) { + needDomLTPre = false; + } + } + if (c.operator === "<" || c.operator === "<=") { + lower = lowerLT(lt, c, options); + if (lower === c && lower !== lt) { + return false; + } + } else if (lt.operator === "<=" && !satisfies(lt.semver, String(c), options)) { + return false; + } + } + if (!c.operator && (lt || gt) && gtltComp !== 0) { + return false; + } + } + if (gt && hasDomLT && !lt && gtltComp !== 0) { + return false; + } + if (lt && hasDomGT && !gt && gtltComp !== 0) { + return false; + } + if (needDomGTPre || needDomLTPre) { + return false; + } + return true; + }; + var higherGT = (a, b, options) => { + if (!a) { + return b; + } + const comp = compare(a.semver, b.semver, options); + return comp > 0 ? a : comp < 0 ? b : b.operator === ">" && a.operator === ">=" ? b : a; + }; + var lowerLT = (a, b, options) => { + if (!a) { + return b; + } + const comp = compare(a.semver, b.semver, options); + return comp < 0 ? a : comp > 0 ? b : b.operator === "<" && a.operator === "<=" ? b : a; + }; + module2.exports = subset; + } +}); + +// ../../node_modules/semver/index.js +var require_semver2 = __commonJS({ + "../../node_modules/semver/index.js"(exports2, module2) { + var internalRe = require_re(); + var constants = require_constants(); + var SemVer = require_semver(); + var identifiers = require_identifiers(); + var parse = require_parse(); + var valid = require_valid(); + var clean = require_clean(); + var inc = require_inc(); + var diff = require_diff(); + var major = require_major(); + var minor = require_minor(); + var patch = require_patch(); + var prerelease = require_prerelease(); + var compare = require_compare(); + var rcompare = require_rcompare(); + var compareLoose = require_compare_loose(); + var compareBuild = require_compare_build(); + var sort = require_sort(); + var rsort = require_rsort(); + var gt = require_gt(); + var lt = require_lt(); + var eq = require_eq(); + var neq = require_neq(); + var gte = require_gte(); + var lte = require_lte(); + var cmp = require_cmp(); + var coerce = require_coerce(); + var Comparator = require_comparator(); + var Range = require_range(); + var satisfies = require_satisfies(); + var toComparators = require_to_comparators(); + var maxSatisfying = require_max_satisfying(); + var minSatisfying = require_min_satisfying(); + var minVersion = require_min_version(); + var validRange = require_valid2(); + var outside = require_outside(); + var gtr = require_gtr(); + var ltr = require_ltr(); + var intersects = require_intersects(); + var simplifyRange = require_simplify(); + var subset = require_subset(); + module2.exports = { + parse, + valid, + clean, + inc, + diff, + major, + minor, + patch, + prerelease, + compare, + rcompare, + compareLoose, + compareBuild, + sort, + rsort, + gt, + lt, + eq, + neq, + gte, + lte, + cmp, + coerce, + Comparator, + Range, + satisfies, + toComparators, + maxSatisfying, + minSatisfying, + minVersion, + validRange, + outside, + gtr, + ltr, + intersects, + simplifyRange, + subset, + SemVer, + re: internalRe.re, + src: internalRe.src, + tokens: internalRe.t, + SEMVER_SPEC_VERSION: constants.SEMVER_SPEC_VERSION, + RELEASE_TYPES: constants.RELEASE_TYPES, + compareIdentifiers: identifiers.compareIdentifiers, + rcompareIdentifiers: identifiers.rcompareIdentifiers + }; + } +}); + +// ../../node_modules/jsonwebtoken/lib/asymmetricKeyDetailsSupported.js +var require_asymmetricKeyDetailsSupported = __commonJS({ + "../../node_modules/jsonwebtoken/lib/asymmetricKeyDetailsSupported.js"(exports2, module2) { + var semver = require_semver2(); + module2.exports = semver.satisfies(process.version, ">=15.7.0"); + } +}); + +// ../../node_modules/jsonwebtoken/lib/rsaPssKeyDetailsSupported.js +var require_rsaPssKeyDetailsSupported = __commonJS({ + "../../node_modules/jsonwebtoken/lib/rsaPssKeyDetailsSupported.js"(exports2, module2) { + var semver = require_semver2(); + module2.exports = semver.satisfies(process.version, ">=16.9.0"); + } +}); + +// ../../node_modules/jsonwebtoken/lib/validateAsymmetricKey.js +var require_validateAsymmetricKey = __commonJS({ + "../../node_modules/jsonwebtoken/lib/validateAsymmetricKey.js"(exports2, module2) { + var ASYMMETRIC_KEY_DETAILS_SUPPORTED = require_asymmetricKeyDetailsSupported(); + var RSA_PSS_KEY_DETAILS_SUPPORTED = require_rsaPssKeyDetailsSupported(); + var allowedAlgorithmsForKeys = { + "ec": ["ES256", "ES384", "ES512"], + "rsa": ["RS256", "PS256", "RS384", "PS384", "RS512", "PS512"], + "rsa-pss": ["PS256", "PS384", "PS512"] + }; + var allowedCurves = { + ES256: "prime256v1", + ES384: "secp384r1", + ES512: "secp521r1" + }; + module2.exports = function(algorithm, key) { + if (!algorithm || !key) return; + const keyType = key.asymmetricKeyType; + if (!keyType) return; + const allowedAlgorithms = allowedAlgorithmsForKeys[keyType]; + if (!allowedAlgorithms) { + throw new Error(`Unknown key type "${keyType}".`); + } + if (!allowedAlgorithms.includes(algorithm)) { + throw new Error(`"alg" parameter for "${keyType}" key type must be one of: ${allowedAlgorithms.join(", ")}.`); + } + if (ASYMMETRIC_KEY_DETAILS_SUPPORTED) { + switch (keyType) { + case "ec": + const keyCurve = key.asymmetricKeyDetails.namedCurve; + const allowedCurve = allowedCurves[algorithm]; + if (keyCurve !== allowedCurve) { + throw new Error(`"alg" parameter "${algorithm}" requires curve "${allowedCurve}".`); + } + break; + case "rsa-pss": + if (RSA_PSS_KEY_DETAILS_SUPPORTED) { + const length = parseInt(algorithm.slice(-3), 10); + const { hashAlgorithm, mgf1HashAlgorithm, saltLength } = key.asymmetricKeyDetails; + if (hashAlgorithm !== `sha${length}` || mgf1HashAlgorithm !== hashAlgorithm) { + throw new Error(`Invalid key for this operation, its RSA-PSS parameters do not meet the requirements of "alg" ${algorithm}.`); + } + if (saltLength !== void 0 && saltLength > length >> 3) { + throw new Error(`Invalid key for this operation, its RSA-PSS parameter saltLength does not meet the requirements of "alg" ${algorithm}.`); + } + } + break; + } + } + }; + } +}); + +// ../../node_modules/jsonwebtoken/lib/psSupported.js +var require_psSupported = __commonJS({ + "../../node_modules/jsonwebtoken/lib/psSupported.js"(exports2, module2) { + var semver = require_semver2(); + module2.exports = semver.satisfies(process.version, "^6.12.0 || >=8.0.0"); + } +}); + +// ../../node_modules/jsonwebtoken/verify.js +var require_verify = __commonJS({ + "../../node_modules/jsonwebtoken/verify.js"(exports2, module2) { + var JsonWebTokenError = require_JsonWebTokenError(); + var NotBeforeError = require_NotBeforeError(); + var TokenExpiredError = require_TokenExpiredError(); + var decode = require_decode(); + var timespan = require_timespan(); + var validateAsymmetricKey = require_validateAsymmetricKey(); + var PS_SUPPORTED = require_psSupported(); + var jws = require_jws(); + var { KeyObject, createSecretKey, createPublicKey } = require("crypto"); + var PUB_KEY_ALGS = ["RS256", "RS384", "RS512"]; + var EC_KEY_ALGS = ["ES256", "ES384", "ES512"]; + var RSA_KEY_ALGS = ["RS256", "RS384", "RS512"]; + var HS_ALGS = ["HS256", "HS384", "HS512"]; + if (PS_SUPPORTED) { + PUB_KEY_ALGS.splice(PUB_KEY_ALGS.length, 0, "PS256", "PS384", "PS512"); + RSA_KEY_ALGS.splice(RSA_KEY_ALGS.length, 0, "PS256", "PS384", "PS512"); + } + module2.exports = function(jwtString, secretOrPublicKey, options, callback) { + if (typeof options === "function" && !callback) { + callback = options; + options = {}; + } + if (!options) { + options = {}; + } + options = Object.assign({}, options); + let done; + if (callback) { + done = callback; + } else { + done = function(err, data) { + if (err) throw err; + return data; + }; + } + if (options.clockTimestamp && typeof options.clockTimestamp !== "number") { + return done(new JsonWebTokenError("clockTimestamp must be a number")); + } + if (options.nonce !== void 0 && (typeof options.nonce !== "string" || options.nonce.trim() === "")) { + return done(new JsonWebTokenError("nonce must be a non-empty string")); + } + if (options.allowInvalidAsymmetricKeyTypes !== void 0 && typeof options.allowInvalidAsymmetricKeyTypes !== "boolean") { + return done(new JsonWebTokenError("allowInvalidAsymmetricKeyTypes must be a boolean")); + } + const clockTimestamp = options.clockTimestamp || Math.floor(Date.now() / 1e3); + if (!jwtString) { + return done(new JsonWebTokenError("jwt must be provided")); + } + if (typeof jwtString !== "string") { + return done(new JsonWebTokenError("jwt must be a string")); + } + const parts = jwtString.split("."); + if (parts.length !== 3) { + return done(new JsonWebTokenError("jwt malformed")); + } + let decodedToken; + try { + decodedToken = decode(jwtString, { complete: true }); + } catch (err) { + return done(err); + } + if (!decodedToken) { + return done(new JsonWebTokenError("invalid token")); + } + const header = decodedToken.header; + let getSecret; + if (typeof secretOrPublicKey === "function") { + if (!callback) { + return done(new JsonWebTokenError("verify must be called asynchronous if secret or public key is provided as a callback")); + } + getSecret = secretOrPublicKey; + } else { + getSecret = function(header2, secretCallback) { + return secretCallback(null, secretOrPublicKey); + }; + } + return getSecret(header, function(err, secretOrPublicKey2) { + if (err) { + return done(new JsonWebTokenError("error in secret or public key callback: " + err.message)); + } + const hasSignature = parts[2].trim() !== ""; + if (!hasSignature && secretOrPublicKey2) { + return done(new JsonWebTokenError("jwt signature is required")); + } + if (hasSignature && !secretOrPublicKey2) { + return done(new JsonWebTokenError("secret or public key must be provided")); + } + if (!hasSignature && !options.algorithms) { + return done(new JsonWebTokenError('please specify "none" in "algorithms" to verify unsigned tokens')); + } + if (secretOrPublicKey2 != null && !(secretOrPublicKey2 instanceof KeyObject)) { + try { + secretOrPublicKey2 = createPublicKey(secretOrPublicKey2); + } catch (_) { + try { + secretOrPublicKey2 = createSecretKey(typeof secretOrPublicKey2 === "string" ? Buffer.from(secretOrPublicKey2) : secretOrPublicKey2); + } catch (_2) { + return done(new JsonWebTokenError("secretOrPublicKey is not valid key material")); + } + } + } + if (!options.algorithms) { + if (secretOrPublicKey2.type === "secret") { + options.algorithms = HS_ALGS; + } else if (["rsa", "rsa-pss"].includes(secretOrPublicKey2.asymmetricKeyType)) { + options.algorithms = RSA_KEY_ALGS; + } else if (secretOrPublicKey2.asymmetricKeyType === "ec") { + options.algorithms = EC_KEY_ALGS; + } else { + options.algorithms = PUB_KEY_ALGS; + } + } + if (options.algorithms.indexOf(decodedToken.header.alg) === -1) { + return done(new JsonWebTokenError("invalid algorithm")); + } + if (header.alg.startsWith("HS") && secretOrPublicKey2.type !== "secret") { + return done(new JsonWebTokenError(`secretOrPublicKey must be a symmetric key when using ${header.alg}`)); + } else if (/^(?:RS|PS|ES)/.test(header.alg) && secretOrPublicKey2.type !== "public") { + return done(new JsonWebTokenError(`secretOrPublicKey must be an asymmetric key when using ${header.alg}`)); + } + if (!options.allowInvalidAsymmetricKeyTypes) { + try { + validateAsymmetricKey(header.alg, secretOrPublicKey2); + } catch (e) { + return done(e); + } + } + let valid; + try { + valid = jws.verify(jwtString, decodedToken.header.alg, secretOrPublicKey2); + } catch (e) { + return done(e); + } + if (!valid) { + return done(new JsonWebTokenError("invalid signature")); + } + const payload = decodedToken.payload; + if (typeof payload.nbf !== "undefined" && !options.ignoreNotBefore) { + if (typeof payload.nbf !== "number") { + return done(new JsonWebTokenError("invalid nbf value")); + } + if (payload.nbf > clockTimestamp + (options.clockTolerance || 0)) { + return done(new NotBeforeError("jwt not active", new Date(payload.nbf * 1e3))); + } + } + if (typeof payload.exp !== "undefined" && !options.ignoreExpiration) { + if (typeof payload.exp !== "number") { + return done(new JsonWebTokenError("invalid exp value")); + } + if (clockTimestamp >= payload.exp + (options.clockTolerance || 0)) { + return done(new TokenExpiredError("jwt expired", new Date(payload.exp * 1e3))); + } + } + if (options.audience) { + const audiences = Array.isArray(options.audience) ? options.audience : [options.audience]; + const target = Array.isArray(payload.aud) ? payload.aud : [payload.aud]; + const match = target.some(function(targetAudience) { + return audiences.some(function(audience) { + return audience instanceof RegExp ? audience.test(targetAudience) : audience === targetAudience; + }); + }); + if (!match) { + return done(new JsonWebTokenError("jwt audience invalid. expected: " + audiences.join(" or "))); + } + } + if (options.issuer) { + const invalid_issuer = typeof options.issuer === "string" && payload.iss !== options.issuer || Array.isArray(options.issuer) && options.issuer.indexOf(payload.iss) === -1; + if (invalid_issuer) { + return done(new JsonWebTokenError("jwt issuer invalid. expected: " + options.issuer)); + } + } + if (options.subject) { + if (payload.sub !== options.subject) { + return done(new JsonWebTokenError("jwt subject invalid. expected: " + options.subject)); + } + } + if (options.jwtid) { + if (payload.jti !== options.jwtid) { + return done(new JsonWebTokenError("jwt jwtid invalid. expected: " + options.jwtid)); + } + } + if (options.nonce) { + if (payload.nonce !== options.nonce) { + return done(new JsonWebTokenError("jwt nonce invalid. expected: " + options.nonce)); + } + } + if (options.maxAge) { + if (typeof payload.iat !== "number") { + return done(new JsonWebTokenError("iat required when maxAge is specified")); + } + const maxAgeTimestamp = timespan(options.maxAge, payload.iat); + if (typeof maxAgeTimestamp === "undefined") { + return done(new JsonWebTokenError('"maxAge" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + if (clockTimestamp >= maxAgeTimestamp + (options.clockTolerance || 0)) { + return done(new TokenExpiredError("maxAge exceeded", new Date(maxAgeTimestamp * 1e3))); + } + } + if (options.complete === true) { + const signature = decodedToken.signature; + return done(null, { + header, + payload, + signature + }); + } + return done(null, payload); + }); + }; + } +}); + +// ../../node_modules/lodash.includes/index.js +var require_lodash = __commonJS({ + "../../node_modules/lodash.includes/index.js"(exports2, module2) { + var INFINITY = 1 / 0; + var MAX_SAFE_INTEGER = 9007199254740991; + var MAX_INTEGER = 17976931348623157e292; + var NAN = 0 / 0; + var argsTag = "[object Arguments]"; + var funcTag = "[object Function]"; + var genTag = "[object GeneratorFunction]"; + var stringTag = "[object String]"; + var symbolTag = "[object Symbol]"; + var reTrim = /^\s+|\s+$/g; + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + var reIsBinary = /^0b[01]+$/i; + var reIsOctal = /^0o[0-7]+$/i; + var reIsUint = /^(?:0|[1-9]\d*)$/; + var freeParseInt = parseInt; + function arrayMap(array, iteratee) { + var index = -1, length = array ? array.length : 0, result = Array(length); + while (++index < length) { + result[index] = iteratee(array[index], index, array); + } + return result; + } + function baseFindIndex(array, predicate, fromIndex, fromRight) { + var length = array.length, index = fromIndex + (fromRight ? 1 : -1); + while (fromRight ? index-- : ++index < length) { + if (predicate(array[index], index, array)) { + return index; + } + } + return -1; + } + function baseIndexOf(array, value, fromIndex) { + if (value !== value) { + return baseFindIndex(array, baseIsNaN, fromIndex); + } + var index = fromIndex - 1, length = array.length; + while (++index < length) { + if (array[index] === value) { + return index; + } + } + return -1; + } + function baseIsNaN(value) { + return value !== value; + } + function baseTimes(n, iteratee) { + var index = -1, result = Array(n); + while (++index < n) { + result[index] = iteratee(index); + } + return result; + } + function baseValues(object, props) { + return arrayMap(props, function(key) { + return object[key]; + }); + } + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + var objectProto = Object.prototype; + var hasOwnProperty = objectProto.hasOwnProperty; + var objectToString = objectProto.toString; + var propertyIsEnumerable = objectProto.propertyIsEnumerable; + var nativeKeys = overArg(Object.keys, Object); + var nativeMax = Math.max; + function arrayLikeKeys(value, inherited) { + var result = isArray(value) || isArguments(value) ? baseTimes(value.length, String) : []; + var length = result.length, skipIndexes = !!length; + for (var key in value) { + if ((inherited || hasOwnProperty.call(value, key)) && !(skipIndexes && (key == "length" || isIndex(key, length)))) { + result.push(key); + } + } + return result; + } + function baseKeys(object) { + if (!isPrototype(object)) { + return nativeKeys(object); + } + var result = []; + for (var key in Object(object)) { + if (hasOwnProperty.call(object, key) && key != "constructor") { + result.push(key); + } + } + return result; + } + function isIndex(value, length) { + length = length == null ? MAX_SAFE_INTEGER : length; + return !!length && (typeof value == "number" || reIsUint.test(value)) && (value > -1 && value % 1 == 0 && value < length); + } + function isPrototype(value) { + var Ctor = value && value.constructor, proto = typeof Ctor == "function" && Ctor.prototype || objectProto; + return value === proto; + } + function includes(collection, value, fromIndex, guard) { + collection = isArrayLike(collection) ? collection : values(collection); + fromIndex = fromIndex && !guard ? toInteger(fromIndex) : 0; + var length = collection.length; + if (fromIndex < 0) { + fromIndex = nativeMax(length + fromIndex, 0); + } + return isString(collection) ? fromIndex <= length && collection.indexOf(value, fromIndex) > -1 : !!length && baseIndexOf(collection, value, fromIndex) > -1; + } + function isArguments(value) { + return isArrayLikeObject(value) && hasOwnProperty.call(value, "callee") && (!propertyIsEnumerable.call(value, "callee") || objectToString.call(value) == argsTag); + } + var isArray = Array.isArray; + function isArrayLike(value) { + return value != null && isLength(value.length) && !isFunction(value); + } + function isArrayLikeObject(value) { + return isObjectLike(value) && isArrayLike(value); + } + function isFunction(value) { + var tag = isObject(value) ? objectToString.call(value) : ""; + return tag == funcTag || tag == genTag; + } + function isLength(value) { + return typeof value == "number" && value > -1 && value % 1 == 0 && value <= MAX_SAFE_INTEGER; + } + function isObject(value) { + var type = typeof value; + return !!value && (type == "object" || type == "function"); + } + function isObjectLike(value) { + return !!value && typeof value == "object"; + } + function isString(value) { + return typeof value == "string" || !isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag; + } + function isSymbol(value) { + return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag; + } + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = value < 0 ? -1 : 1; + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + function toInteger(value) { + var result = toFinite(value), remainder = result % 1; + return result === result ? remainder ? result - remainder : result : 0; + } + function toNumber(value) { + if (typeof value == "number") { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == "function" ? value.valueOf() : value; + value = isObject(other) ? other + "" : other; + } + if (typeof value != "string") { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ""); + var isBinary = reIsBinary.test(value); + return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; + } + function keys(object) { + return isArrayLike(object) ? arrayLikeKeys(object) : baseKeys(object); + } + function values(object) { + return object ? baseValues(object, keys(object)) : []; + } + module2.exports = includes; + } +}); + +// ../../node_modules/lodash.isboolean/index.js +var require_lodash2 = __commonJS({ + "../../node_modules/lodash.isboolean/index.js"(exports2, module2) { + var boolTag = "[object Boolean]"; + var objectProto = Object.prototype; + var objectToString = objectProto.toString; + function isBoolean(value) { + return value === true || value === false || isObjectLike(value) && objectToString.call(value) == boolTag; + } + function isObjectLike(value) { + return !!value && typeof value == "object"; + } + module2.exports = isBoolean; + } +}); + +// ../../node_modules/lodash.isinteger/index.js +var require_lodash3 = __commonJS({ + "../../node_modules/lodash.isinteger/index.js"(exports2, module2) { + var INFINITY = 1 / 0; + var MAX_INTEGER = 17976931348623157e292; + var NAN = 0 / 0; + var symbolTag = "[object Symbol]"; + var reTrim = /^\s+|\s+$/g; + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + var reIsBinary = /^0b[01]+$/i; + var reIsOctal = /^0o[0-7]+$/i; + var freeParseInt = parseInt; + var objectProto = Object.prototype; + var objectToString = objectProto.toString; + function isInteger(value) { + return typeof value == "number" && value == toInteger(value); + } + function isObject(value) { + var type = typeof value; + return !!value && (type == "object" || type == "function"); + } + function isObjectLike(value) { + return !!value && typeof value == "object"; + } + function isSymbol(value) { + return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag; + } + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = value < 0 ? -1 : 1; + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + function toInteger(value) { + var result = toFinite(value), remainder = result % 1; + return result === result ? remainder ? result - remainder : result : 0; + } + function toNumber(value) { + if (typeof value == "number") { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == "function" ? value.valueOf() : value; + value = isObject(other) ? other + "" : other; + } + if (typeof value != "string") { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ""); + var isBinary = reIsBinary.test(value); + return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; + } + module2.exports = isInteger; + } +}); + +// ../../node_modules/lodash.isnumber/index.js +var require_lodash4 = __commonJS({ + "../../node_modules/lodash.isnumber/index.js"(exports2, module2) { + var numberTag = "[object Number]"; + var objectProto = Object.prototype; + var objectToString = objectProto.toString; + function isObjectLike(value) { + return !!value && typeof value == "object"; + } + function isNumber(value) { + return typeof value == "number" || isObjectLike(value) && objectToString.call(value) == numberTag; + } + module2.exports = isNumber; + } +}); + +// ../../node_modules/lodash.isplainobject/index.js +var require_lodash5 = __commonJS({ + "../../node_modules/lodash.isplainobject/index.js"(exports2, module2) { + var objectTag = "[object Object]"; + function isHostObject(value) { + var result = false; + if (value != null && typeof value.toString != "function") { + try { + result = !!(value + ""); + } catch (e) { + } + } + return result; + } + function overArg(func, transform) { + return function(arg) { + return func(transform(arg)); + }; + } + var funcProto = Function.prototype; + var objectProto = Object.prototype; + var funcToString = funcProto.toString; + var hasOwnProperty = objectProto.hasOwnProperty; + var objectCtorString = funcToString.call(Object); + var objectToString = objectProto.toString; + var getPrototype = overArg(Object.getPrototypeOf, Object); + function isObjectLike(value) { + return !!value && typeof value == "object"; + } + function isPlainObject(value) { + if (!isObjectLike(value) || objectToString.call(value) != objectTag || isHostObject(value)) { + return false; + } + var proto = getPrototype(value); + if (proto === null) { + return true; + } + var Ctor = hasOwnProperty.call(proto, "constructor") && proto.constructor; + return typeof Ctor == "function" && Ctor instanceof Ctor && funcToString.call(Ctor) == objectCtorString; + } + module2.exports = isPlainObject; + } +}); + +// ../../node_modules/lodash.isstring/index.js +var require_lodash6 = __commonJS({ + "../../node_modules/lodash.isstring/index.js"(exports2, module2) { + var stringTag = "[object String]"; + var objectProto = Object.prototype; + var objectToString = objectProto.toString; + var isArray = Array.isArray; + function isObjectLike(value) { + return !!value && typeof value == "object"; + } + function isString(value) { + return typeof value == "string" || !isArray(value) && isObjectLike(value) && objectToString.call(value) == stringTag; + } + module2.exports = isString; + } +}); + +// ../../node_modules/lodash.once/index.js +var require_lodash7 = __commonJS({ + "../../node_modules/lodash.once/index.js"(exports2, module2) { + var FUNC_ERROR_TEXT = "Expected a function"; + var INFINITY = 1 / 0; + var MAX_INTEGER = 17976931348623157e292; + var NAN = 0 / 0; + var symbolTag = "[object Symbol]"; + var reTrim = /^\s+|\s+$/g; + var reIsBadHex = /^[-+]0x[0-9a-f]+$/i; + var reIsBinary = /^0b[01]+$/i; + var reIsOctal = /^0o[0-7]+$/i; + var freeParseInt = parseInt; + var objectProto = Object.prototype; + var objectToString = objectProto.toString; + function before(n, func) { + var result; + if (typeof func != "function") { + throw new TypeError(FUNC_ERROR_TEXT); + } + n = toInteger(n); + return function() { + if (--n > 0) { + result = func.apply(this, arguments); + } + if (n <= 1) { + func = void 0; + } + return result; + }; + } + function once(func) { + return before(2, func); + } + function isObject(value) { + var type = typeof value; + return !!value && (type == "object" || type == "function"); + } + function isObjectLike(value) { + return !!value && typeof value == "object"; + } + function isSymbol(value) { + return typeof value == "symbol" || isObjectLike(value) && objectToString.call(value) == symbolTag; + } + function toFinite(value) { + if (!value) { + return value === 0 ? value : 0; + } + value = toNumber(value); + if (value === INFINITY || value === -INFINITY) { + var sign = value < 0 ? -1 : 1; + return sign * MAX_INTEGER; + } + return value === value ? value : 0; + } + function toInteger(value) { + var result = toFinite(value), remainder = result % 1; + return result === result ? remainder ? result - remainder : result : 0; + } + function toNumber(value) { + if (typeof value == "number") { + return value; + } + if (isSymbol(value)) { + return NAN; + } + if (isObject(value)) { + var other = typeof value.valueOf == "function" ? value.valueOf() : value; + value = isObject(other) ? other + "" : other; + } + if (typeof value != "string") { + return value === 0 ? value : +value; + } + value = value.replace(reTrim, ""); + var isBinary = reIsBinary.test(value); + return isBinary || reIsOctal.test(value) ? freeParseInt(value.slice(2), isBinary ? 2 : 8) : reIsBadHex.test(value) ? NAN : +value; + } + module2.exports = once; + } +}); + +// ../../node_modules/jsonwebtoken/sign.js +var require_sign = __commonJS({ + "../../node_modules/jsonwebtoken/sign.js"(exports2, module2) { + var timespan = require_timespan(); + var PS_SUPPORTED = require_psSupported(); + var validateAsymmetricKey = require_validateAsymmetricKey(); + var jws = require_jws(); + var includes = require_lodash(); + var isBoolean = require_lodash2(); + var isInteger = require_lodash3(); + var isNumber = require_lodash4(); + var isPlainObject = require_lodash5(); + var isString = require_lodash6(); + var once = require_lodash7(); + var { KeyObject, createSecretKey, createPrivateKey } = require("crypto"); + var SUPPORTED_ALGS = ["RS256", "RS384", "RS512", "ES256", "ES384", "ES512", "HS256", "HS384", "HS512", "none"]; + if (PS_SUPPORTED) { + SUPPORTED_ALGS.splice(3, 0, "PS256", "PS384", "PS512"); + } + var sign_options_schema = { + expiresIn: { isValid: function(value) { + return isInteger(value) || isString(value) && value; + }, message: '"expiresIn" should be a number of seconds or string representing a timespan' }, + notBefore: { isValid: function(value) { + return isInteger(value) || isString(value) && value; + }, message: '"notBefore" should be a number of seconds or string representing a timespan' }, + audience: { isValid: function(value) { + return isString(value) || Array.isArray(value); + }, message: '"audience" must be a string or array' }, + algorithm: { isValid: includes.bind(null, SUPPORTED_ALGS), message: '"algorithm" must be a valid string enum value' }, + header: { isValid: isPlainObject, message: '"header" must be an object' }, + encoding: { isValid: isString, message: '"encoding" must be a string' }, + issuer: { isValid: isString, message: '"issuer" must be a string' }, + subject: { isValid: isString, message: '"subject" must be a string' }, + jwtid: { isValid: isString, message: '"jwtid" must be a string' }, + noTimestamp: { isValid: isBoolean, message: '"noTimestamp" must be a boolean' }, + keyid: { isValid: isString, message: '"keyid" must be a string' }, + mutatePayload: { isValid: isBoolean, message: '"mutatePayload" must be a boolean' }, + allowInsecureKeySizes: { isValid: isBoolean, message: '"allowInsecureKeySizes" must be a boolean' }, + allowInvalidAsymmetricKeyTypes: { isValid: isBoolean, message: '"allowInvalidAsymmetricKeyTypes" must be a boolean' } + }; + var registered_claims_schema = { + iat: { isValid: isNumber, message: '"iat" should be a number of seconds' }, + exp: { isValid: isNumber, message: '"exp" should be a number of seconds' }, + nbf: { isValid: isNumber, message: '"nbf" should be a number of seconds' } + }; + function validate(schema, allowUnknown, object, parameterName) { + if (!isPlainObject(object)) { + throw new Error('Expected "' + parameterName + '" to be a plain object.'); + } + Object.keys(object).forEach(function(key) { + const validator = schema[key]; + if (!validator) { + if (!allowUnknown) { + throw new Error('"' + key + '" is not allowed in "' + parameterName + '"'); + } + return; + } + if (!validator.isValid(object[key])) { + throw new Error(validator.message); + } + }); + } + function validateOptions(options) { + return validate(sign_options_schema, false, options, "options"); + } + function validatePayload(payload) { + return validate(registered_claims_schema, true, payload, "payload"); + } + var options_to_payload = { + "audience": "aud", + "issuer": "iss", + "subject": "sub", + "jwtid": "jti" + }; + var options_for_objects = [ + "expiresIn", + "notBefore", + "noTimestamp", + "audience", + "issuer", + "subject", + "jwtid" + ]; + module2.exports = function(payload, secretOrPrivateKey, options, callback) { + if (typeof options === "function") { + callback = options; + options = {}; + } else { + options = options || {}; + } + const isObjectPayload = typeof payload === "object" && !Buffer.isBuffer(payload); + const header = Object.assign({ + alg: options.algorithm || "HS256", + typ: isObjectPayload ? "JWT" : void 0, + kid: options.keyid + }, options.header); + function failure(err) { + if (callback) { + return callback(err); + } + throw err; + } + if (!secretOrPrivateKey && options.algorithm !== "none") { + return failure(new Error("secretOrPrivateKey must have a value")); + } + if (secretOrPrivateKey != null && !(secretOrPrivateKey instanceof KeyObject)) { + try { + secretOrPrivateKey = createPrivateKey(secretOrPrivateKey); + } catch (_) { + try { + secretOrPrivateKey = createSecretKey(typeof secretOrPrivateKey === "string" ? Buffer.from(secretOrPrivateKey) : secretOrPrivateKey); + } catch (_2) { + return failure(new Error("secretOrPrivateKey is not valid key material")); + } + } + } + if (header.alg.startsWith("HS") && secretOrPrivateKey.type !== "secret") { + return failure(new Error(`secretOrPrivateKey must be a symmetric key when using ${header.alg}`)); + } else if (/^(?:RS|PS|ES)/.test(header.alg)) { + if (secretOrPrivateKey.type !== "private") { + return failure(new Error(`secretOrPrivateKey must be an asymmetric key when using ${header.alg}`)); + } + if (!options.allowInsecureKeySizes && !header.alg.startsWith("ES") && secretOrPrivateKey.asymmetricKeyDetails !== void 0 && //KeyObject.asymmetricKeyDetails is supported in Node 15+ + secretOrPrivateKey.asymmetricKeyDetails.modulusLength < 2048) { + return failure(new Error(`secretOrPrivateKey has a minimum key size of 2048 bits for ${header.alg}`)); + } + } + if (typeof payload === "undefined") { + return failure(new Error("payload is required")); + } else if (isObjectPayload) { + try { + validatePayload(payload); + } catch (error) { + return failure(error); + } + if (!options.mutatePayload) { + payload = Object.assign({}, payload); + } + } else { + const invalid_options = options_for_objects.filter(function(opt) { + return typeof options[opt] !== "undefined"; + }); + if (invalid_options.length > 0) { + return failure(new Error("invalid " + invalid_options.join(",") + " option for " + typeof payload + " payload")); + } + } + if (typeof payload.exp !== "undefined" && typeof options.expiresIn !== "undefined") { + return failure(new Error('Bad "options.expiresIn" option the payload already has an "exp" property.')); + } + if (typeof payload.nbf !== "undefined" && typeof options.notBefore !== "undefined") { + return failure(new Error('Bad "options.notBefore" option the payload already has an "nbf" property.')); + } + try { + validateOptions(options); + } catch (error) { + return failure(error); + } + if (!options.allowInvalidAsymmetricKeyTypes) { + try { + validateAsymmetricKey(header.alg, secretOrPrivateKey); + } catch (error) { + return failure(error); + } + } + const timestamp = payload.iat || Math.floor(Date.now() / 1e3); + if (options.noTimestamp) { + delete payload.iat; + } else if (isObjectPayload) { + payload.iat = timestamp; + } + if (typeof options.notBefore !== "undefined") { + try { + payload.nbf = timespan(options.notBefore, timestamp); + } catch (err) { + return failure(err); + } + if (typeof payload.nbf === "undefined") { + return failure(new Error('"notBefore" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + } + if (typeof options.expiresIn !== "undefined" && typeof payload === "object") { + try { + payload.exp = timespan(options.expiresIn, timestamp); + } catch (err) { + return failure(err); + } + if (typeof payload.exp === "undefined") { + return failure(new Error('"expiresIn" should be a number of seconds or string representing a timespan eg: "1d", "20h", 60')); + } + } + Object.keys(options_to_payload).forEach(function(key) { + const claim = options_to_payload[key]; + if (typeof options[key] !== "undefined") { + if (typeof payload[claim] !== "undefined") { + return failure(new Error('Bad "options.' + key + '" option. The payload already has an "' + claim + '" property.')); + } + payload[claim] = options[key]; + } + }); + const encoding = options.encoding || "utf8"; + if (typeof callback === "function") { + callback = callback && once(callback); + jws.createSign({ + header, + privateKey: secretOrPrivateKey, + payload, + encoding + }).once("error", callback).once("done", function(signature) { + if (!options.allowInsecureKeySizes && /^(?:RS|PS)/.test(header.alg) && signature.length < 256) { + return callback(new Error(`secretOrPrivateKey has a minimum key size of 2048 bits for ${header.alg}`)); + } + callback(null, signature); + }); + } else { + let signature = jws.sign({ header, payload, secret: secretOrPrivateKey, encoding }); + if (!options.allowInsecureKeySizes && /^(?:RS|PS)/.test(header.alg) && signature.length < 256) { + throw new Error(`secretOrPrivateKey has a minimum key size of 2048 bits for ${header.alg}`); + } + return signature; + } + }; + } +}); + +// ../../node_modules/jsonwebtoken/index.js +var require_jsonwebtoken = __commonJS({ + "../../node_modules/jsonwebtoken/index.js"(exports2, module2) { + module2.exports = { + decode: require_decode(), + verify: require_verify(), + sign: require_sign(), + JsonWebTokenError: require_JsonWebTokenError(), + NotBeforeError: require_NotBeforeError(), + TokenExpiredError: require_TokenExpiredError() + }; + } +}); + +// src/index.ts +var src_exports = {}; +__export(src_exports, { + plugin: () => plugin +}); +module.exports = __toCommonJS(src_exports); +var import_jsonwebtoken = __toESM(require_jsonwebtoken()); +var plugin = { + authentication: { + name: "JWT", + config: [{ + type: "text", + name: "foo", + label: "Foo", + optional: true + }], + async onApply(_ctx, args) { + const token = import_jsonwebtoken.default.sign({ foo: "bar" }, null); + return { + url: args.url, + headers: [{ name: "Authorization", value: `Bearer ${token}` }] + }; + } + } +}; +// Annotate the CommonJS export names for ESM import in node: +0 && (module.exports = { + plugin +}); +/*! Bundled license information: + +safe-buffer/index.js: + (*! safe-buffer. MIT License. Feross Aboukhadijeh *) +*/ diff --git a/src-tauri/vendored/plugins/auth-jwt/package.json b/src-tauri/vendored/plugins/auth-jwt/package.json new file mode 100644 index 00000000..777f9d9b --- /dev/null +++ b/src-tauri/vendored/plugins/auth-jwt/package.json @@ -0,0 +1,15 @@ +{ + "name": "@yaakapp/auth-jwt", + "private": true, + "version": "0.0.1", + "scripts": { + "build": "yaakcli build ./src/index.ts", + "dev": "yaakcli dev ./src/index.js" + }, + "dependencies": { + "jsonwebtoken": "^9.0.2" + }, + "devDependencies": { + "@types/jsonwebtoken": "^9.0.7" + } +} diff --git a/src-tauri/yaak-grpc/Cargo.toml b/src-tauri/yaak-grpc/Cargo.toml index a99c1e03..1725b2f0 100644 --- a/src-tauri/yaak-grpc/Cargo.toml +++ b/src-tauri/yaak-grpc/Cargo.toml @@ -5,19 +5,20 @@ edition = "2021" publish = false [dependencies] -tonic = "0.10.2" -prost = "0.12" +tonic = "0.12.3" +prost = "0.13.4" tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "fs"] } -tonic-reflection = "0.10.2" +tonic-reflection = "0.12.3" tokio-stream = "0.1.14" -prost-types = "0.12.3" +prost-types = "0.13.4" serde = { version = "1.0.196", features = ["derive"] } serde_json = "1.0.113" -prost-reflect = { version = "0.12.0", features = ["serde", "derive"] } +prost-reflect = { version = "0.14.4", features = ["serde", "derive"] } log = "0.4.20" anyhow = "1.0.79" -hyper = { version = "0.14" } -hyper-rustls = { version = "0.24.0", features = ["http2"] } +hyper = "1.5.2" +hyper-util = { version = "0.1.10", features = ["client-legacy", "client"] } +hyper-rustls = { version = "0.27.5", default-features = false, features = ["http2", "rustls-platform-verifier"] } uuid = { version = "1.7.0", features = ["v4"] } tauri = { workspace = true } tauri-plugin-shell = { workspace = true } diff --git a/src-tauri/yaak-grpc/src/client.rs b/src-tauri/yaak-grpc/src/client.rs new file mode 100644 index 00000000..c7d1afe6 --- /dev/null +++ b/src-tauri/yaak-grpc/src/client.rs @@ -0,0 +1,172 @@ +use crate::transport::get_transport; +use async_recursion::async_recursion; +use hyper_rustls::HttpsConnector; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client; +use log::debug; +use tokio_stream::StreamExt; +use tonic::body::BoxBody; +use tonic::transport::Uri; +use tonic::Request; +use tonic_reflection::pb::v1::server_reflection_request::MessageRequest; +use tonic_reflection::pb::v1::server_reflection_response::MessageResponse; +use tonic_reflection::pb::v1::{ + ErrorResponse, ExtensionNumberResponse, ListServiceResponse, ServerReflectionRequest, + ServiceResponse, +}; +use tonic_reflection::pb::v1::{ExtensionRequest, FileDescriptorResponse}; +use tonic_reflection::pb::{v1, v1alpha}; + +pub struct AutoReflectionClient, BoxBody>> { + use_v1alpha: bool, + client_v1: v1::server_reflection_client::ServerReflectionClient, + client_v1alpha: v1alpha::server_reflection_client::ServerReflectionClient, +} + +impl AutoReflectionClient { + pub fn new(uri: &Uri) -> Self { + let client_v1 = v1::server_reflection_client::ServerReflectionClient::with_origin( + get_transport(), + uri.clone(), + ); + let client_v1alpha = v1alpha::server_reflection_client::ServerReflectionClient::with_origin( + get_transport(), + uri.clone(), + ); + AutoReflectionClient { + use_v1alpha: false, + client_v1, + client_v1alpha, + } + } + + #[async_recursion] + pub async fn send_reflection_request( + &mut self, + message: MessageRequest, + ) -> Result { + let reflection_request = ServerReflectionRequest { + host: "".into(), // Doesn't matter + message_request: Some(message.clone()), + }; + + if self.use_v1alpha { + let request = Request::new(tokio_stream::once(to_v1alpha_request(reflection_request))); + self.client_v1alpha + .server_reflection_info(request) + .await + .map_err(|e| match e.code() { + tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(), + tonic::Code::Unauthenticated => "Authentication failed".to_string(), + tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(), + _ => e.to_string(), + })? + .into_inner() + .next() + .await + .expect("steamed response") + .map_err(|e| e.to_string())? + .message_response + .ok_or("No reflection response".to_string()) + .map(|resp| to_v1_msg_response(resp)) + } else { + let request = Request::new(tokio_stream::once(reflection_request)); + let resp = self.client_v1.server_reflection_info(request).await; + match resp { + Ok(r) => Ok(r), + Err(e) => match e.code().clone() { + tonic::Code::Unimplemented => { + // If v1 fails, change to v1alpha and try again + debug!("gRPC schema reflection falling back to v1alpha"); + self.use_v1alpha = true; + return self.send_reflection_request(message).await; + } + _ => Err(e), + }, + } + .map_err(|e| match e.code() { + tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(), + tonic::Code::Unauthenticated => "Authentication failed".to_string(), + tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(), + _ => e.to_string(), + })? + .into_inner() + .next() + .await + .expect("steamed response") + .map_err(|e| e.to_string())? + .message_response + .ok_or("No reflection response".to_string()) + } + } +} + +fn to_v1_msg_response( + response: v1alpha::server_reflection_response::MessageResponse, +) -> MessageResponse { + match response { + v1alpha::server_reflection_response::MessageResponse::FileDescriptorResponse(v) => { + MessageResponse::FileDescriptorResponse(FileDescriptorResponse { + file_descriptor_proto: v.file_descriptor_proto, + }) + } + v1alpha::server_reflection_response::MessageResponse::AllExtensionNumbersResponse(v) => { + MessageResponse::AllExtensionNumbersResponse(ExtensionNumberResponse { + extension_number: v.extension_number, + base_type_name: v.base_type_name, + }) + } + v1alpha::server_reflection_response::MessageResponse::ListServicesResponse(v) => { + MessageResponse::ListServicesResponse(ListServiceResponse { + service: v + .service + .iter() + .map(|s| ServiceResponse { + name: s.name.clone(), + }) + .collect(), + }) + } + v1alpha::server_reflection_response::MessageResponse::ErrorResponse(v) => { + MessageResponse::ErrorResponse(ErrorResponse { + error_code: v.error_code, + error_message: v.error_message, + }) + } + } +} + +fn to_v1alpha_request(request: ServerReflectionRequest) -> v1alpha::ServerReflectionRequest { + v1alpha::ServerReflectionRequest { + host: request.host, + message_request: request.message_request.map(|m| to_v1alpha_msg_request(m)), + } +} + +fn to_v1alpha_msg_request( + message: MessageRequest, +) -> v1alpha::server_reflection_request::MessageRequest { + match message { + MessageRequest::FileByFilename(v) => { + v1alpha::server_reflection_request::MessageRequest::FileByFilename(v) + } + MessageRequest::FileContainingSymbol(v) => { + v1alpha::server_reflection_request::MessageRequest::FileContainingSymbol(v) + } + MessageRequest::FileContainingExtension(ExtensionRequest { + extension_number, + containing_type, + }) => v1alpha::server_reflection_request::MessageRequest::FileContainingExtension( + v1alpha::ExtensionRequest { + extension_number, + containing_type, + }, + ), + MessageRequest::AllExtensionNumbersOfType(v) => { + v1alpha::server_reflection_request::MessageRequest::AllExtensionNumbersOfType(v) + } + MessageRequest::ListServices(v) => { + v1alpha::server_reflection_request::MessageRequest::ListServices(v) + } + } +} diff --git a/src-tauri/yaak-grpc/src/lib.rs b/src-tauri/yaak-grpc/src/lib.rs index f05f7382..e49d9ea3 100644 --- a/src-tauri/yaak-grpc/src/lib.rs +++ b/src-tauri/yaak-grpc/src/lib.rs @@ -5,7 +5,9 @@ use serde_json::Deserializer; mod codec; mod json_schema; pub mod manager; -mod proto; +mod reflection; +mod transport; +mod client; pub use tonic::metadata::*; pub use tonic::Code; diff --git a/src-tauri/yaak-grpc/src/manager.rs b/src-tauri/yaak-grpc/src/manager.rs index 348e18e8..a71459c3 100644 --- a/src-tauri/yaak-grpc/src/manager.rs +++ b/src-tauri/yaak-grpc/src/manager.rs @@ -2,9 +2,9 @@ use std::collections::BTreeMap; use std::path::PathBuf; use std::str::FromStr; -use hyper::client::HttpConnector; -use hyper::Client; use hyper_rustls::HttpsConnector; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::client::legacy::Client; pub use prost_reflect::DynamicMessage; use prost_reflect::{DescriptorPool, MethodDescriptor, ServiceDescriptor}; use serde_json::Deserializer; @@ -16,10 +16,11 @@ use tonic::transport::Uri; use tonic::{IntoRequest, IntoStreamingRequest, Request, Response, Status, Streaming}; use crate::codec::DynamicCodec; -use crate::proto::{ - fill_pool_from_files, fill_pool_from_reflection, get_transport, method_desc_to_path, +use crate::reflection::{ + fill_pool_from_files, fill_pool_from_reflection, method_desc_to_path, }; use crate::{json_schema, MethodDefinition, ServiceDefinition}; +use crate::transport::get_transport; #[derive(Clone)] pub struct GrpcConnection { diff --git a/src-tauri/yaak-grpc/src/proto.rs b/src-tauri/yaak-grpc/src/reflection.rs similarity index 78% rename from src-tauri/yaak-grpc/src/proto.rs rename to src-tauri/yaak-grpc/src/reflection.rs index a1b95366..559b93c3 100644 --- a/src-tauri/yaak-grpc/src/proto.rs +++ b/src-tauri/yaak-grpc/src/reflection.rs @@ -3,11 +3,9 @@ use std::ops::Deref; use std::path::PathBuf; use std::str::FromStr; +use crate::client::AutoReflectionClient; use anyhow::anyhow; use async_recursion::async_recursion; -use hyper::client::HttpConnector; -use hyper::Client; -use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; use log::{debug, warn}; use prost::Message; use prost_reflect::{DescriptorPool, MethodDescriptor}; @@ -16,15 +14,10 @@ use tauri::path::BaseDirectory; use tauri::{AppHandle, Manager}; use tauri_plugin_shell::ShellExt; use tokio::fs; -use tokio_stream::StreamExt; -use tonic::body::BoxBody; use tonic::codegen::http::uri::PathAndQuery; use tonic::transport::Uri; -use tonic::Request; -use tonic_reflection::pb::server_reflection_client::ServerReflectionClient; -use tonic_reflection::pb::server_reflection_request::MessageRequest; -use tonic_reflection::pb::server_reflection_response::MessageResponse; -use tonic_reflection::pb::ServerReflectionRequest; +use tonic_reflection::pb::v1::server_reflection_request::MessageRequest; +use tonic_reflection::pb::v1::server_reflection_response::MessageResponse; pub async fn fill_pool_from_files( app_handle: &AppHandle, @@ -98,7 +91,7 @@ pub async fn fill_pool_from_files( pub async fn fill_pool_from_reflection(uri: &Uri) -> Result { let mut pool = DescriptorPool::new(); - let mut client = ServerReflectionClient::with_origin(get_transport(), uri.clone()); + let mut client = AutoReflectionClient::new(uri); for service in list_services(&mut client).await? { if service == "grpc.reflection.v1alpha.ServerReflection" { @@ -114,21 +107,8 @@ pub async fn fill_pool_from_reflection(uri: &Uri) -> Result Client, BoxBody> { - let connector = HttpsConnectorBuilder::new().with_native_roots(); - let connector = connector.https_or_http().enable_http2().wrap_connector({ - let mut http_connector = HttpConnector::new(); - http_connector.enforce_http(false); - http_connector - }); - Client::builder().pool_max_idle_per_host(0).http2_only(true).build(connector) -} - -async fn list_services( - reflect_client: &mut ServerReflectionClient, BoxBody>>, -) -> Result, String> { - let response = - send_reflection_request(reflect_client, MessageRequest::ListServices("".into())).await?; +async fn list_services(client: &mut AutoReflectionClient) -> Result, String> { + let response = client.send_reflection_request(MessageRequest::ListServices("".into())).await?; let list_services_response = match response { MessageResponse::ListServicesResponse(resp) => resp, @@ -141,13 +121,11 @@ async fn list_services( async fn file_descriptor_set_from_service_name( service_name: &str, pool: &mut DescriptorPool, - client: &mut ServerReflectionClient, BoxBody>>, + client: &mut AutoReflectionClient, ) { - let response = match send_reflection_request( - client, - MessageRequest::FileContainingSymbol(service_name.into()), - ) - .await + let response = match client + .send_reflection_request(MessageRequest::FileContainingSymbol(service_name.into())) + .await { Ok(resp) => resp, Err(e) => { @@ -169,7 +147,7 @@ async fn file_descriptor_set_from_service_name( async fn add_file_descriptors_to_pool( fds: Vec>, pool: &mut DescriptorPool, - client: &mut ServerReflectionClient, BoxBody>>, + client: &mut AutoReflectionClient, ) { let mut topo_sort = topology::SimpleTopoSort::new(); let mut fd_mapping = std::collections::HashMap::with_capacity(fds.len()); @@ -198,15 +176,15 @@ async fn add_file_descriptors_to_pool( async fn file_descriptor_set_by_filename( filename: &str, pool: &mut DescriptorPool, - client: &mut ServerReflectionClient, BoxBody>>, + client: &mut AutoReflectionClient, ) { // We already fetched this file if let Some(_) = pool.get_file_by_name(filename) { return; } - let response = - send_reflection_request(client, MessageRequest::FileByFilename(filename.into())).await; + let msg = MessageRequest::FileByFilename(filename.into()); + let response = client.send_reflection_request(msg).await; let file_descriptor_response = match response { Ok(MessageResponse::FileDescriptorResponse(resp)) => resp, Ok(_) => { @@ -222,35 +200,6 @@ async fn file_descriptor_set_by_filename( .await; } -async fn send_reflection_request( - client: &mut ServerReflectionClient, BoxBody>>, - message: MessageRequest, -) -> Result { - let reflection_request = ServerReflectionRequest { - host: "".into(), // Doesn't matter - message_request: Some(message), - }; - - let request = Request::new(tokio_stream::once(reflection_request)); - - client - .server_reflection_info(request) - .await - .map_err(|e| match e.code() { - tonic::Code::Unavailable => "Failed to connect to endpoint".to_string(), - tonic::Code::Unauthenticated => "Authentication failed".to_string(), - tonic::Code::DeadlineExceeded => "Deadline exceeded".to_string(), - _ => e.to_string(), - })? - .into_inner() - .next() - .await - .expect("steamed response") - .map_err(|e| e.to_string())? - .message_response - .ok_or("No reflection response".to_string()) -} - pub fn method_desc_to_path(md: &MethodDescriptor) -> PathAndQuery { let full_name = md.full_name(); let (namespace, method_name) = full_name @@ -292,8 +241,8 @@ mod topology { where T: Eq + std::hash::Hash + Clone, { - type IntoIter = SimpleTopoSortIter; type Item = as Iterator>::Item; + type IntoIter = SimpleTopoSortIter; fn into_iter(self) -> Self::IntoIter { SimpleTopoSortIter::new(self) diff --git a/src-tauri/yaak-grpc/src/transport.rs b/src-tauri/yaak-grpc/src/transport.rs new file mode 100644 index 00000000..58df85dc --- /dev/null +++ b/src-tauri/yaak-grpc/src/transport.rs @@ -0,0 +1,19 @@ +use hyper_rustls::{HttpsConnector, HttpsConnectorBuilder}; +use hyper_util::client::legacy::Client; +use hyper_util::client::legacy::connect::HttpConnector; +use hyper_util::rt::TokioExecutor; +use tonic::body::BoxBody; + +pub(crate) fn get_transport() -> Client, BoxBody> { + let connector = HttpsConnectorBuilder::new().with_platform_verifier(); + let connector = connector.https_or_http().enable_http2().wrap_connector({ + let mut http_connector = HttpConnector::new(); + http_connector.enforce_http(false); + http_connector + }); + Client::builder(TokioExecutor::new()) + .pool_max_idle_per_host(0) + .http2_only(true) + .build(connector) +} + diff --git a/src-tauri/yaak-models/bindings/models.ts b/src-tauri/yaak-models/bindings/models.ts index c58b9698..faad831c 100644 --- a/src-tauri/yaak-models/bindings/models.ts +++ b/src-tauri/yaak-models/bindings/models.ts @@ -26,13 +26,13 @@ export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, up export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end"; -export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, }; +export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, }; export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record, description: string, message: string, metadata: Array, method: string | null, name: string, service: string | null, sortPriority: number, url: string, }; export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record, authenticationType: string | null, body: Record, bodyType: string | null, description: string, headers: Array, method: string, name: string, sortPriority: number, url: string, urlParameters: Array, }; -export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, }; export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, }; @@ -40,7 +40,7 @@ export type HttpResponseHeader = { name: string, value: string, }; export type HttpResponseState = "initialized" | "connected" | "closed"; -export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, }; export type KeyValue = { model: "key_value", createdAt: string, updatedAt: string, key: string, namespace: string, value: string, }; diff --git a/src-tauri/yaak-models/src/models.rs b/src-tauri/yaak-models/src/models.rs index 2d1d1d0f..7197b8dd 100644 --- a/src-tauri/yaak-models/src/models.rs +++ b/src-tauri/yaak-models/src/models.rs @@ -446,7 +446,8 @@ pub struct HttpRequestHeader { pub enabled: bool, pub name: String, pub value: String, - pub id: String, + #[ts(optional, as = "Option")] + pub id: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] @@ -458,7 +459,8 @@ pub struct HttpUrlParameter { pub enabled: bool, pub name: String, pub value: String, - pub id: String, + #[ts(optional, as = "Option")] + pub id: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] @@ -664,7 +666,8 @@ pub struct GrpcMetadataEntry { pub enabled: bool, pub name: String, pub value: String, - pub id: String, + #[ts(optional, as = "Option")] + pub id: Option, } #[derive(Debug, Clone, Serialize, Deserialize, Default, TS)] diff --git a/src-tauri/yaak-plugins/Cargo.toml b/src-tauri/yaak-plugins/Cargo.toml index 18503354..7f9d4489 100644 --- a/src-tauri/yaak-plugins/Cargo.toml +++ b/src-tauri/yaak-plugins/Cargo.toml @@ -13,9 +13,9 @@ serde = { version = "1.0.198", features = ["derive"] } serde_json = "1.0.113" tauri = { workspace = true } tauri-plugin-shell = { workspace = true } -tokio = { version = "1.0", features = ["macros", "rt-multi-thread", "process"] } -tonic = "0.12.1" -ts-rs = "10.0.0" +tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "process"] } +tonic = { version = "0.12.3"} +ts-rs = { workspace = true } thiserror = "2.0.7" yaak-models = { workspace = true } regex = "1.10.6" diff --git a/src-tauri/yaak-plugins/bindings/events.ts b/src-tauri/yaak-plugins/bindings/events.ts index 75268516..de27461c 100644 --- a/src-tauri/yaak-plugins/bindings/events.ts +++ b/src-tauri/yaak-plugins/bindings/events.ts @@ -11,6 +11,10 @@ export type BootRequest = { dir: string, watch: boolean, }; export type BootResponse = { name: string, version: string, capabilities: Array, }; +export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array, }; + +export type CallHttpAuthenticationResponse = { url: string, headers: Array, }; + export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, }; export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, }; @@ -25,10 +29,18 @@ export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "s export type CopyTextRequest = { text: string, }; +export type EmptyPayload = {}; + export type ExportHttpRequestRequest = { httpRequest: HttpRequest, }; export type ExportHttpRequestResponse = { content: string, }; +export type FileFilter = { name: string, +/** + * File extensions to require + */ +extensions: Array, }; + export type FilterRequest = { content: string, filter: string, }; export type FilterResponse = { content: string, }; @@ -37,6 +49,112 @@ export type FindHttpResponsesRequest = { requestId: string, limit?: number, }; export type FindHttpResponsesResponse = { httpResponses: Array, }; +export type FormInput = { "type": "text" } & FormInputText | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest; + +export type FormInputBase = { name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputCheckbox = { name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputFile = { +/** + * The title of the file selection window + */ +title: string, +/** + * Allow selecting multiple files + */ +multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array, name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputHttpRequest = { name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputSelect = { +/** + * The options that will be available in the select input + */ +options: Array, name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type FormInputSelectOption = { name: string, value: string, }; + +export type FormInputText = { +/** + * Placeholder for the text input + */ +placeholder?: string | null, name: string, +/** + * Whether the user must fill in the argument + */ +optional?: boolean, +/** + * The label of the input + */ +label?: string, +/** + * The default value + */ +defaultValue?: string, }; + +export type GetHttpAuthenticationResponse = { name: string, pluginName: string, config: Array, }; + export type GetHttpRequestActionsRequest = Record; export type GetHttpRequestActionsResponse = { actions: Array, pluginRefId: string, }; @@ -47,6 +165,8 @@ export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, }; export type GetTemplateFunctionsResponse = { functions: Array, pluginRefId: string, }; +export type HttpHeader = { name: string, value: string, }; + export type HttpRequestAction = { key: string, label: string, icon?: Icon, }; export type Icon = "copy" | "info" | "check_circle" | "alert_triangle" | "_unknown"; @@ -59,13 +179,7 @@ export type ImportResponse = { resources: ImportResources, }; export type InternalEvent = { id: string, pluginRefId: string, replyId: string | null, payload: InternalEventPayload, windowContext: WindowContext, }; -export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } | { "type": "reload_response" } | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & GetHttpRequestActionsRequest | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" }; - -export type OpenFileFilter = { name: string, -/** - * File extensions to require - */ -extensions: Array, }; +export type InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } & EmptyPayload | { "type": "reload_response" } & EmptyPayload | { "type": "terminate_request" } | { "type": "terminate_response" } | { "type": "import_request" } & ImportRequest | { "type": "import_response" } & ImportResponse | { "type": "filter_request" } & FilterRequest | { "type": "filter_response" } & FilterResponse | { "type": "export_http_request_request" } & ExportHttpRequestRequest | { "type": "export_http_request_response" } & ExportHttpRequestResponse | { "type": "send_http_request_request" } & SendHttpRequestRequest | { "type": "send_http_request_response" } & SendHttpRequestResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_template_functions_request" } | { "type": "get_template_functions_response" } & GetTemplateFunctionsResponse | { "type": "call_template_function_request" } & CallTemplateFunctionRequest | { "type": "call_template_function_response" } & CallTemplateFunctionResponse | { "type": "get_http_authentication_request" } & EmptyPayload | { "type": "get_http_authentication_response" } & GetHttpAuthenticationResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "copy_text_request" } & CopyTextRequest | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "prompt_text_request" } & PromptTextRequest | { "type": "prompt_text_response" } & PromptTextResponse | { "type": "get_http_request_by_id_request" } & GetHttpRequestByIdRequest | { "type": "get_http_request_by_id_response" } & GetHttpRequestByIdResponse | { "type": "find_http_responses_request" } & FindHttpResponsesRequest | { "type": "find_http_responses_response" } & FindHttpResponsesResponse | { "type": "empty_response" } & EmptyPayload; export type PromptTextRequest = { id: string, title: string, label: string, description?: string, defaultValue?: string, placeholder?: string, /** @@ -100,135 +214,7 @@ export type TemplateFunction = { name: string, description?: string, * Also support alternative names. This is useful for not breaking existing * tags when changing the `name` property */ -aliases?: Array, args: Array, }; - -export type TemplateFunctionArg = { "type": "text" } & TemplateFunctionTextArg | { "type": "select" } & TemplateFunctionSelectArg | { "type": "checkbox" } & TemplateFunctionCheckboxArg | { "type": "http_request" } & TemplateFunctionHttpRequestArg | { "type": "file" } & TemplateFunctionFileArg; - -export type TemplateFunctionBaseArg = { -/** - * The name of the argument. Should be `camelCase` format - */ -name: string, -/** - * Whether the user must fill in the argument - */ -optional?: boolean, -/** - * The label of the input - */ -label?: string, -/** - * The default value - */ -defaultValue?: string, }; - -export type TemplateFunctionCheckboxArg = { -/** - * The name of the argument. Should be `camelCase` format - */ -name: string, -/** - * Whether the user must fill in the argument - */ -optional?: boolean, -/** - * The label of the input - */ -label?: string, -/** - * The default value - */ -defaultValue?: string, }; - -export type TemplateFunctionFileArg = { -/** - * The title of the file selection window - */ -title: string, -/** - * Allow selecting multiple files - */ -multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array, -/** - * The name of the argument. Should be `camelCase` format - */ -name: string, -/** - * Whether the user must fill in the argument - */ -optional?: boolean, -/** - * The label of the input - */ -label?: string, -/** - * The default value - */ -defaultValue?: string, }; - -export type TemplateFunctionHttpRequestArg = { -/** - * The name of the argument. Should be `camelCase` format - */ -name: string, -/** - * Whether the user must fill in the argument - */ -optional?: boolean, -/** - * The label of the input - */ -label?: string, -/** - * The default value - */ -defaultValue?: string, }; - -export type TemplateFunctionSelectArg = { -/** - * The options that will be available in the select input - */ -options: Array, -/** - * The name of the argument. Should be `camelCase` format - */ -name: string, -/** - * Whether the user must fill in the argument - */ -optional?: boolean, -/** - * The label of the input - */ -label?: string, -/** - * The default value - */ -defaultValue?: string, }; - -export type TemplateFunctionSelectOption = { label: string, value: string, }; - -export type TemplateFunctionTextArg = { -/** - * Placeholder for the text input - */ -placeholder?: string, -/** - * The name of the argument. Should be `camelCase` format - */ -name: string, -/** - * Whether the user must fill in the argument - */ -optional?: boolean, -/** - * The label of the input - */ -label?: string, -/** - * The default value - */ -defaultValue?: string, }; +aliases?: Array, args: Array, }; export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, }; diff --git a/src-tauri/yaak-plugins/bindings/models.ts b/src-tauri/yaak-plugins/bindings/models.ts index 518443ed..42a93e00 100644 --- a/src-tauri/yaak-plugins/bindings/models.ts +++ b/src-tauri/yaak-plugins/bindings/models.ts @@ -6,13 +6,13 @@ export type EnvironmentVariable = { enabled?: boolean, name: string, value: stri export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, }; -export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, }; +export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, }; export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record, description: string, message: string, metadata: Array, method: string | null, name: string, service: string | null, sortPriority: number, url: string, }; export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record, authenticationType: string | null, body: Record, bodyType: string | null, description: string, headers: Array, method: string, name: string, sortPriority: number, url: string, urlParameters: Array, }; -export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, }; export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array, remoteAddr: string | null, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, }; @@ -20,6 +20,6 @@ export type HttpResponseHeader = { name: string, value: string, }; export type HttpResponseState = "initialized" | "connected" | "closed"; -export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, }; export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, name: string, description: string, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, }; diff --git a/src-tauri/yaak-plugins/src/events.rs b/src-tauri/yaak-plugins/src/events.rs index 73afddaa..f24462b5 100644 --- a/src-tauri/yaak-plugins/src/events.rs +++ b/src-tauri/yaak-plugins/src/events.rs @@ -39,8 +39,8 @@ pub enum InternalEventPayload { BootRequest(BootRequest), BootResponse(BootResponse), - ReloadRequest, - ReloadResponse, + ReloadRequest(EmptyPayload), + ReloadResponse(EmptyPayload), TerminateRequest, TerminateResponse, @@ -57,15 +57,22 @@ pub enum InternalEventPayload { SendHttpRequestRequest(SendHttpRequestRequest), SendHttpRequestResponse(SendHttpRequestResponse), - GetHttpRequestActionsRequest(GetHttpRequestActionsRequest), + // Request Actions + GetHttpRequestActionsRequest(EmptyPayload), GetHttpRequestActionsResponse(GetHttpRequestActionsResponse), CallHttpRequestActionRequest(CallHttpRequestActionRequest), + // Template Functions GetTemplateFunctionsRequest, GetTemplateFunctionsResponse(GetTemplateFunctionsResponse), CallTemplateFunctionRequest(CallTemplateFunctionRequest), CallTemplateFunctionResponse(CallTemplateFunctionResponse), + GetHttpAuthenticationRequest(EmptyPayload), + GetHttpAuthenticationResponse(GetHttpAuthenticationResponse), + CallHttpAuthenticationRequest(CallHttpAuthenticationRequest), + CallHttpAuthenticationResponse(CallHttpAuthenticationResponse), + CopyTextRequest(CopyTextRequest), RenderHttpRequestRequest(RenderHttpRequestRequest), @@ -87,9 +94,14 @@ pub enum InternalEventPayload { /// Returned when a plugin doesn't get run, just so the server /// has something to listen for - EmptyResponse, + EmptyResponse(EmptyPayload), } +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] +#[serde(default)] +#[ts(export, type = "{}", export_to = "events.ts")] +pub struct EmptyPayload {} + #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] @@ -281,6 +293,41 @@ pub enum Icon { _Unknown(String), } +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +#[ts(export, export_to = "events.ts")] +pub struct GetHttpAuthenticationResponse { + pub name: String, + pub plugin_name: String, + pub config: Vec, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +#[ts(export, export_to = "events.ts")] +pub struct CallHttpAuthenticationRequest { + pub config: serde_json::Map, + pub method: String, + pub url: String, + pub headers: Vec, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +#[ts(export, export_to = "events.ts")] +pub struct HttpHeader { + pub name: String, + pub value: String, +} + +#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] +#[serde(default, rename_all = "camelCase")] +#[ts(export, export_to = "events.ts")] +pub struct CallHttpAuthenticationResponse { + pub url: String, + pub headers: Vec, +} + #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] @@ -301,25 +348,24 @@ pub struct TemplateFunction { /// tags when changing the `name` property #[ts(optional)] pub aliases: Option>, - pub args: Vec, + pub args: Vec, } #[derive(Debug, Clone, Serialize, Deserialize, TS)] #[serde(rename_all = "snake_case", tag = "type")] #[ts(export, export_to = "events.ts")] -pub enum TemplateFunctionArg { - Text(TemplateFunctionTextArg), - Select(TemplateFunctionSelectArg), - Checkbox(TemplateFunctionCheckboxArg), - HttpRequest(TemplateFunctionHttpRequestArg), - File(TemplateFunctionFileArg), +pub enum FormInput { + Text(FormInputText), + Select(FormInputSelect), + Checkbox(FormInputCheckbox), + File(FormInputFile), + HttpRequest(FormInputHttpRequest), } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct TemplateFunctionBaseArg { - /// The name of the argument. Should be `camelCase` format +pub struct FormInputBase { pub name: String, /// Whether the user must fill in the argument @@ -338,29 +384,29 @@ pub struct TemplateFunctionBaseArg { #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct TemplateFunctionTextArg { +pub struct FormInputText { #[serde(flatten)] - pub base: TemplateFunctionBaseArg, + pub base: FormInputBase, /// Placeholder for the text input - #[ts(optional)] + #[ts(optional = nullable)] pub placeholder: Option, } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct TemplateFunctionHttpRequestArg { +pub struct FormInputHttpRequest { #[serde(flatten)] - pub base: TemplateFunctionBaseArg, + pub base: FormInputBase, } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct TemplateFunctionFileArg { +pub struct FormInputFile { #[serde(flatten)] - pub base: TemplateFunctionBaseArg, + pub base: FormInputBase, /// The title of the file selection window pub title: String, @@ -379,13 +425,13 @@ pub struct TemplateFunctionFileArg { // Specify to only allow selection of certain file extensions #[ts(optional)] - pub filters: Option>, + pub filters: Option>, } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct OpenFileFilter { +pub struct FileFilter { pub name: String, /// File extensions to require pub extensions: Vec, @@ -394,27 +440,27 @@ pub struct OpenFileFilter { #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct TemplateFunctionSelectArg { +pub struct FormInputSelect { #[serde(flatten)] - pub base: TemplateFunctionBaseArg, + pub base: FormInputBase, /// The options that will be available in the select input - pub options: Vec, + pub options: Vec, } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct TemplateFunctionCheckboxArg { +pub struct FormInputCheckbox { #[serde(flatten)] - pub base: TemplateFunctionBaseArg, + pub base: FormInputBase, } #[derive(Debug, Clone, Default, Serialize, Deserialize, TS)] #[serde(default, rename_all = "camelCase")] #[ts(export, export_to = "events.ts")] -pub struct TemplateFunctionSelectOption { - pub label: String, +pub struct FormInputSelectOption { + pub name: String, pub value: String, } diff --git a/src-tauri/yaak-plugins/src/manager.rs b/src-tauri/yaak-plugins/src/manager.rs index acf8da87..5a25b648 100644 --- a/src-tauri/yaak-plugins/src/manager.rs +++ b/src-tauri/yaak-plugins/src/manager.rs @@ -1,9 +1,10 @@ use crate::error::Error::{ClientNotInitializedErr, PluginErr, PluginNotFoundErr, UnknownEventErr}; use crate::error::Result; use crate::events::{ - BootRequest, CallHttpRequestActionRequest, CallTemplateFunctionArgs, - CallTemplateFunctionRequest, CallTemplateFunctionResponse, FilterRequest, FilterResponse, - GetHttpRequestActionsRequest, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, + BootRequest, CallHttpAuthenticationRequest, CallHttpAuthenticationResponse, + CallHttpRequestActionRequest, CallTemplateFunctionArgs, CallTemplateFunctionRequest, + CallTemplateFunctionResponse, EmptyPayload, FilterRequest, FilterResponse, + GetHttpAuthenticationResponse, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose, WindowContext, }; @@ -402,9 +403,7 @@ impl PluginManager { let reply_events = self .send_and_wait( WindowContext::from_window(window), - &InternalEventPayload::GetHttpRequestActionsRequest( - GetHttpRequestActionsRequest {}, - ), + &InternalEventPayload::GetHttpRequestActionsRequest(EmptyPayload {}), ) .await?; @@ -460,6 +459,54 @@ impl PluginManager { Ok(()) } + pub async fn get_http_authentication( + &self, + window: &WebviewWindow, + ) -> Result> { + let window_context = WindowContext::from_window(window); + let reply_events = self + .send_and_wait( + window_context, + &InternalEventPayload::GetHttpAuthenticationRequest(EmptyPayload {}), + ) + .await?; + + let mut results = Vec::new(); + for event in reply_events { + if let InternalEventPayload::GetHttpAuthenticationResponse(resp) = event.payload { + results.push(resp.clone()); + } + } + + Ok(results) + } + + pub async fn call_http_authentication( + &self, + window: &WebviewWindow, + plugin_name: &str, + req: CallHttpAuthenticationRequest, + ) -> Result { + let plugin = self + .get_plugin_by_name(plugin_name) + .await + .ok_or(PluginNotFoundErr(plugin_name.to_string()))?; + let event = self + .send_to_plugin_and_wait( + WindowContext::from_window(window), + &plugin, + &InternalEventPayload::CallHttpAuthenticationRequest(req), + ) + .await?; + match event.payload { + InternalEventPayload::CallHttpAuthenticationResponse(resp) => Ok(resp), + InternalEventPayload::EmptyResponse(_) => { + Err(PluginErr("Auth plugin returned empty".to_string())) + } + e => Err(PluginErr(format!("Auth plugin returned invalid event {:?}", e))), + } + } + pub async fn call_template_function( &self, window_context: WindowContext, @@ -552,7 +599,7 @@ impl PluginManager { match event.payload { InternalEventPayload::FilterResponse(resp) => Ok(resp), - InternalEventPayload::EmptyResponse => { + InternalEventPayload::EmptyResponse(_) => { Err(PluginErr("Filter returned empty".to_string())) } e => Err(PluginErr(format!("Export returned invalid event {:?}", e))), diff --git a/src-tauri/yaak-sync/bindings/models.ts b/src-tauri/yaak-sync/bindings/models.ts index a2fd5b90..ff0ccba6 100644 --- a/src-tauri/yaak-sync/bindings/models.ts +++ b/src-tauri/yaak-sync/bindings/models.ts @@ -6,15 +6,15 @@ export type EnvironmentVariable = { enabled?: boolean, name: string, value: stri export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, name: string, description: string, sortPriority: number, }; -export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id: string, }; +export type GrpcMetadataEntry = { enabled?: boolean, name: string, value: string, id?: string, }; export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record, description: string, message: string, metadata: Array, method: string | null, name: string, service: string | null, sortPriority: number, url: string, }; export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record, authenticationType: string | null, body: Record, bodyType: string | null, description: string, headers: Array, method: string, name: string, sortPriority: number, url: string, urlParameters: Array, }; -export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, }; -export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id: string, }; +export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, }; export type SyncModel = { "type": "workspace" } & Workspace | { "type": "environment" } & Environment | { "type": "folder" } & Folder | { "type": "http_request" } & HttpRequest | { "type": "grpc_request" } & GrpcRequest; diff --git a/src-web/components/DynamicForm.tsx b/src-web/components/DynamicForm.tsx new file mode 100644 index 00000000..92c1edfb --- /dev/null +++ b/src-web/components/DynamicForm.tsx @@ -0,0 +1,251 @@ +import type { Folder, HttpRequest } from '@yaakapp-internal/models'; +import type { + FormInput, + FormInputCheckbox, + FormInputFile, + FormInputHttpRequest, + FormInputSelect, + FormInputText, +} from '@yaakapp-internal/plugins'; +import { useCallback } from 'react'; +import { useActiveRequest } from '../hooks/useActiveRequest'; +import { useFolders } from '../hooks/useFolders'; +import { useHttpRequests } from '../hooks/useHttpRequests'; +import { fallbackRequestName } from '../lib/fallbackRequestName'; +import { Checkbox } from './core/Checkbox'; +import { Input } from './core/Input'; +import { Select } from './core/Select'; +import { SelectFile } from './SelectFile'; + +// eslint-disable-next-line react-refresh/only-export-components +export const DYNAMIC_FORM_NULL_ARG = '__NULL__'; + +export function DynamicForm>({ + config, + data, + onChange, + useTemplating, + stateKey, +}: { + config: FormInput[]; + onChange: (value: T) => void; + data: T; + useTemplating?: boolean; + stateKey: string; +}) { + const setDataAttr = useCallback( + (name: string, value: string | boolean | null) => { + onChange({ ...data, [name]: value == null ? '__NULL__' : value }); + }, + [data, onChange], + ); + + return ( +
+ {config.map((a, i) => { + switch (a.type) { + case 'select': + return ( + setDataAttr(a.name, v)} + value={data[a.name] ? String(data[a.name]) : '__ERROR__'} + /> + ); + case 'text': + return ( + setDataAttr(a.name, v)} + value={data[a.name] ? String(data[a.name]) : ''} + /> + ); + case 'checkbox': + return ( + setDataAttr(a.name, v)} + value={data[a.name] !== undefined ? data[a.name] === true : false} + /> + ); + case 'http_request': + return ( + setDataAttr(a.name, v)} + value={data[a.name] ? String(data[a.name]) : '__ERROR__'} + /> + ); + case 'file': + return ( + setDataAttr(a.name, v)} + filePath={data[a.name] ? String(data[a.name]) : '__ERROR__'} + /> + ); + } + })} +
+ ); +} + +function TextArg({ + arg, + onChange, + value, + useTemplating, + stateKey, +}: { + arg: FormInputText; + value: string; + onChange: (v: string) => void; + useTemplating: boolean; + stateKey: string; +}) { + const handleChange = useCallback( + (value: string) => { + onChange(value === '' ? DYNAMIC_FORM_NULL_ARG : value); + }, + [onChange], + ); + + return ( + + {arg.label ?? arg.name} + {arg.optional && (optional)} + + } + hideLabel={arg.label == null} + placeholder={arg.placeholder ?? arg.defaultValue ?? ''} + useTemplating={useTemplating} + stateKey={stateKey} + forceUpdateKey={stateKey} + /> + ); +} + +function SelectArg({ + arg, + value, + onChange, +}: { + arg: FormInputSelect; + value: string; + onChange: (v: string) => void; +}) { + return ( + { + return { + label: + buildRequestBreadcrumbs(r, folders).join(' / ') + + (r.id == activeRequest?.id ? ' (current)' : ''), + value: r.id, + }; + }), + ]} + /> + ); +} + +function buildRequestBreadcrumbs(request: HttpRequest, folders: Folder[]): string[] { + const ancestors: (HttpRequest | Folder)[] = [request]; + + const next = () => { + const latest = ancestors[0]; + if (latest == null) return []; + + const parent = folders.find((f) => f.id === latest.folderId); + if (parent == null) return; + + ancestors.unshift(parent); + next(); + }; + next(); + + return ancestors.map((a) => (a.model === 'folder' ? a.name : fallbackRequestName(a))); +} + +function CheckboxArg({ + arg, + onChange, + value, +}: { + arg: FormInputCheckbox; + value: boolean; + onChange: (v: boolean) => void; +}) { + return ( + + ); +} diff --git a/src-web/components/GlobalHooks.tsx b/src-web/components/GlobalHooks.tsx index 5d4ef69a..3158c55e 100644 --- a/src-web/components/GlobalHooks.tsx +++ b/src-web/components/GlobalHooks.tsx @@ -4,6 +4,7 @@ import type { ShowToastRequest } from '@yaakapp/api'; import { useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace'; import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast'; import { useGenerateThemeCss } from '../hooks/useGenerateThemeCss'; +import { useSubscribeHttpAuthentication } from '../hooks/useHttpAuthentication'; import { useListenToTauriEvent } from '../hooks/useListenToTauriEvent'; import { useNotificationToast } from '../hooks/useNotificationToast'; import { useSyncFontSizeSetting } from '../hooks/useSyncFontSizeSetting'; @@ -24,6 +25,7 @@ export function GlobalHooks() { useSyncWorkspaceChildModels(); useSubscribeTemplateFunctions(); + useSubscribeHttpAuthentication(); // Other useful things useNotificationToast(); diff --git a/src-web/components/GrpcConnectionSetupPane.tsx b/src-web/components/GrpcConnectionSetupPane.tsx index 56617a78..45d480bd 100644 --- a/src-web/components/GrpcConnectionSetupPane.tsx +++ b/src-web/components/GrpcConnectionSetupPane.tsx @@ -6,12 +6,10 @@ import type { CSSProperties } from 'react'; import React, { useCallback, useMemo, useRef } from 'react'; import { useContainerSize } from '../hooks/useContainerQuery'; import type { ReflectResponseService } from '../hooks/useGrpc'; +import { useHttpAuthentication } from '../hooks/useHttpAuthentication'; import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey'; import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest'; import { fallbackRequestName } from '../lib/fallbackRequestName'; -import { AUTH_TYPE_BASIC, AUTH_TYPE_BEARER, AUTH_TYPE_NONE } from '../lib/model_util'; -import { BasicAuth } from './BasicAuth'; -import { BearerAuth } from './BearerAuth'; import { Button } from './core/Button'; import { CountBadge } from './core/CountBadge'; import { Icon } from './core/Icon'; @@ -22,8 +20,8 @@ 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 { EmptyStateText } from './EmptyStateText'; import { GrpcEditor } from './GrpcEditor'; +import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; import { MarkdownEditor } from './MarkdownEditor'; import { UrlBar } from './UrlBar'; @@ -71,6 +69,7 @@ export function GrpcConnectionSetupPane({ onSend, }: Props) { const updateRequest = useUpdateAnyGrpcRequest(); + const authentication = useHttpAuthentication(); const [activeTabs, setActiveTabs] = useAtom(tabsAtom); const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null); @@ -136,11 +135,6 @@ export function GrpcConnectionSetupPane({ const tabs: TabItem[] = useMemo( () => [ - { - value: TAB_DESCRIPTION, - label: 'Info', - rightSlot: activeRequest.description && , - }, { value: TAB_MESSAGE, label: 'Message' }, { value: TAB_AUTH, @@ -148,24 +142,21 @@ export function GrpcConnectionSetupPane({ options: { value: activeRequest.authenticationType, items: [ - { label: 'Basic Auth', shortLabel: 'Basic', value: AUTH_TYPE_BASIC }, - { label: 'Bearer Token', shortLabel: 'Bearer', value: AUTH_TYPE_BEARER }, + ...authentication.map((a) => ({ + label: a.name, + value: a.pluginName, + })), { type: 'separator' }, - { label: 'No Authentication', shortLabel: 'Auth', value: AUTH_TYPE_NONE }, + { label: 'No Authentication', shortLabel: 'Auth', value: null }, ], - onChange: async (authenticationType) => { + onChange: (authenticationType) => { let authentication: GrpcRequest['authentication'] = activeRequest.authentication; - if (authenticationType === AUTH_TYPE_BASIC) { + if (activeRequest.authenticationType !== authenticationType) { authentication = { - username: authentication.username ?? '', - password: authentication.password ?? '', - }; - } else if (authenticationType === AUTH_TYPE_BEARER) { - authentication = { - token: authentication.token ?? '', + // Reset auth if changing types }; } - await updateRequest.mutateAsync({ + updateRequest.mutate({ id: activeRequest.id, update: { authenticationType, authentication }, }); @@ -173,12 +164,18 @@ export function GrpcConnectionSetupPane({ }, }, { value: TAB_METADATA, label: 'Metadata' }, + { + value: TAB_DESCRIPTION, + label: 'Info', + rightSlot: activeRequest.description && , + }, ], [ activeRequest.authentication, activeRequest.authenticationType, activeRequest.description, activeRequest.id, + authentication, updateRequest, ], ); @@ -213,6 +210,7 @@ export function GrpcConnectionSetupPane({ )} > - - {activeRequest.authenticationType === AUTH_TYPE_BASIC ? ( - - ) : activeRequest.authenticationType === AUTH_TYPE_BEARER ? ( - - ) : ( - No Authentication {activeRequest.authenticationType} - )} + + - + a.pluginName === request.authenticationType); + + const handleChange = useCallback( + (authentication: Record) => { + if (request.model === 'http_request') { + updateHttpRequest.mutate({ + id: request.id, + update: (r) => ({ ...r, authentication }), + }); + } else { + updateGrpcRequest.mutate({ + id: request.id, + update: (r) => ({ ...r, authentication }), + }); + } + }, + [request.id, request.model, updateGrpcRequest, updateHttpRequest], + ); + + if (auth == null) { + return No Authentication {request.authenticationType}; + } + + return ( + + ); +} diff --git a/src-web/components/ImportCurlButton.tsx b/src-web/components/ImportCurlButton.tsx index d1af4bda..8fde74e7 100644 --- a/src-web/components/ImportCurlButton.tsx +++ b/src-web/components/ImportCurlButton.tsx @@ -32,8 +32,9 @@ export function ImportCurlButton() { variant="border" color="success" className="rounded-full" - leftSlot={} + rightSlot={} isLoading={isLoading} + title="Import Curl command from clipboard" onClick={async () => { setIsLoading(true); try { diff --git a/src-web/components/RecentRequestsDropdown.tsx b/src-web/components/RecentRequestsDropdown.tsx index 7367d8f5..b6d147aa 100644 --- a/src-web/components/RecentRequestsDropdown.tsx +++ b/src-web/components/RecentRequestsDropdown.tsx @@ -1,11 +1,11 @@ import classNames from 'classnames'; import { useMemo, useRef } from 'react'; -import { useKeyPressEvent } from 'react-use'; import { useActiveRequest } from '../hooks/useActiveRequest'; import { getActiveWorkspaceId } from '../hooks/useActiveWorkspace'; import { grpcRequestsAtom } from '../hooks/useGrpcRequests'; import { useHotKey } from '../hooks/useHotKey'; import { httpRequestsAtom } from '../hooks/useHttpRequests'; +import { useKeyboardEvent } from '../hooks/useKeyboardEvent'; import { useRecentRequests } from '../hooks/useRecentRequests'; import { fallbackRequestName } from '../lib/fallbackRequestName'; import { jotaiStore } from '../lib/jotai'; @@ -27,16 +27,16 @@ export function RecentRequestsDropdown({ className }: Props) { // Handle key-up // TODO: Somehow make useHotKey have this functionality. Note: e.key does not work // on Linux, for example, when Control is mapped to CAPS. This will never fire. - useKeyPressEvent('Control', undefined, () => { - if (!dropdownRef.current?.isOpen) return; - dropdownRef.current?.select?.(); + useKeyboardEvent('keyup', 'Control', () => { + if (dropdownRef.current?.isOpen) { + dropdownRef.current?.select?.(); + } }); useHotKey('request_switcher.prev', () => { if (!dropdownRef.current?.isOpen) { - dropdownRef.current?.open(); // Select the second because the first is the current request - dropdownRef.current?.next?.(2); + dropdownRef.current?.open(1); } else { dropdownRef.current?.next?.(); } diff --git a/src-web/components/RecentResponsesDropdown.tsx b/src-web/components/RecentResponsesDropdown.tsx index 9879122b..581e3277 100644 --- a/src-web/components/RecentResponsesDropdown.tsx +++ b/src-web/components/RecentResponsesDropdown.tsx @@ -52,7 +52,6 @@ export const RecentResponsesDropdown = function ResponsePane({ label: 'Delete', leftSlot: , onSelect: deleteResponse.mutate, - disabled: activeResponse.state !== 'closed', }, { key: 'unpin', diff --git a/src-web/components/RequestPane.tsx b/src-web/components/RequestPane.tsx index 503e7bd7..cbf33247 100644 --- a/src-web/components/RequestPane.tsx +++ b/src-web/components/RequestPane.tsx @@ -8,6 +8,7 @@ import { activeRequestIdAtom } from '../hooks/useActiveRequestId'; import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse'; import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders'; import { grpcRequestsAtom } from '../hooks/useGrpcRequests'; +import { useHttpAuthentication } from '../hooks/useHttpAuthentication'; import { httpRequestsAtom } from '../hooks/useHttpRequests'; import { useImportCurl } from '../hooks/useImportCurl'; import { useImportQuerystring } from '../hooks/useImportQuerystring'; @@ -23,9 +24,6 @@ import { fallbackRequestName } from '../lib/fallbackRequestName'; import { tryFormatJson } from '../lib/formatters'; import { generateId } from '../lib/generateId'; import { - AUTH_TYPE_BASIC, - AUTH_TYPE_BEARER, - AUTH_TYPE_NONE, BODY_TYPE_BINARY, BODY_TYPE_FORM_MULTIPART, BODY_TYPE_FORM_URLENCODED, @@ -36,8 +34,6 @@ import { BODY_TYPE_XML, } from '../lib/model_util'; import { showToast } from '../lib/toast'; -import { BasicAuth } from './BasicAuth'; -import { BearerAuth } from './BearerAuth'; import { BinaryFileEditor } from './BinaryFileEditor'; import { CountBadge } from './core/CountBadge'; import { Editor } from './core/Editor/Editor'; @@ -55,6 +51,7 @@ import { FormMultipartEditor } from './FormMultipartEditor'; import { FormUrlencodedEditor } from './FormUrlencodedEditor'; import { GraphQLEditor } from './GraphQLEditor'; import { HeadersEditor } from './HeadersEditor'; +import { HttpAuthenticationEditor } from './HttpAuthenticationEditor'; import { MarkdownEditor } from './MarkdownEditor'; import { UrlBar } from './UrlBar'; import { UrlParametersEditor } from './UrlParameterEditor'; @@ -97,6 +94,7 @@ export const RequestPane = memo(function RequestPane({ const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null); const [{ urlKey }] = useRequestEditor(); const contentType = useContentTypeFromHeaders(activeRequest.headers); + const authentication = useHttpAuthentication(); const handleContentTypeChange = useCallback( async (contentType: string | null) => { @@ -236,21 +234,18 @@ export const RequestPane = memo(function RequestPane({ options: { value: activeRequest.authenticationType, items: [ - { label: 'Basic Auth', shortLabel: 'Basic', value: AUTH_TYPE_BASIC }, - { label: 'Bearer Token', shortLabel: 'Bearer', value: AUTH_TYPE_BEARER }, + ...authentication.map((a) => ({ + label: a.name, + value: a.pluginName, + })), { type: 'separator' }, - { label: 'No Authentication', shortLabel: 'Auth', value: AUTH_TYPE_NONE }, + { label: 'No Authentication', shortLabel: 'Auth', value: null }, ], onChange: async (authenticationType) => { let authentication: HttpRequest['authentication'] = activeRequest.authentication; - if (authenticationType === AUTH_TYPE_BASIC) { + if (activeRequest.authenticationType !== authenticationType) { authentication = { - username: authentication.username ?? '', - password: authentication.password ?? '', - }; - } else if (authenticationType === AUTH_TYPE_BEARER) { - authentication = { - token: authentication.token ?? '', + // Reset auth if changing types }; } updateRequest({ @@ -272,6 +267,7 @@ export const RequestPane = memo(function RequestPane({ activeRequest.headers, activeRequest.method, activeRequestId, + authentication, handleContentTypeChange, numParams, updateRequest, @@ -384,15 +380,7 @@ export const RequestPane = memo(function RequestPane({ tabListClassName="mt-2 !mb-1.5" > - {activeRequest.authenticationType === AUTH_TYPE_BASIC ? ( - - ) : activeRequest.authenticationType === AUTH_TYPE_BEARER ? ( - - ) : ( - - No Authentication {activeRequest.authenticationType} - - )} + { - setArgValues((v) => ({ ...v, [name]: value == null ? '__NULL__' : value })); - }, []); - const tokens: Tokens = useMemo(() => { const argTokens: FnArg[] = Object.keys(argValues).map((name) => ({ name, value: - argValues[name] === NULL_ARG + argValues[name] === DYNAMIC_FORM_NULL_ARG ? { type: 'null' } : typeof argValues[name] === 'boolean' ? { type: 'bool', value: argValues[name] === true } @@ -100,57 +78,12 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens, return (

{templateFunction.name}(…)

- - {templateFunction.args.map((a: TemplateFunctionArg, i: number) => { - switch (a.type) { - case 'select': - return ( - setArgValue(a.name, v)} - value={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'} - /> - ); - case 'text': - return ( - setArgValue(a.name, v)} - value={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'} - /> - ); - case 'checkbox': - return ( - setArgValue(a.name, v)} - value={argValues[a.name] !== undefined ? argValues[a.name] === true : false} - /> - ); - case 'http_request': - return ( - setArgValue(a.name, v)} - value={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'} - /> - ); - case 'file': - return ( - setArgValue(a.name, v)} - filePath={argValues[a.name] ? String(argValues[a.name]) : '__ERROR__'} - /> - ); - } - })} - +
Preview
); } - -function TextArg({ - arg, - onChange, - value, -}: { - arg: TemplateFunctionTextArg; - value: string; - onChange: (v: string) => void; -}) { - const handleChange = useCallback( - (value: string) => { - onChange(value === '' ? NULL_ARG : value); - }, - [onChange], - ); - - return ( - - {arg.label ?? arg.name} - {arg.optional && (optional)} - - } - hideLabel={arg.label == null} - placeholder={arg.placeholder ?? arg.defaultValue ?? ''} - /> - ); -} - -function SelectArg({ - arg, - value, - onChange, -}: { - arg: TemplateFunctionSelectArg; - value: string; - onChange: (v: string) => void; -}) { - return ( - { - return { - label: buildRequestBreadcrumbs(r, folders).join(' / ') + (r.id == activeRequest?.id ? ' (current)' : ''), - value: r.id, - }; - }), - ]} - /> - ); -} - -function buildRequestBreadcrumbs(request: HttpRequest, folders: Folder[]): string[] { - const ancestors: (HttpRequest | Folder)[] = [request]; - - const next = () => { - const latest = ancestors[0]; - if (latest == null) return []; - - const parent = folders.find((f) => f.id === latest.folderId); - if (parent == null) return; - - ancestors.unshift(parent); - next(); - }; - next(); - - return ancestors.map((a) => (a.model === 'folder' ? a.name : fallbackRequestName(a))); -} - -function CheckboxArg({ - arg, - onChange, - value, -}: { - arg: TemplateFunctionCheckboxArg; - value: boolean; - onChange: (v: boolean) => void; -}) { - return ( - - ); -} diff --git a/src-web/components/core/BulkPairEditor.tsx b/src-web/components/core/BulkPairEditor.tsx index 6c195ace..26e62f9c 100644 --- a/src-web/components/core/BulkPairEditor.tsx +++ b/src-web/components/core/BulkPairEditor.tsx @@ -1,7 +1,7 @@ import { useCallback, useMemo } from 'react'; import { generateId } from '../../lib/generateId'; import { Editor } from './Editor/Editor'; -import type { PairEditorProps } from './PairEditor'; +import type { PairEditorProps, PairWithId } from './PairEditor'; type Props = PairEditorProps; @@ -45,14 +45,12 @@ export function BulkPairEditor({ ); } -function lineToPair(line: string): PairEditorProps['pairs'][0] { +function lineToPair(line: string): PairWithId { const [, name, value] = line.match(/^(:?[^:]+):\s+(.*)$/) ?? []; - - const pair: PairEditorProps['pairs'][0] = { + return { enabled: true, name: (name ?? '').trim(), value: (value ?? '').trim(), id: generateId(), }; - return pair; } diff --git a/src-web/components/core/Dropdown.tsx b/src-web/components/core/Dropdown.tsx index 13cd4a72..28563cc3 100644 --- a/src-web/components/core/Dropdown.tsx +++ b/src-web/components/core/Dropdown.tsx @@ -1,6 +1,6 @@ import classNames from 'classnames'; import { motion } from 'framer-motion'; -import { atom, useAtom } from 'jotai'; +import { atom } from 'jotai'; import type { CSSProperties, FocusEvent as ReactFocusEvent, @@ -12,11 +12,11 @@ import type { SetStateAction, } from 'react'; import React, { - useEffect, Children, cloneElement, forwardRef, useCallback, + useEffect, useImperativeHandle, useMemo, useRef, @@ -29,6 +29,7 @@ import { useHotKey } from '../../hooks/useHotKey'; import { useStateWithDeps } from '../../hooks/useStateWithDeps'; import { generateId } from '../../lib/generateId'; import { getNodeText } from '../../lib/getNodeText'; +import { jotaiStore } from '../../lib/jotai'; import { Overlay } from '../Overlay'; import { Button } from './Button'; import { HotKey } from './HotKey'; @@ -62,15 +63,13 @@ export type DropdownItem = DropdownItemDefault | DropdownItemSeparator; export interface DropdownProps { children: ReactElement>; items: DropdownItem[]; - onOpen?: () => void; - onClose?: () => void; fullWidth?: boolean; hotKeyAction?: HotkeyAction; } export interface DropdownRef { isOpen: boolean; - open: () => void; + open: (index?: number) => void; toggle: () => void; close?: () => void; next?: (incrBy?: number) => void; @@ -84,46 +83,52 @@ export interface DropdownRef { const openAtom = atom(null); export const Dropdown = forwardRef(function Dropdown( - { children, items, onOpen, onClose, hotKeyAction, fullWidth }: DropdownProps, + { children, items, hotKeyAction, fullWidth }: DropdownProps, ref, ) { - const id = useRef(generateId()).current; - const [openId, setOpenId] = useAtom(openAtom); - const isOpen = openId === id; + const id = useRef(generateId()); + const [isOpen, _setIsOpen] = useState(false); + + useEffect(() => { + return jotaiStore.sub(openAtom, () => { + const globalOpenId = jotaiStore.get(openAtom); + const newIsOpen = globalOpenId === id.current; + if (newIsOpen !== isOpen) { + _setIsOpen(newIsOpen); + } + }); + }, [isOpen, _setIsOpen]); // const [isOpen, _setIsOpen] = useState(false); const [defaultSelectedIndex, setDefaultSelectedIndex] = useState(null); const buttonRef = useRef(null); const menuRef = useRef>(null); - const setIsOpen = useCallback( - (o: SetStateAction) => { - setOpenId((prevId) => { - const prevIsOpen = prevId === id; - const newIsOpen = typeof o === 'function' ? o(prevIsOpen) : o; - return newIsOpen ? id : null; // Set global atom to current ID to signify open state - }); - }, - [id, setOpenId], - ); - - useEffect(() => { - if (isOpen) { + const setIsOpen = useCallback((o: SetStateAction) => { + jotaiStore.set(openAtom, (prevId) => { + const prevIsOpen = prevId === id.current; + const newIsOpen = typeof o === 'function' ? o(prevIsOpen) : o; // Persist background color of button until we close the dropdown - buttonRef.current!.style.backgroundColor = window - .getComputedStyle(buttonRef.current!) - .getPropertyValue('background-color'); - onOpen?.(); - } else { - onClose?.(); + if (newIsOpen) { + buttonRef.current!.style.backgroundColor = window + .getComputedStyle(buttonRef.current!) + .getPropertyValue('background-color'); + } + return newIsOpen ? id.current : null; // Set global atom to current ID to signify open state + }); + }, []); + + // Because a different dropdown can cause ours to close, a useEffect([isOpen]) is the only method + // we have of detecting the dropdown closed, to do cleanup. + useEffect(() => { + if (!isOpen) { buttonRef.current?.focus(); // Focus button buttonRef.current!.style.backgroundColor = ''; // Clear persisted BG + // Set to different value when opened and closed to force it to update. This is to force + // to reset its selected-index state, which it does when this prop changes + setDefaultSelectedIndex(null); } - - // Set to different value when opened and closed to force it to update. This is to force - // to reset its selected-index state, which it does when this prop changes - setDefaultSelectedIndex(isOpen ? -1 : null); - }, [isOpen, onClose, onOpen]); + }, [isOpen]); // Pull into variable so linter forces us to add it as a hook dep to useImperativeHandle. If we don't, // the ref will not update when menuRef updates, causing stale callback state to be used. @@ -138,8 +143,9 @@ export const Dropdown = forwardRef(function Dropdown if (!isOpen) this.open(); else this.close(); }, - open() { + open(index?: number) { setIsOpen(true); + setDefaultSelectedIndex(index ?? -1); }, close() { setIsOpen(false); @@ -264,11 +270,18 @@ const Menu = forwardRef( - defaultSelectedIndex ?? null, + defaultSelectedIndex ?? -1, [defaultSelectedIndex], ); const [filter, setFilter] = useState(''); + // HACK: Use a ref to track selectedIndex so our closure functions (eg. select()) can + // have access to the latest value. + const selectedIndexRef = useRef(selectedIndex); + useEffect(() => { + selectedIndexRef.current = selectedIndex; + }, [selectedIndex]); + const handleClose = useCallback(() => { onClose(); setFilter(''); @@ -380,12 +393,12 @@ const Menu = forwardRef(function Editor( { readOnly, @@ -126,7 +127,6 @@ export const Editor = forwardRef(function E ) { const settings = useSettings(); - const templateFunctions = useTemplateFunctions(); const allEnvironmentVariables = useActiveEnvironmentVariables(); const environmentVariables = autocompleteVariables ? allEnvironmentVariables : emptyVariables; @@ -178,7 +178,7 @@ export const Editor = forwardRef(function E useEffect( function configurePlaceholder() { if (cm.current === null) return; - const ext = placeholderExt(placeholderElFromText(placeholder ?? '')); + const ext = placeholderExt(placeholderElFromText(placeholder || emptyPlaceholder)); const effect = placeholderCompartment.current.reconfigure(ext); cm.current?.view.dispatch({ effects: effect }); }, @@ -300,7 +300,7 @@ export const Editor = forwardRef(function E [focusParamValue], ); - const completionOptions = useTwigCompletionOptions(onClickFunction); + const completionOptions = useTemplateFunctionCompletionOptions(onClickFunction); // Update the language extension when the language changes useEffect(() => { @@ -322,7 +322,6 @@ export const Editor = forwardRef(function E autocomplete, useTemplating, environmentVariables, - templateFunctions, onClickFunction, onClickVariable, onClickMissingVariable, @@ -355,7 +354,7 @@ export const Editor = forwardRef(function E const extensions = [ languageCompartment.of(langExt), placeholderCompartment.current.of( - placeholderExt(placeholderElFromText(placeholder ?? '')), + placeholderExt(placeholderElFromText(placeholder || emptyPlaceholder)), ), wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : []), keymapCompartment.current.of( @@ -601,13 +600,16 @@ const placeholderElFromText = (text: string) => { function saveCachedEditorState(stateKey: string | null, state: EditorState | null) { if (!stateKey || state == null) return; - sessionStorage.setItem(stateKey, JSON.stringify(state.toJSON(stateFields))); + sessionStorage.setItem( + computeFullStateKey(stateKey), + JSON.stringify(state.toJSON(stateFields)), + ); } function getCachedEditorState(doc: string, stateKey: string | null) { if (stateKey == null) return; - const stateStr = sessionStorage.getItem(stateKey); + const stateStr = sessionStorage.getItem(computeFullStateKey(stateKey)); if (stateStr == null) return null; try { @@ -621,3 +623,7 @@ function getCachedEditorState(doc: string, stateKey: string | null) { return null; } + +function computeFullStateKey(stateKey: string): string { + return `editor.${stateKey}`; +} diff --git a/src-web/components/core/Icon.tsx b/src-web/components/core/Icon.tsx index b02095e3..207723aa 100644 --- a/src-web/components/core/Icon.tsx +++ b/src-web/components/core/Icon.tsx @@ -56,6 +56,7 @@ const icons = { help: lucide.CircleHelpIcon, history: lucide.HistoryIcon, house: lucide.HomeIcon, + import: lucide.ImportIcon, info: lucide.InfoIcon, keyboard: lucide.KeyboardIcon, left_panel_hidden: lucide.PanelLeftOpenIcon, diff --git a/src-web/components/core/Input.tsx b/src-web/components/core/Input.tsx index d73af7ab..02f99500 100644 --- a/src-web/components/core/Input.tsx +++ b/src-web/components/core/Input.tsx @@ -1,7 +1,7 @@ import classNames from 'classnames'; import type { EditorView } from 'codemirror'; import type { ReactNode } from 'react'; -import { forwardRef, useCallback, useMemo, useRef, useState } from 'react'; +import { forwardRef, useCallback, useImperativeHandle, useMemo, useRef, useState } from 'react'; import { useStateWithDeps } from '../../hooks/useStateWithDeps'; import type { EditorProps } from './Editor/Editor'; import { Editor } from './Editor/Editor'; @@ -46,7 +46,7 @@ export type InputProps = Pick< stateKey: EditorProps['stateKey']; }; -export const Input = forwardRef(function Input( +export const Input = forwardRef(function Input( { className, containerClassName, @@ -79,6 +79,8 @@ export const Input = forwardRef(function Inp const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]); const [currentValue, setCurrentValue] = useState(defaultValue ?? ''); const [focused, setFocused] = useState(false); + const editorRef = useRef(null); + useImperativeHandle(ref, () => editorRef.current); const handleFocus = useCallback(() => { if (readOnly) return; @@ -88,6 +90,7 @@ export const Input = forwardRef(function Inp const handleBlur = useCallback(() => { setFocused(false); + editorRef.current?.dispatch({ selection: { anchor: 0 } }); onBlur?.(); }, [onBlur]); @@ -164,7 +167,7 @@ export const Input = forwardRef(function Inp )} > void; + onChange: (pairs: PairWithId[]) => void; pairs: Pair[]; stateKey: InputProps['stateKey']; valueAutocomplete?: (name: string) => GenericCompletionConfig | undefined; @@ -54,7 +54,7 @@ export type PairEditorProps = { }; export type Pair = { - id: string; + id?: string; enabled?: boolean; name: string; value: string; @@ -63,6 +63,10 @@ export type Pair = { readOnlyName?: boolean; }; +export type PairWithId = Pair & { + id: string; +}; + /** Max number of pairs to show before prompting the user to reveal the rest */ const MAX_INITIAL_PAIRS = 50; @@ -90,7 +94,7 @@ export const PairEditor = forwardRef(function Pa const [forceFocusNamePairId, setForceFocusNamePairId] = useState(null); const [forceFocusValuePairId, setForceFocusValuePairId] = useState(null); const [hoveredIndex, setHoveredIndex] = useState(null); - const [pairs, setPairs] = useState([]); + const [pairs, setPairs] = useState([]); const [showAll, toggleShowAll] = useToggle(false); useImperativeHandle( @@ -105,14 +109,13 @@ export const PairEditor = forwardRef(function Pa ); useEffect(() => { - // Remove empty headers on initial render and ensure they all have valid ids (pairs didn't used to have IDs) - const newPairs = []; + // Remove empty headers on initial render and ensure they all have valid ids (pairs didn't use to have IDs) + const newPairs: PairWithId[] = []; for (let i = 0; i < originalPairs.length; i++) { const p = originalPairs[i]; if (!p) continue; // Make TS happy if (isPairEmpty(p)) continue; - if (!p.id) p.id = generateId(); - newPairs.push(p); + newPairs.push({ ...p, id: p.id ?? generateId() }); } // Add empty last pair if there is none @@ -127,7 +130,7 @@ export const PairEditor = forwardRef(function Pa }, [forceUpdateKey]); const setPairsAndSave = useCallback( - (fn: (pairs: Pair[]) => Pair[]) => { + (fn: (pairs: PairWithId[]) => PairWithId[]) => { setPairs((oldPairs) => { const pairs = fn(oldPairs); onChange(pairs); @@ -165,7 +168,7 @@ export const PairEditor = forwardRef(function Pa ); const handleChange = useCallback( - (pair: Pair) => setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))), + (pair: PairWithId) => setPairsAndSave((pairs) => pairs.map((p) => (pair.id !== p.id ? p : pair))), [setPairsAndSave], ); @@ -267,15 +270,15 @@ enum ItemTypes { type PairEditorRowProps = { className?: string; - pair: Pair; + pair: PairWithId; forceFocusNamePairId?: string | null; forceFocusValuePairId?: string | null; onMove: (id: string, side: 'above' | 'below') => void; onEnd: (id: string) => void; - onChange: (pair: Pair) => void; - onDelete?: (pair: Pair, focusPrevious: boolean) => void; - onFocus?: (pair: Pair) => void; - onSubmit?: (pair: Pair) => void; + onChange: (pair: PairWithId) => void; + onDelete?: (pair: PairWithId, focusPrevious: boolean) => void; + onFocus?: (pair: PairWithId) => void; + onSubmit?: (pair: PairWithId) => void; isLast?: boolean; index: number; } & Pick< @@ -618,7 +621,7 @@ function FileActionsDropdown({ ); } -function emptyPair(): Pair { +function emptyPair(): PairWithId { return { enabled: true, name: '', diff --git a/src-web/hooks/useHttpAuthentication.ts b/src-web/hooks/useHttpAuthentication.ts new file mode 100644 index 00000000..d322a888 --- /dev/null +++ b/src-web/hooks/useHttpAuthentication.ts @@ -0,0 +1,38 @@ +import { useQuery } from '@tanstack/react-query'; +import type { GetHttpAuthenticationResponse } from '@yaakapp-internal/plugins'; +import { useAtomValue } from 'jotai'; +import { atom, useSetAtom } from 'jotai/index'; +import { useState } from 'react'; +import { invokeCmd } from '../lib/tauri'; + +const httpAuthenticationAtom = atom([]); +const orderedHttpAuthenticationAtom = atom((get) => + get(httpAuthenticationAtom).sort((a, b) => a.name.localeCompare(b.name)), +); + +export function useHttpAuthentication() { + return useAtomValue(orderedHttpAuthenticationAtom); +} + +export function useSubscribeHttpAuthentication() { + const [numResults, setNumResults] = useState(0); + const setAtom = useSetAtom(httpAuthenticationAtom); + + useQuery({ + queryKey: ['http_authentication'], + // Fetch periodically until functions are returned + // NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic + // to refetch things until that's working again + // TODO: Update plugin system to wait for plugins to initialize before sending the first event to them + refetchInterval: numResults > 0 ? Infinity : 1000, + refetchOnMount: true, + queryFn: async () => { + const result = await invokeCmd( + 'cmd_get_http_authentication', + ); + setNumResults(result.length); + setAtom(result); + return result; + }, + }); +} diff --git a/src-web/hooks/useKeyboardEvent.ts b/src-web/hooks/useKeyboardEvent.ts new file mode 100644 index 00000000..1ebd01e7 --- /dev/null +++ b/src-web/hooks/useKeyboardEvent.ts @@ -0,0 +1,18 @@ +import { useEffect } from 'react'; + +export function useKeyboardEvent( + event: 'keyup' | 'keydown', + key: KeyboardEvent['key'], + cb: () => void, +) { + useEffect(() => { + const fn = (e: KeyboardEvent) => { + if (e.key === key) cb(); + }; + document.addEventListener(event, fn); + return () => document.removeEventListener(event, fn); + + // Don't have `cb` as a dep for caller convenience + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [event]); +} diff --git a/src-web/hooks/useSendAnyHttpRequest.ts b/src-web/hooks/useSendAnyHttpRequest.ts index 80cd5560..46be3a34 100644 --- a/src-web/hooks/useSendAnyHttpRequest.ts +++ b/src-web/hooks/useSendAnyHttpRequest.ts @@ -1,5 +1,4 @@ import type { HttpResponse } from '@yaakapp-internal/models'; -import { showAlert } from '../lib/alert'; import { trackEvent } from '../lib/analytics'; import { getHttpRequest } from '../lib/store'; import { invokeCmd } from '../lib/tauri'; @@ -23,6 +22,5 @@ export function useSendAnyHttpRequest() { }); }, onSettled: () => trackEvent('http_request', 'send'), - onError: (err) => showAlert({ id: 'send-failed', title: 'Send Failed', body: err }), }); } diff --git a/src-web/hooks/useTemplateFunctions.ts b/src-web/hooks/useTemplateFunctions.ts index d2d08cd7..353324fc 100644 --- a/src-web/hooks/useTemplateFunctions.ts +++ b/src-web/hooks/useTemplateFunctions.ts @@ -9,10 +9,10 @@ import { usePluginsKey } from './usePlugins'; const templateFunctionsAtom = atom([]); -export function useTwigCompletionOptions( +export function useTemplateFunctionCompletionOptions( onClick: (fn: TemplateFunction, ragTag: string, pos: number) => void, ) { - const templateFunctions = useTemplateFunctions(); + const templateFunctions = useAtomValue(templateFunctionsAtom); return useMemo(() => { return ( templateFunctions.map((fn) => { @@ -37,10 +37,6 @@ export function useTwigCompletionOptions( }, [onClick, templateFunctions]); } -export function useTemplateFunctions() { - return useAtomValue(templateFunctionsAtom); -} - export function useSubscribeTemplateFunctions() { const pluginsKey = usePluginsKey(); const [numFns, setNumFns] = useState(0); diff --git a/src-web/lib/model_util.ts b/src-web/lib/model_util.ts index 6bcbc86a..5238735f 100644 --- a/src-web/lib/model_util.ts +++ b/src-web/lib/model_util.ts @@ -16,10 +16,6 @@ export const BODY_TYPE_FORM_URLENCODED = 'application/x-www-form-urlencoded'; export const BODY_TYPE_FORM_MULTIPART = 'multipart/form-data'; export const BODY_TYPE_XML = 'text/xml'; -export const AUTH_TYPE_NONE = null; -export const AUTH_TYPE_BASIC = 'basic'; -export const AUTH_TYPE_BEARER = 'bearer'; - export function cookieDomain(cookie: Cookie): string { if (cookie.domain === 'NotPresent' || cookie.domain === 'Empty') { return 'n/a'; diff --git a/src-web/lib/tauri.ts b/src-web/lib/tauri.ts index 496a8d5e..96623a1e 100644 --- a/src-web/lib/tauri.ts +++ b/src-web/lib/tauri.ts @@ -32,6 +32,7 @@ type TauriCmd = | 'cmd_get_environment' | 'cmd_get_folder' | 'cmd_get_grpc_request' + | 'cmd_get_http_authentication' | 'cmd_get_http_request' | 'cmd_get_sse_events' | 'cmd_get_key_value' diff --git a/src-web/main.css b/src-web/main.css index 638d4c2d..aa44b0bc 100644 --- a/src-web/main.css +++ b/src-web/main.css @@ -13,9 +13,8 @@ font-variant-ligatures: none; } - ::selection, - .cm-selectionBackground { - @apply bg-selection !important; + ::selection { + @apply bg-selection; } /* Disable user selection to make it more "app-like" */ @@ -26,7 +25,7 @@ @apply select-none cursor-default; } - input, + input, textarea { &::placeholder { @apply text-placeholder;