mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-07-06 12:55:24 +02:00
OAuth 2 (#158)
This commit is contained in:
Generated
+44
-44
@@ -20,7 +20,7 @@
|
|||||||
"src-web"
|
"src-web"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2.2.4",
|
"@tauri-apps/cli": "^2.2.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
||||||
"@typescript-eslint/parser": "^8.18.1",
|
"@typescript-eslint/parser": "^8.18.1",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
@@ -2709,9 +2709,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli": {
|
"node_modules/@tauri-apps/cli": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.2.5.tgz",
|
||||||
"integrity": "sha512-pihbuHEWJa9SEcN7JdEbMa0oq28MTTbk0nNNnRG8/irNQTKcjwM+KzxG2wuYZYbsXQVqwSu7PstdIEAnXqYHkw==",
|
"integrity": "sha512-PaefTQUCYYqvZWdH8EhXQkyJEjQwtoy/OHGoPcZx7Gk3D3K6AtGSxZ9OlHIz3Bu5LDGgVBk36vKtHW0WYsWnbw==",
|
||||||
"dev": true,
|
"dev": true,
|
||||||
"license": "Apache-2.0 OR MIT",
|
"license": "Apache-2.0 OR MIT",
|
||||||
"bin": {
|
"bin": {
|
||||||
@@ -2725,22 +2725,22 @@
|
|||||||
"url": "https://opencollective.com/tauri"
|
"url": "https://opencollective.com/tauri"
|
||||||
},
|
},
|
||||||
"optionalDependencies": {
|
"optionalDependencies": {
|
||||||
"@tauri-apps/cli-darwin-arm64": "2.2.4",
|
"@tauri-apps/cli-darwin-arm64": "2.2.5",
|
||||||
"@tauri-apps/cli-darwin-x64": "2.2.4",
|
"@tauri-apps/cli-darwin-x64": "2.2.5",
|
||||||
"@tauri-apps/cli-linux-arm-gnueabihf": "2.2.4",
|
"@tauri-apps/cli-linux-arm-gnueabihf": "2.2.5",
|
||||||
"@tauri-apps/cli-linux-arm64-gnu": "2.2.4",
|
"@tauri-apps/cli-linux-arm64-gnu": "2.2.5",
|
||||||
"@tauri-apps/cli-linux-arm64-musl": "2.2.4",
|
"@tauri-apps/cli-linux-arm64-musl": "2.2.5",
|
||||||
"@tauri-apps/cli-linux-x64-gnu": "2.2.4",
|
"@tauri-apps/cli-linux-x64-gnu": "2.2.5",
|
||||||
"@tauri-apps/cli-linux-x64-musl": "2.2.4",
|
"@tauri-apps/cli-linux-x64-musl": "2.2.5",
|
||||||
"@tauri-apps/cli-win32-arm64-msvc": "2.2.4",
|
"@tauri-apps/cli-win32-arm64-msvc": "2.2.5",
|
||||||
"@tauri-apps/cli-win32-ia32-msvc": "2.2.4",
|
"@tauri-apps/cli-win32-ia32-msvc": "2.2.5",
|
||||||
"@tauri-apps/cli-win32-x64-msvc": "2.2.4"
|
"@tauri-apps/cli-win32-x64-msvc": "2.2.5"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-darwin-arm64": {
|
"node_modules/@tauri-apps/cli-darwin-arm64": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.2.5.tgz",
|
||||||
"integrity": "sha512-+sMLkQBFebn/UENyaXpyQqRkdFQie8RdEvYVz0AGthm2p0lMVlWiBmc4ImBJmfo8569zVeDX8B+5OWt4/AuZzA==",
|
"integrity": "sha512-qdPmypQE7qj62UJy3Wl/ccCJZwsv5gyBByOrAaG7u5c/PB3QSxhNPegice2k4EHeIuApaVJOoe/CEYVgm/og2Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2755,9 +2755,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-darwin-x64": {
|
"node_modules/@tauri-apps/cli-darwin-x64": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.2.5.tgz",
|
||||||
"integrity": "sha512-6fJvXVtQJh7H8q9sll2XC2wO5bpn7bzeh+MQxpcLq6F8SE02sFuNDLN+AqX0DQnuYV0V6jdzM2+bTYOlc1FBsw==",
|
"integrity": "sha512-8JVlCAb2c3n0EcGW7n/1kU4Rq831SsoLDD/0hNp85Um8HGIH2Mg/qos/MLOc8Qv2mOaoKcRKf4hd0I1y0Rl9Cg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2772,9 +2772,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
|
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.2.5.tgz",
|
||||||
"integrity": "sha512-QU6Ac6tx79iqkxsDUQesCBNq8RrVSkP9HhVzS2reKthK3xbdTCwNUXoRlfhudKMVrIxV4K7uTwUV99eAnwbm5Q==",
|
"integrity": "sha512-mzxQCqZg7ljRVgekPpXQ5TOehCNgnXh/DNWU6kFjALaBvaw4fGzc369/hV94wOt29htNFyxf8ty2DaQaYljEHw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm"
|
"arm"
|
||||||
],
|
],
|
||||||
@@ -2789,9 +2789,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
|
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.2.5.tgz",
|
||||||
"integrity": "sha512-uZhp312s6VgJJDgUg+HuHZnhjGg93OT+q/aZMoccdZVQ6dvwH8kJzIkKt9zL1U126AXXoesb1EyYmsAruxaUKA==",
|
"integrity": "sha512-M9nkzx5jsSJSNpp7aSza0qv0/N13SUNzH8ysYSZ7IaCN8coGeMg2KgQ5qC6tqUVij2rbg8A/X1n0pPo/gtLx0A==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2806,9 +2806,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
|
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.5.tgz",
|
||||||
"integrity": "sha512-k6JCXd9E+XU0J48nVcFr3QO//bzKg/gp8ZKagBfI2wBpHOk14CnHNBQKNs11nMQUwko4bnPeqj4llcdkbmwIbw==",
|
"integrity": "sha512-tFhZu950HNRLR1RM5Q9Xj5gAlA6AhyyiZgeoXGFAWto+s2jpWmmA3Qq2GUxnVDr7Xui8PF4UY5kANDIOschuwg==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2823,9 +2823,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
|
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.2.5.tgz",
|
||||||
"integrity": "sha512-bUBPU46OF1pNfM6SsGbUlkCBh/pTzvFlEdUpDISsS40v9NVt+kqCy3tHzLGB412E3lSlA6FnshB6HxkdRRdTtg==",
|
"integrity": "sha512-eaGhTQLr3EKeksGsp2tK/Ndi7/oyo3P53Pye6kg0zqXiqu8LQjg1CgvDm1l+5oit04S60zR4AqlDFpoeEtDGgw==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2840,9 +2840,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-linux-x64-musl": {
|
"node_modules/@tauri-apps/cli-linux-x64-musl": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.5.tgz",
|
||||||
"integrity": "sha512-vOrpsQDiMtP8q/ZeXfXqgNi3G4Yv5LVX2vI5XkB2yvVuVF1Dvky/hcCJfi9tZQD+IpeiYxjuj7+SxHp82eQ/kA==",
|
"integrity": "sha512-NLAO/SymDxeGuOWWQZCpwoED1K1jaHUvW+u9ip+rTetnxFPLvf3zXthx4QVKfCZLdj2WLQz4cLjHyQdMDXAM+w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
@@ -2857,9 +2857,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
|
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.2.5.tgz",
|
||||||
"integrity": "sha512-iEP/Cq0ts4Ln4Zh2NSC01lkYEAhr+LotbG4U2z+gxHfCdMrtYYtYdG05C2mpeIxShzL7uEIQb/lhVRBMd7robg==",
|
"integrity": "sha512-yG5KFbqrHfGjkAQAaaCD4i7cJklBjmMxZ2C92DEnqCOujSsEuLxrwwoKxQ4+hqEHOmF3lyX0vfqhgZcS03H38w==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"arm64"
|
"arm64"
|
||||||
],
|
],
|
||||||
@@ -2874,9 +2874,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
|
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.2.5.tgz",
|
||||||
"integrity": "sha512-YBbqF0wyHUT00zAGZTTbEbz/C5JDGPnT1Nodor96+tzEU6qAPRYfe5eFe/rpRARbalkpw1UkcVP0Ay8gnksAiA==",
|
"integrity": "sha512-G5lq+2EdxOc8ttg3uhME5t9U3hMGTxwaKz0X4DplTG2Iv4lcNWqw/AESIJVHa5a+EB+ZCC8I+yOfIykp/Cd5mQ==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"ia32"
|
"ia32"
|
||||||
],
|
],
|
||||||
@@ -2891,9 +2891,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
|
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
|
||||||
"version": "2.2.4",
|
"version": "2.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.2.4.tgz",
|
"resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.2.5.tgz",
|
||||||
"integrity": "sha512-MMago/SfWZbUFrwFmPCXmmbb42h7u8Y5jvLvnK2mOpOfCAsei2tLO4hx+Inoai0l2DByuYO4Ef1xDyP6shCsZQ==",
|
"integrity": "sha512-vw4fPVOo0rIQIlqw6xUvK2nwiRFBHNgayDE2Z/SomJlQJAJ1q4VgpHOPl12ouuicmTjK1gWKm7RTouQe3Nig0Q==",
|
||||||
"cpu": [
|
"cpu": [
|
||||||
"x64"
|
"x64"
|
||||||
],
|
],
|
||||||
|
|||||||
+1
-1
@@ -34,7 +34,7 @@
|
|||||||
"tauri-before-dev": "npm run --workspaces --if-present dev"
|
"tauri-before-dev": "npm run --workspaces --if-present dev"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@tauri-apps/cli": "^2.2.4",
|
"@tauri-apps/cli": "^2.2.5",
|
||||||
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
"@typescript-eslint/eslint-plugin": "^8.18.1",
|
||||||
"@typescript-eslint/parser": "^8.18.1",
|
"@typescript-eslint/parser": "^8.18.1",
|
||||||
"eslint": "^8",
|
"eslint": "^8",
|
||||||
|
|||||||
@@ -1,290 +0,0 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
||||||
import type { Environment } from "./models.js";
|
|
||||||
import type { Folder } from "./models.js";
|
|
||||||
import type { GrpcRequest } from "./models.js";
|
|
||||||
import type { HttpRequest } from "./models.js";
|
|
||||||
import type { HttpResponse } from "./models.js";
|
|
||||||
import type { JsonValue } from "./serde_json/JsonValue.js";
|
|
||||||
import type { Workspace } from "./models.js";
|
|
||||||
|
|
||||||
export type BootRequest = { dir: string, watch: boolean, };
|
|
||||||
|
|
||||||
export type BootResponse = { name: string, version: string, };
|
|
||||||
|
|
||||||
export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array<HttpHeader>, };
|
|
||||||
|
|
||||||
export type CallHttpAuthenticationResponse = {
|
|
||||||
/**
|
|
||||||
* HTTP headers to add to the request. Existing headers will be replaced, while
|
|
||||||
* new headers will be added.
|
|
||||||
*/
|
|
||||||
setHeaders: Array<HttpHeader>, };
|
|
||||||
|
|
||||||
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
|
||||||
|
|
||||||
export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, };
|
|
||||||
|
|
||||||
export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, };
|
|
||||||
|
|
||||||
export type CallTemplateFunctionResponse = { value: string | null, };
|
|
||||||
|
|
||||||
export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
|
||||||
|
|
||||||
export type CopyTextRequest = { text: string, };
|
|
||||||
|
|
||||||
export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown";
|
|
||||||
|
|
||||||
export type EmptyPayload = {};
|
|
||||||
|
|
||||||
export type ErrorResponse = { error: string, };
|
|
||||||
|
|
||||||
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type ExportHttpRequestResponse = { content: string, };
|
|
||||||
|
|
||||||
export type FileFilter = { name: string,
|
|
||||||
/**
|
|
||||||
* File extensions to require
|
|
||||||
*/
|
|
||||||
extensions: Array<string>, };
|
|
||||||
|
|
||||||
export type FilterRequest = { content: string, filter: string, };
|
|
||||||
|
|
||||||
export type FilterResponse = { content: string, };
|
|
||||||
|
|
||||||
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
|
||||||
|
|
||||||
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
|
||||||
|
|
||||||
export type FormInput = { "type": "text" } & FormInputText | { "type": "editor" } & FormInputEditor | { "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,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* 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,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type FormInputEditor = {
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
placeholder?: string | null,
|
|
||||||
/**
|
|
||||||
* Don't show the editor gutter (line numbers, folds, etc.)
|
|
||||||
*/
|
|
||||||
hideGutter?: boolean,
|
|
||||||
/**
|
|
||||||
* Language for syntax highlighting
|
|
||||||
*/
|
|
||||||
language?: EditorLanguage, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* 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<FileFilter>, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* 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,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type FormInputSelect = {
|
|
||||||
/**
|
|
||||||
* The options that will be available in the select input
|
|
||||||
*/
|
|
||||||
options: Array<FormInputSelectOption>, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type FormInputSelectOption = { name: string, value: string, };
|
|
||||||
|
|
||||||
export type FormInputText = {
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
placeholder?: string | null,
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
password?: boolean, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type GetHttpAuthenticationResponse = { name: string, label: string, shortLabel: string, config: Array<FormInput>, };
|
|
||||||
|
|
||||||
export type GetHttpRequestActionsRequest = Record<string, never>;
|
|
||||||
|
|
||||||
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
|
||||||
|
|
||||||
export type GetHttpRequestByIdRequest = { id: string, };
|
|
||||||
|
|
||||||
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
|
||||||
|
|
||||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, 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";
|
|
||||||
|
|
||||||
export type ImportRequest = { content: string, };
|
|
||||||
|
|
||||||
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
|
||||||
|
|
||||||
export type ImportResponse = { resources: ImportResources, };
|
|
||||||
|
|
||||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: WindowContext, payload: InternalEventPayload, };
|
|
||||||
|
|
||||||
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 | { "type": "error_response" } & ErrorResponse;
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
required?: boolean, };
|
|
||||||
|
|
||||||
export type PromptTextResponse = { value: string | null, };
|
|
||||||
|
|
||||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
|
||||||
|
|
||||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type RenderPurpose = "send" | "preview";
|
|
||||||
|
|
||||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
|
||||||
|
|
||||||
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<string>, args: Array<FormInput>, };
|
|
||||||
|
|
||||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
|
||||||
|
|
||||||
export type TemplateRenderResponse = { data: JsonValue, };
|
|
||||||
|
|
||||||
export type WindowContext = { "type": "none" } | { "type": "label", label: string, };
|
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { Environment } from "./gen_models.js";
|
||||||
|
import type { Folder } from "./gen_models.js";
|
||||||
|
import type { GrpcRequest } from "./gen_models.js";
|
||||||
|
import type { HttpRequest } from "./gen_models.js";
|
||||||
|
import type { HttpResponse } from "./gen_models.js";
|
||||||
|
import type { JsonValue } from "./serde_json/JsonValue.js";
|
||||||
|
import type { Workspace } from "./gen_models.js";
|
||||||
|
|
||||||
|
export type BootRequest = { dir: string, watch: boolean, };
|
||||||
|
|
||||||
|
export type BootResponse = { name: string, version: string, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationActionArgs = { contextId: string, values: { [key in string]?: JsonPrimitive }, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationActionRequest = { index: number, pluginRefId: string, args: CallHttpAuthenticationActionArgs, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationRequest = { contextId: string, values: { [key in string]?: JsonPrimitive }, method: string, url: string, headers: Array<HttpHeader>, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationResponse = {
|
||||||
|
/**
|
||||||
|
* HTTP headers to add to the request. Existing headers will be replaced, while
|
||||||
|
* new headers will be added.
|
||||||
|
*/
|
||||||
|
setHeaders: Array<HttpHeader>, };
|
||||||
|
|
||||||
|
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
|
export type CallHttpRequestActionRequest = { index: number, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
||||||
|
|
||||||
|
export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, };
|
||||||
|
|
||||||
|
export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, };
|
||||||
|
|
||||||
|
export type CallTemplateFunctionResponse = { value: string | null, };
|
||||||
|
|
||||||
|
export type CloseWindowRequest = { label: string, };
|
||||||
|
|
||||||
|
export type Color = "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
||||||
|
|
||||||
|
export type CompletionOptionType = "constant" | "variable";
|
||||||
|
|
||||||
|
export type Content = { "type": "text", content: string, } | { "type": "markdown", content: string, };
|
||||||
|
|
||||||
|
export type CopyTextRequest = { text: string, };
|
||||||
|
|
||||||
|
export type DeleteKeyValueRequest = { key: string, };
|
||||||
|
|
||||||
|
export type DeleteKeyValueResponse = { deleted: boolean, };
|
||||||
|
|
||||||
|
export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown";
|
||||||
|
|
||||||
|
export type EmptyPayload = {};
|
||||||
|
|
||||||
|
export type ErrorResponse = { error: string, };
|
||||||
|
|
||||||
|
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
|
export type ExportHttpRequestResponse = { content: string, };
|
||||||
|
|
||||||
|
export type FileFilter = { name: string,
|
||||||
|
/**
|
||||||
|
* File extensions to require
|
||||||
|
*/
|
||||||
|
extensions: Array<string>, };
|
||||||
|
|
||||||
|
export type FilterRequest = { content: string, filter: string, };
|
||||||
|
|
||||||
|
export type FilterResponse = { content: string, };
|
||||||
|
|
||||||
|
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||||
|
|
||||||
|
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
||||||
|
|
||||||
|
export type FormInput = { "type": "text" } & FormInputText | { "type": "editor" } & FormInputEditor | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest | { "type": "accordion" } & FormInputAccordion | { "type": "banner" } & FormInputBanner | { "type": "markdown" } & FormInputMarkdown;
|
||||||
|
|
||||||
|
export type FormInputAccordion = { label: string, inputs?: Array<FormInput>, hidden?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputBanner = { inputs?: Array<FormInput>, hidden?: boolean, color?: Color, };
|
||||||
|
|
||||||
|
export type FormInputBase = {
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputCheckbox = {
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputEditor = {
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
placeholder?: string | null,
|
||||||
|
/**
|
||||||
|
* Don't show the editor gutter (line numbers, folds, etc.)
|
||||||
|
*/
|
||||||
|
hideGutter?: boolean,
|
||||||
|
/**
|
||||||
|
* Language for syntax highlighting
|
||||||
|
*/
|
||||||
|
language?: EditorLanguage, readOnly?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputFile = {
|
||||||
|
/**
|
||||||
|
* The title of the file selection window
|
||||||
|
*/
|
||||||
|
title: string,
|
||||||
|
/**
|
||||||
|
* Allow selecting multiple files
|
||||||
|
*/
|
||||||
|
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<FileFilter>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputHttpRequest = {
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputMarkdown = { content: string, hidden?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputSelect = {
|
||||||
|
/**
|
||||||
|
* The options that will be available in the select input
|
||||||
|
*/
|
||||||
|
options: Array<FormInputSelectOption>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputSelectOption = { label: string, value: string, };
|
||||||
|
|
||||||
|
export type FormInputText = {
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
placeholder?: string | null,
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
password?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether to allow newlines in the input, like a <textarea/>
|
||||||
|
*/
|
||||||
|
multiLine?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type GenericCompletionOption = { label: string, detail?: string, info?: string, type?: CompletionOptionType, boost?: number, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationConfigRequest = { contextId: string, values: { [key in string]?: JsonPrimitive }, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationConfigResponse = { args: Array<FormInput>, pluginRefId: string, actions?: Array<HttpAuthenticationAction>, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationSummaryResponse = { name: string, label: string, shortLabel: string, };
|
||||||
|
|
||||||
|
export type GetHttpRequestActionsRequest = Record<string, never>;
|
||||||
|
|
||||||
|
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
||||||
|
|
||||||
|
export type GetHttpRequestByIdRequest = { id: string, };
|
||||||
|
|
||||||
|
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
||||||
|
|
||||||
|
export type GetKeyValueRequest = { key: string, };
|
||||||
|
|
||||||
|
export type GetKeyValueResponse = { value?: string, };
|
||||||
|
|
||||||
|
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||||
|
|
||||||
|
export type HttpAuthenticationAction = { label: string, icon?: Icon, };
|
||||||
|
|
||||||
|
export type HttpHeader = { name: string, value: string, };
|
||||||
|
|
||||||
|
export type HttpRequestAction = { label: string, icon?: Icon, };
|
||||||
|
|
||||||
|
export type Icon = "alert_triangle" | "check" | "check_circle" | "chevron_down" | "copy" | "info" | "pin" | "search" | "trash" | "_unknown";
|
||||||
|
|
||||||
|
export type ImportRequest = { content: string, };
|
||||||
|
|
||||||
|
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
||||||
|
|
||||||
|
export type ImportResponse = { resources: ImportResources, };
|
||||||
|
|
||||||
|
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: WindowContext, payload: InternalEventPayload, };
|
||||||
|
|
||||||
|
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_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "close_window_request" } & CloseWindowRequest | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "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 | { "type": "error_response" } & ErrorResponse;
|
||||||
|
|
||||||
|
export type JsonPrimitive = string | number | boolean | null;
|
||||||
|
|
||||||
|
export type OpenWindowRequest = { url: string,
|
||||||
|
/**
|
||||||
|
* Label for the window. If not provided, a random one will be generated.
|
||||||
|
*/
|
||||||
|
label: string, title?: string, size?: WindowSize, };
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
required?: boolean, };
|
||||||
|
|
||||||
|
export type PromptTextResponse = { value: string | null, };
|
||||||
|
|
||||||
|
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||||
|
|
||||||
|
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
|
export type RenderPurpose = "send" | "preview";
|
||||||
|
|
||||||
|
export type SendHttpRequestRequest = { httpRequest: Partial<HttpRequest>, };
|
||||||
|
|
||||||
|
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||||
|
|
||||||
|
export type SetKeyValueRequest = { key: string, value: string, };
|
||||||
|
|
||||||
|
export type SetKeyValueResponse = {};
|
||||||
|
|
||||||
|
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<string>, args: Array<FormInput>, };
|
||||||
|
|
||||||
|
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||||
|
|
||||||
|
export type TemplateRenderResponse = { data: JsonValue, };
|
||||||
|
|
||||||
|
export type WindowContext = { "type": "none" } | { "type": "label", label: string, };
|
||||||
|
|
||||||
|
export type WindowNavigateEvent = { url: string, };
|
||||||
|
|
||||||
|
export type WindowSize = { width: number, height: number, };
|
||||||
@@ -1 +1,2 @@
|
|||||||
export type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
export type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||||
|
export type MaybePromise<T> = Promise<T> | T;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
export type * from './plugins';
|
export type * from './plugins';
|
||||||
export type * from './themes';
|
export type * from './themes';
|
||||||
|
|
||||||
export * from './bindings/models';
|
export * from './bindings/gen_models';
|
||||||
export * from './bindings/events';
|
export * from './bindings/gen_events';
|
||||||
|
|||||||
@@ -1,13 +1,29 @@
|
|||||||
import {
|
import {
|
||||||
|
CallHttpAuthenticationActionArgs,
|
||||||
CallHttpAuthenticationRequest,
|
CallHttpAuthenticationRequest,
|
||||||
CallHttpAuthenticationResponse,
|
CallHttpAuthenticationResponse,
|
||||||
GetHttpAuthenticationResponse,
|
FormInput,
|
||||||
} from '../bindings/events';
|
GetHttpAuthenticationConfigRequest,
|
||||||
|
GetHttpAuthenticationSummaryResponse,
|
||||||
|
HttpAuthenticationAction,
|
||||||
|
} from '../bindings/gen_events';
|
||||||
|
import { MaybePromise } from '../helpers';
|
||||||
import { Context } from './Context';
|
import { Context } from './Context';
|
||||||
|
|
||||||
export type AuthenticationPlugin = Omit<GetHttpAuthenticationResponse, 'pluginName'> & {
|
type DynamicFormInput = FormInput & {
|
||||||
|
dynamic(
|
||||||
|
ctx: Context,
|
||||||
|
args: GetHttpAuthenticationConfigRequest,
|
||||||
|
): MaybePromise<Partial<FormInput> | undefined | null>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type AuthenticationPlugin = GetHttpAuthenticationSummaryResponse & {
|
||||||
|
args: (FormInput | DynamicFormInput)[];
|
||||||
onApply(
|
onApply(
|
||||||
ctx: Context,
|
ctx: Context,
|
||||||
args: CallHttpAuthenticationRequest,
|
args: CallHttpAuthenticationRequest,
|
||||||
): Promise<CallHttpAuthenticationResponse> | CallHttpAuthenticationResponse;
|
): MaybePromise<CallHttpAuthenticationResponse>;
|
||||||
|
actions?: (HttpAuthenticationAction & {
|
||||||
|
onSelect(ctx: Context, args: CallHttpAuthenticationActionArgs): Promise<void> | void;
|
||||||
|
})[];
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ import type {
|
|||||||
FindHttpResponsesResponse,
|
FindHttpResponsesResponse,
|
||||||
GetHttpRequestByIdRequest,
|
GetHttpRequestByIdRequest,
|
||||||
GetHttpRequestByIdResponse,
|
GetHttpRequestByIdResponse,
|
||||||
|
OpenWindowRequest,
|
||||||
PromptTextRequest,
|
PromptTextRequest,
|
||||||
PromptTextResponse,
|
PromptTextResponse,
|
||||||
RenderHttpRequestRequest,
|
RenderHttpRequestRequest,
|
||||||
@@ -12,7 +13,7 @@ import type {
|
|||||||
ShowToastRequest,
|
ShowToastRequest,
|
||||||
TemplateRenderRequest,
|
TemplateRenderRequest,
|
||||||
TemplateRenderResponse,
|
TemplateRenderResponse,
|
||||||
} from "../bindings/events.ts";
|
} from '../bindings/gen_events.ts';
|
||||||
|
|
||||||
export interface Context {
|
export interface Context {
|
||||||
clipboard: {
|
clipboard: {
|
||||||
@@ -22,27 +23,27 @@ export interface Context {
|
|||||||
show(args: ShowToastRequest): Promise<void>;
|
show(args: ShowToastRequest): Promise<void>;
|
||||||
};
|
};
|
||||||
prompt: {
|
prompt: {
|
||||||
text(args: PromptTextRequest): Promise<PromptTextResponse["value"]>;
|
text(args: PromptTextRequest): Promise<PromptTextResponse['value']>;
|
||||||
|
};
|
||||||
|
store: {
|
||||||
|
set<T>(key: string, value: T): Promise<void>;
|
||||||
|
get<T>(key: string): Promise<T | undefined>;
|
||||||
|
delete(key: string): Promise<boolean>;
|
||||||
|
};
|
||||||
|
window: {
|
||||||
|
openUrl(
|
||||||
|
args: OpenWindowRequest & { onNavigate?: (args: { url: string }) => void },
|
||||||
|
): Promise<{ close: () => void }>;
|
||||||
};
|
};
|
||||||
httpRequest: {
|
httpRequest: {
|
||||||
send(
|
send(args: SendHttpRequestRequest): Promise<SendHttpRequestResponse['httpResponse']>;
|
||||||
args: SendHttpRequestRequest,
|
getById(args: GetHttpRequestByIdRequest): Promise<GetHttpRequestByIdResponse['httpRequest']>;
|
||||||
): Promise<SendHttpRequestResponse["httpResponse"]>;
|
render(args: RenderHttpRequestRequest): Promise<RenderHttpRequestResponse['httpRequest']>;
|
||||||
getById(
|
|
||||||
args: GetHttpRequestByIdRequest,
|
|
||||||
): Promise<GetHttpRequestByIdResponse["httpRequest"]>;
|
|
||||||
render(
|
|
||||||
args: RenderHttpRequestRequest,
|
|
||||||
): Promise<RenderHttpRequestResponse["httpRequest"]>;
|
|
||||||
};
|
};
|
||||||
httpResponse: {
|
httpResponse: {
|
||||||
find(
|
find(args: FindHttpResponsesRequest): Promise<FindHttpResponsesResponse['httpResponses']>;
|
||||||
args: FindHttpResponsesRequest,
|
|
||||||
): Promise<FindHttpResponsesResponse["httpResponses"]>;
|
|
||||||
};
|
};
|
||||||
templates: {
|
templates: {
|
||||||
render(
|
render(args: TemplateRenderRequest): Promise<TemplateRenderResponse['data']>;
|
||||||
args: TemplateRenderRequest,
|
|
||||||
): Promise<TemplateRenderResponse["data"]>;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { CallHttpRequestActionArgs, HttpRequestAction } from '../bindings/events';
|
import type { CallHttpRequestActionArgs, HttpRequestAction } from '../bindings/gen_events';
|
||||||
import type { Context } from './Context';
|
import type { Context } from './Context';
|
||||||
|
|
||||||
export type HttpRequestActionPlugin = HttpRequestAction & {
|
export type HttpRequestActionPlugin = HttpRequestAction & {
|
||||||
|
|||||||
@@ -1,29 +1,14 @@
|
|||||||
import {
|
import { Environment, Folder, GrpcRequest, HttpRequest, Workspace } from '../bindings/gen_models';
|
||||||
Environment,
|
import type { AtLeast } from '../helpers';
|
||||||
Folder,
|
import type { Context } from './Context';
|
||||||
GrpcRequest,
|
|
||||||
HttpRequest,
|
|
||||||
Workspace,
|
|
||||||
} from "../bindings/models";
|
|
||||||
import type { AtLeast } from "../helpers";
|
|
||||||
import type { Context } from "./Context";
|
|
||||||
|
|
||||||
type ImportPluginResponse = null | {
|
type ImportPluginResponse = null | {
|
||||||
resources: {
|
resources: {
|
||||||
workspaces: AtLeast<Workspace, "name" | "id" | "model">[];
|
workspaces: AtLeast<Workspace, 'name' | 'id' | 'model'>[];
|
||||||
environments: AtLeast<
|
environments: AtLeast<Environment, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||||
Environment,
|
folders: AtLeast<Folder, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||||
"name" | "id" | "model" | "workspaceId"
|
httpRequests: AtLeast<HttpRequest, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||||
>[];
|
grpcRequests: AtLeast<GrpcRequest, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||||
folders: AtLeast<Folder, "name" | "id" | "model" | "workspaceId">[];
|
|
||||||
httpRequests: AtLeast<
|
|
||||||
HttpRequest,
|
|
||||||
"name" | "id" | "model" | "workspaceId"
|
|
||||||
>[];
|
|
||||||
grpcRequests: AtLeast<
|
|
||||||
GrpcRequest,
|
|
||||||
"name" | "id" | "model" | "workspaceId"
|
|
||||||
>[];
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import {
|
import {
|
||||||
CallTemplateFunctionArgs,
|
CallTemplateFunctionArgs,
|
||||||
TemplateFunction,
|
TemplateFunction,
|
||||||
} from "../bindings/events";
|
} from "../bindings/gen_events";
|
||||||
import { Context } from "./Context";
|
import { Context } from "./Context";
|
||||||
|
|
||||||
export type TemplateFunctionPlugin = TemplateFunction & {
|
export type TemplateFunctionPlugin = TemplateFunction & {
|
||||||
|
|||||||
@@ -1,11 +1,18 @@
|
|||||||
|
// OAuth 2.0 spec -> https://datatracker.ietf.org/doc/html/rfc6749
|
||||||
|
|
||||||
import type {
|
import type {
|
||||||
BootRequest,
|
BootRequest,
|
||||||
Context,
|
Context,
|
||||||
|
DeleteKeyValueResponse,
|
||||||
FindHttpResponsesResponse,
|
FindHttpResponsesResponse,
|
||||||
|
FormInput,
|
||||||
GetHttpRequestByIdResponse,
|
GetHttpRequestByIdResponse,
|
||||||
|
GetKeyValueResponse,
|
||||||
|
HttpAuthenticationAction,
|
||||||
HttpRequestAction,
|
HttpRequestAction,
|
||||||
InternalEvent,
|
InternalEvent,
|
||||||
InternalEventPayload,
|
InternalEventPayload,
|
||||||
|
JsonPrimitive,
|
||||||
PluginDefinition,
|
PluginDefinition,
|
||||||
PromptTextResponse,
|
PromptTextResponse,
|
||||||
RenderHttpRequestResponse,
|
RenderHttpRequestResponse,
|
||||||
@@ -19,8 +26,16 @@ import type { Stats } from 'node:fs';
|
|||||||
import { readFileSync, statSync, watch } from 'node:fs';
|
import { readFileSync, statSync, watch } from 'node:fs';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import * as util from 'node:util';
|
import * as util from 'node:util';
|
||||||
|
import { parentPort as nullableParentPort, workerData } from 'node:worker_threads';
|
||||||
|
import Promise from '../../../../../Library/Caches/deno/npm/registry.npmjs.org/any-promise/1.3.0';
|
||||||
import { interceptStdout } from './interceptStdout';
|
import { interceptStdout } from './interceptStdout';
|
||||||
import { parentPort, workerData } from 'node:worker_threads';
|
import { migrateHttpRequestActionKey, migrateTemplateFunctionSelectOptions } from './migrations';
|
||||||
|
|
||||||
|
if (nullableParentPort == null) {
|
||||||
|
throw new Error('Worker does not have access to parentPort');
|
||||||
|
}
|
||||||
|
|
||||||
|
const parentPort = nullableParentPort;
|
||||||
|
|
||||||
export interface PluginWorkerData {
|
export interface PluginWorkerData {
|
||||||
bootRequest: BootRequest;
|
bootRequest: BootRequest;
|
||||||
@@ -73,7 +88,7 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
if (event.payload.type !== 'empty_response') {
|
if (event.payload.type !== 'empty_response') {
|
||||||
console.log('Sending event to app', event.id, event.payload.type);
|
console.log('Sending event to app', event.id, event.payload.type);
|
||||||
}
|
}
|
||||||
parentPort!.postMessage(event);
|
parentPort.postMessage(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
function sendAndWaitForReply<T extends Omit<InternalEventPayload, 'type'>>(
|
function sendAndWaitForReply<T extends Omit<InternalEventPayload, 'type'>>(
|
||||||
@@ -84,14 +99,15 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
const eventToSend = buildEventToSend(windowContext, payload, null);
|
const eventToSend = buildEventToSend(windowContext, payload, null);
|
||||||
|
|
||||||
// 2. Spawn listener in background
|
// 2. Spawn listener in background
|
||||||
const promise = new Promise<InternalEventPayload>((resolve) => {
|
const promise = new Promise<T>((resolve) => {
|
||||||
const cb = (event: InternalEvent) => {
|
const cb = (event: InternalEvent) => {
|
||||||
if (event.replyId === eventToSend.id) {
|
if (event.replyId === eventToSend.id) {
|
||||||
parentPort!.off('message', cb); // Unlisten, now that we're done
|
parentPort.off('message', cb); // Unlisten, now that we're done
|
||||||
resolve(event.payload); // Not type-safe but oh well
|
const { type: _, ...payload } = event.payload;
|
||||||
|
resolve(payload as T);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
parentPort!.on('message', cb);
|
parentPort.on('message', cb);
|
||||||
});
|
});
|
||||||
|
|
||||||
// 3. Send the event after we start listening (to prevent race)
|
// 3. Send the event after we start listening (to prevent race)
|
||||||
@@ -101,10 +117,29 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
return promise as unknown as Promise<T>;
|
return promise as unknown as Promise<T>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function sendAndListenForEvents(
|
||||||
|
windowContext: WindowContext,
|
||||||
|
payload: InternalEventPayload,
|
||||||
|
onEvent: (event: InternalEventPayload) => void,
|
||||||
|
): void {
|
||||||
|
// 1. Build event to send
|
||||||
|
const eventToSend = buildEventToSend(windowContext, payload, null);
|
||||||
|
|
||||||
|
// 2. Listen for replies in the background
|
||||||
|
parentPort.on('message', (event: InternalEvent) => {
|
||||||
|
if (event.replyId === eventToSend.id) {
|
||||||
|
onEvent(event.payload);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// 3. Send the event after we start listening (to prevent race)
|
||||||
|
sendEvent(eventToSend);
|
||||||
|
}
|
||||||
|
|
||||||
// Reload plugin if the JS or package.json changes
|
// Reload plugin if the JS or package.json changes
|
||||||
const windowContextNone: WindowContext = { type: 'none' };
|
const windowContextNone: WindowContext = { type: 'none' };
|
||||||
const fileChangeCallback = async () => {
|
const fileChangeCallback = async () => {
|
||||||
await importModule();
|
importModule();
|
||||||
return sendPayload(windowContextNone, { type: 'reload_response' }, null);
|
return sendPayload(windowContextNone, { type: 'reload_response' }, null);
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -130,6 +165,27 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
window: {
|
||||||
|
async openUrl({ onNavigate, ...args }) {
|
||||||
|
args.label = args.label || `${Math.random()}`;
|
||||||
|
const payload: InternalEventPayload = { type: 'open_window_request', ...args };
|
||||||
|
const onEvent = (event: InternalEventPayload) => {
|
||||||
|
if (event.type === 'window_navigate_event') {
|
||||||
|
onNavigate?.(event);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
sendAndListenForEvents(event.windowContext, payload, onEvent);
|
||||||
|
return {
|
||||||
|
close: () => {
|
||||||
|
const closePayload: InternalEventPayload = {
|
||||||
|
type: 'close_window_request',
|
||||||
|
label: args.label,
|
||||||
|
};
|
||||||
|
sendPayload(event.windowContext, closePayload, null);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
},
|
||||||
|
},
|
||||||
prompt: {
|
prompt: {
|
||||||
async text(args) {
|
async text(args) {
|
||||||
const reply: PromptTextResponse = await sendAndWaitForReply(event.windowContext, {
|
const reply: PromptTextResponse = await sendAndWaitForReply(event.windowContext, {
|
||||||
@@ -201,6 +257,30 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
return result.data;
|
return result.data;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
store: {
|
||||||
|
async get<T>(key: string) {
|
||||||
|
const payload = { type: 'get_key_value_request', key } as const;
|
||||||
|
const result = await sendAndWaitForReply<GetKeyValueResponse>(event.windowContext, payload);
|
||||||
|
return result.value ? (JSON.parse(result.value) as T) : undefined;
|
||||||
|
},
|
||||||
|
async set<T>(key: string, value: T) {
|
||||||
|
const valueStr = JSON.stringify(value);
|
||||||
|
const payload: InternalEventPayload = {
|
||||||
|
type: 'set_key_value_request',
|
||||||
|
key,
|
||||||
|
value: valueStr,
|
||||||
|
};
|
||||||
|
await sendAndWaitForReply<GetKeyValueResponse>(event.windowContext, payload);
|
||||||
|
},
|
||||||
|
async delete(key: string) {
|
||||||
|
const payload = { type: 'delete_key_value_request', key } as const;
|
||||||
|
const result = await sendAndWaitForReply<DeleteKeyValueResponse>(
|
||||||
|
event.windowContext,
|
||||||
|
payload,
|
||||||
|
);
|
||||||
|
return result.deleted;
|
||||||
|
},
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
let plug: PluginDefinition | null = null;
|
let plug: PluginDefinition | null = null;
|
||||||
@@ -210,10 +290,11 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
delete require.cache[id];
|
delete require.cache[id];
|
||||||
plug = require(id).plugin;
|
plug = require(id).plugin;
|
||||||
}
|
}
|
||||||
|
|
||||||
importModule();
|
importModule();
|
||||||
|
|
||||||
// Message comes into the plugin to be processed
|
// Message comes into the plugin to be processed
|
||||||
parentPort!.on('message', async (event: InternalEvent) => {
|
parentPort.on('message', async (event: InternalEvent) => {
|
||||||
const ctx = newCtx(event);
|
const ctx = newCtx(event);
|
||||||
const { windowContext, payload, id: replyId } = event;
|
const { windowContext, payload, id: replyId } = event;
|
||||||
try {
|
try {
|
||||||
@@ -272,7 +353,7 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
Array.isArray(plug?.httpRequestActions)
|
Array.isArray(plug?.httpRequestActions)
|
||||||
) {
|
) {
|
||||||
const reply: HttpRequestAction[] = plug.httpRequestActions.map((a) => ({
|
const reply: HttpRequestAction[] = plug.httpRequestActions.map((a) => ({
|
||||||
...a,
|
...migrateHttpRequestActionKey(a),
|
||||||
// Add everything except onSelect
|
// Add everything except onSelect
|
||||||
onSelect: undefined,
|
onSelect: undefined,
|
||||||
}));
|
}));
|
||||||
@@ -289,11 +370,13 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
payload.type === 'get_template_functions_request' &&
|
payload.type === 'get_template_functions_request' &&
|
||||||
Array.isArray(plug?.templateFunctions)
|
Array.isArray(plug?.templateFunctions)
|
||||||
) {
|
) {
|
||||||
const reply: TemplateFunction[] = plug.templateFunctions.map((a) => ({
|
const reply: TemplateFunction[] = plug.templateFunctions.map((templateFunction) => {
|
||||||
...a,
|
return {
|
||||||
// Add everything except render
|
...migrateTemplateFunctionSelectOptions(templateFunction),
|
||||||
onRender: undefined,
|
// Add everything except render
|
||||||
}));
|
onRender: undefined,
|
||||||
|
};
|
||||||
|
});
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
type: 'get_template_functions_response',
|
type: 'get_template_functions_response',
|
||||||
pluginRefId,
|
pluginRefId,
|
||||||
@@ -303,11 +386,42 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'get_http_authentication_request' && plug?.authentication) {
|
if (payload.type === 'get_http_authentication_summary_request' && plug?.authentication) {
|
||||||
const { onApply: _, ...auth } = plug.authentication;
|
const { name, shortLabel, label } = plug.authentication;
|
||||||
const replyPayload: InternalEventPayload = {
|
const replyPayload: InternalEventPayload = {
|
||||||
...auth,
|
type: 'get_http_authentication_summary_response',
|
||||||
type: 'get_http_authentication_response',
|
name,
|
||||||
|
label,
|
||||||
|
shortLabel,
|
||||||
|
};
|
||||||
|
|
||||||
|
sendPayload(windowContext, replyPayload, replyId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.type === 'get_http_authentication_config_request' && plug?.authentication) {
|
||||||
|
const { args, actions } = plug.authentication;
|
||||||
|
const resolvedArgs: FormInput[] = [];
|
||||||
|
for (let i = 0; i < args.length; i++) {
|
||||||
|
let v = args[i];
|
||||||
|
if ('dynamic' in v) {
|
||||||
|
const dynamicAttrs = await v.dynamic(ctx, payload);
|
||||||
|
const { dynamic, ...other } = v;
|
||||||
|
resolvedArgs.push({ ...other, ...dynamicAttrs } as FormInput);
|
||||||
|
} else {
|
||||||
|
resolvedArgs.push(v);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const resolvedActions: HttpAuthenticationAction[] = [];
|
||||||
|
for (const { onSelect, ...action } of actions ?? []) {
|
||||||
|
resolvedActions.push(action);
|
||||||
|
}
|
||||||
|
|
||||||
|
const replyPayload: InternalEventPayload = {
|
||||||
|
type: 'get_http_authentication_config_response',
|
||||||
|
args: resolvedArgs,
|
||||||
|
actions: resolvedActions,
|
||||||
|
pluginRefId,
|
||||||
};
|
};
|
||||||
|
|
||||||
sendPayload(windowContext, replyPayload, replyId);
|
sendPayload(windowContext, replyPayload, replyId);
|
||||||
@@ -317,12 +431,13 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
if (payload.type === 'call_http_authentication_request' && plug?.authentication) {
|
if (payload.type === 'call_http_authentication_request' && plug?.authentication) {
|
||||||
const auth = plug.authentication;
|
const auth = plug.authentication;
|
||||||
if (typeof auth?.onApply === 'function') {
|
if (typeof auth?.onApply === 'function') {
|
||||||
|
applyFormInputDefaults(auth.args, payload.values);
|
||||||
const result = await auth.onApply(ctx, payload);
|
const result = await auth.onApply(ctx, payload);
|
||||||
sendPayload(
|
sendPayload(
|
||||||
windowContext,
|
windowContext,
|
||||||
{
|
{
|
||||||
...result,
|
|
||||||
type: 'call_http_authentication_response',
|
type: 'call_http_authentication_response',
|
||||||
|
setHeaders: result.setHeaders,
|
||||||
},
|
},
|
||||||
replyId,
|
replyId,
|
||||||
);
|
);
|
||||||
@@ -330,11 +445,25 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
payload.type === 'call_http_authentication_action_request' &&
|
||||||
|
plug?.authentication != null
|
||||||
|
) {
|
||||||
|
const action = plug.authentication.actions?.find((a) => a.name === payload.name);
|
||||||
|
if (typeof action?.onSelect === 'function') {
|
||||||
|
await action.onSelect(ctx, payload.args);
|
||||||
|
sendEmpty(windowContext, replyId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (
|
if (
|
||||||
payload.type === 'call_http_request_action_request' &&
|
payload.type === 'call_http_request_action_request' &&
|
||||||
Array.isArray(plug?.httpRequestActions)
|
Array.isArray(plug?.httpRequestActions)
|
||||||
) {
|
) {
|
||||||
const action = plug.httpRequestActions.find((a) => a.key === payload.key);
|
const action = plug.httpRequestActions.find(
|
||||||
|
(a) => migrateHttpRequestActionKey(a).name === payload.name,
|
||||||
|
);
|
||||||
if (typeof action?.onSelect === 'function') {
|
if (typeof action?.onSelect === 'function') {
|
||||||
await action.onSelect(ctx, payload.args);
|
await action.onSelect(ctx, payload.args);
|
||||||
sendEmpty(windowContext, replyId);
|
sendEmpty(windowContext, replyId);
|
||||||
@@ -348,6 +477,7 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
) {
|
) {
|
||||||
const action = plug.templateFunctions.find((a) => a.name === payload.name);
|
const action = plug.templateFunctions.find((a) => a.name === payload.name);
|
||||||
if (typeof action?.onRender === 'function') {
|
if (typeof action?.onRender === 'function') {
|
||||||
|
applyFormInputDefaults(action.args, payload.args.values);
|
||||||
const result = await action.onRender(ctx, payload.args);
|
const result = await action.onRender(ctx, payload.args);
|
||||||
sendPayload(
|
sendPayload(
|
||||||
windowContext,
|
windowContext,
|
||||||
@@ -362,7 +492,7 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (payload.type === 'reload_request') {
|
if (payload.type === 'reload_request') {
|
||||||
await importModule();
|
importModule();
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log('Plugin call threw exception', payload.type, err);
|
console.log('Plugin call threw exception', payload.type, err);
|
||||||
@@ -374,7 +504,7 @@ function initialize(workerData: PluginWorkerData) {
|
|||||||
},
|
},
|
||||||
replyId,
|
replyId,
|
||||||
);
|
);
|
||||||
// TODO: Return errors to server
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No matches, so send back an empty response so the caller doesn't block forever
|
// No matches, so send back an empty response so the caller doesn't block forever
|
||||||
@@ -425,3 +555,17 @@ function watchFile(filepath: string, cb: (filepath: string) => void) {
|
|||||||
watchedFiles[filepath] = stat;
|
watchedFiles[filepath] = stat;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Recursively apply form input defaults to a set of values */
|
||||||
|
function applyFormInputDefaults(
|
||||||
|
inputs: FormInput[],
|
||||||
|
values: { [p: string]: JsonPrimitive | undefined },
|
||||||
|
) {
|
||||||
|
for (const input of inputs) {
|
||||||
|
if ('inputs' in input) {
|
||||||
|
applyFormInputDefaults(input.inputs ?? [], values);
|
||||||
|
} else if ('defaultValue' in input && values[input.name] === undefined) {
|
||||||
|
values[input.name] = input.defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -0,0 +1,25 @@
|
|||||||
|
import {HttpRequestAction, TemplateFunction} from '@yaakapp/api';
|
||||||
|
|
||||||
|
export function migrateTemplateFunctionSelectOptions(f: TemplateFunction): TemplateFunction {
|
||||||
|
const migratedArgs = f.args.map((a) => {
|
||||||
|
if (a.type === 'select') {
|
||||||
|
a.options = a.options.map((o) => ({
|
||||||
|
...o,
|
||||||
|
label: o.label || (o as any).name,
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
return a;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
...f,
|
||||||
|
args: migratedArgs,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function migrateHttpRequestActionKey(a: HttpRequestAction): HttpRequestAction {
|
||||||
|
return {
|
||||||
|
...a,
|
||||||
|
name: a.name || (a as any).key,
|
||||||
|
}
|
||||||
|
}
|
||||||
Generated
+30
-31
@@ -712,7 +712,7 @@ dependencies = [
|
|||||||
"semver",
|
"semver",
|
||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5212,8 +5212,6 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sha2",
|
"sha2",
|
||||||
"sqlx-core",
|
"sqlx-core",
|
||||||
"sqlx-mysql",
|
|
||||||
"sqlx-postgres",
|
|
||||||
"sqlx-sqlite",
|
"sqlx-sqlite",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
@@ -5252,7 +5250,6 @@ dependencies = [
|
|||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"rsa",
|
"rsa",
|
||||||
"serde",
|
|
||||||
"sha1",
|
"sha1",
|
||||||
"sha2",
|
"sha2",
|
||||||
"smallvec",
|
"smallvec",
|
||||||
@@ -5556,9 +5553,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tauri"
|
name = "tauri"
|
||||||
version = "2.2.0"
|
version = "2.2.3"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2e2e3349fbb2be7af9fad1b43d61ac83ba55ab48d47fbe1b2732f0c8211610a9"
|
checksum = "78f6efc261c7905839b4914889a5b25df07f0ff89c63fb4afd6ff8c96af15e4d"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"bytes",
|
"bytes",
|
||||||
@@ -5594,7 +5591,7 @@ dependencies = [
|
|||||||
"tauri-runtime",
|
"tauri-runtime",
|
||||||
"tauri-runtime-wry",
|
"tauri-runtime-wry",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tray-icon",
|
"tray-icon",
|
||||||
"url",
|
"url",
|
||||||
@@ -5647,7 +5644,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"syn 2.0.87",
|
"syn 2.0.87",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"time",
|
"time",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -5697,7 +5694,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5714,7 +5711,7 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"tauri-plugin-fs",
|
"tauri-plugin-fs",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"url",
|
"url",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5735,7 +5732,7 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"toml 0.8.19",
|
"toml 0.8.19",
|
||||||
"url",
|
"url",
|
||||||
"uuid",
|
"uuid",
|
||||||
@@ -5759,7 +5756,7 @@ dependencies = [
|
|||||||
"swift-rs",
|
"swift-rs",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"time",
|
"time",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5779,7 +5776,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"url",
|
"url",
|
||||||
"windows",
|
"windows",
|
||||||
"zbus 5.3.0",
|
"zbus 5.3.0",
|
||||||
@@ -5800,7 +5797,7 @@ dependencies = [
|
|||||||
"sys-locale",
|
"sys-locale",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5820,7 +5817,7 @@ dependencies = [
|
|||||||
"shared_child",
|
"shared_child",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"tokio",
|
"tokio",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -5833,7 +5830,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"tracing",
|
"tracing",
|
||||||
"windows-sys 0.59.0",
|
"windows-sys 0.59.0",
|
||||||
"zbus 5.3.0",
|
"zbus 5.3.0",
|
||||||
@@ -5861,7 +5858,7 @@ dependencies = [
|
|||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"tempfile",
|
"tempfile",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"time",
|
"time",
|
||||||
"tokio",
|
"tokio",
|
||||||
"url",
|
"url",
|
||||||
@@ -5881,7 +5878,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -5898,7 +5895,7 @@ dependencies = [
|
|||||||
"serde",
|
"serde",
|
||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri-utils",
|
"tauri-utils",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"url",
|
"url",
|
||||||
"windows",
|
"windows",
|
||||||
]
|
]
|
||||||
@@ -5958,7 +5955,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"serde_with",
|
"serde_with",
|
||||||
"swift-rs",
|
"swift-rs",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"toml 0.8.19",
|
"toml 0.8.19",
|
||||||
"url",
|
"url",
|
||||||
"urlpattern",
|
"urlpattern",
|
||||||
@@ -6026,11 +6023,11 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror"
|
name = "thiserror"
|
||||||
version = "2.0.7"
|
version = "2.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "93605438cbd668185516ab499d589afb7ee1859ea3d5fc8f6b0755e1c7443767"
|
checksum = "d452f284b73e6d76dd36758a0c8684b1d5be31f92b89d07fd5822175732206fc"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"thiserror-impl 2.0.7",
|
"thiserror-impl 2.0.11",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
@@ -6046,9 +6043,9 @@ dependencies = [
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "thiserror-impl"
|
name = "thiserror-impl"
|
||||||
version = "2.0.7"
|
version = "2.0.11"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "e1d8749b4531af2117677a5fcd12b1348a3fe2b81e36e61ffeac5c4aa3273e36"
|
checksum = "26afc1baea8a989337eeb52b6e72a039780ce45c3edfcc9c5b9d112feeb173c2"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
@@ -6458,7 +6455,7 @@ dependencies = [
|
|||||||
"log",
|
"log",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"sha1",
|
"sha1",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"utf-8",
|
"utf-8",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -7406,7 +7403,7 @@ dependencies = [
|
|||||||
"sha2",
|
"sha2",
|
||||||
"soup3",
|
"soup3",
|
||||||
"tao-macros",
|
"tao-macros",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"url",
|
"url",
|
||||||
"webkit2gtk",
|
"webkit2gtk",
|
||||||
"webkit2gtk-sys",
|
"webkit2gtk-sys",
|
||||||
@@ -7497,6 +7494,7 @@ dependencies = [
|
|||||||
"hex_color",
|
"hex_color",
|
||||||
"http",
|
"http",
|
||||||
"log",
|
"log",
|
||||||
|
"md5",
|
||||||
"mime_guess",
|
"mime_guess",
|
||||||
"objc",
|
"objc",
|
||||||
"openssl-sys",
|
"openssl-sys",
|
||||||
@@ -7571,7 +7569,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"yaak-models",
|
"yaak-models",
|
||||||
]
|
]
|
||||||
@@ -7592,7 +7590,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"sqlx",
|
"sqlx",
|
||||||
"tauri",
|
"tauri",
|
||||||
"thiserror 1.0.63",
|
"thiserror 2.0.11",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -7603,6 +7601,7 @@ dependencies = [
|
|||||||
"dunce",
|
"dunce",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
"log",
|
"log",
|
||||||
|
"md5",
|
||||||
"path-slash",
|
"path-slash",
|
||||||
"rand 0.8.5",
|
"rand 0.8.5",
|
||||||
"regex",
|
"regex",
|
||||||
@@ -7610,7 +7609,7 @@ dependencies = [
|
|||||||
"serde_json",
|
"serde_json",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin-shell",
|
"tauri-plugin-shell",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"tokio",
|
"tokio",
|
||||||
"tokio-tungstenite",
|
"tokio-tungstenite",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
@@ -7639,7 +7638,7 @@ dependencies = [
|
|||||||
"sha1",
|
"sha1",
|
||||||
"tauri",
|
"tauri",
|
||||||
"tauri-plugin",
|
"tauri-plugin",
|
||||||
"thiserror 2.0.7",
|
"thiserror 2.0.11",
|
||||||
"tokio",
|
"tokio",
|
||||||
"ts-rs",
|
"ts-rs",
|
||||||
"yaak-models",
|
"yaak-models",
|
||||||
|
|||||||
@@ -44,6 +44,7 @@ eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client
|
|||||||
hex_color = "3.0.0"
|
hex_color = "3.0.0"
|
||||||
http = { version = "1.2.0", default-features = false }
|
http = { version = "1.2.0", default-features = false }
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
|
md5 = "0.7.0"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
regex = "1.10.2"
|
regex = "1.10.2"
|
||||||
reqwest = { workspace = true, features = ["multipart", "cookies", "gzip", "brotli", "deflate", "json", "rustls-tls-manual-roots-no-provider"] }
|
reqwest = { workspace = true, features = ["multipart", "cookies", "gzip", "brotli", "deflate", "json", "rustls-tls-manual-roots-no-provider"] }
|
||||||
@@ -85,7 +86,7 @@ yaak-plugins = { path = "yaak-plugins" }
|
|||||||
serde = "1.0.215"
|
serde = "1.0.215"
|
||||||
serde_json = "1.0.132"
|
serde_json = "1.0.132"
|
||||||
tauri-plugin-shell = "2.2.0"
|
tauri-plugin-shell = "2.2.0"
|
||||||
tauri = "2.2.0"
|
tauri = "2.2.3"
|
||||||
thiserror = "2.0.3"
|
thiserror = "2.0.3"
|
||||||
ts-rs = "10.0.0"
|
ts-rs = "10.0.0"
|
||||||
reqwest = "0.12.12"
|
reqwest = "0.12.12"
|
||||||
|
|||||||
@@ -30,10 +30,8 @@
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"opener:allow-open-url",
|
"clipboard-manager:allow-read-text",
|
||||||
"opener:allow-open-path",
|
"clipboard-manager:allow-write-text",
|
||||||
"opener:allow-default-urls",
|
|
||||||
"opener:allow-reveal-item-in-dir",
|
|
||||||
"core:webview:allow-set-webview-zoom",
|
"core:webview:allow-set-webview-zoom",
|
||||||
"core:window:allow-close",
|
"core:window:allow-close",
|
||||||
"core:window:allow-internal-toggle-maximize",
|
"core:window:allow-internal-toggle-maximize",
|
||||||
@@ -47,8 +45,11 @@
|
|||||||
"core:window:allow-start-dragging",
|
"core:window:allow-start-dragging",
|
||||||
"core:window:allow-theme",
|
"core:window:allow-theme",
|
||||||
"core:window:allow-unmaximize",
|
"core:window:allow-unmaximize",
|
||||||
"clipboard-manager:allow-read-text",
|
"opener:allow-default-urls",
|
||||||
"clipboard-manager:allow-write-text",
|
"opener:allow-open-path",
|
||||||
|
"opener:allow-open-url",
|
||||||
|
"opener:allow-reveal-item-in-dir",
|
||||||
|
"shell:allow-open",
|
||||||
"yaak-license:default",
|
"yaak-license:default",
|
||||||
"yaak-sync:default"
|
"yaak-sync:default"
|
||||||
]
|
]
|
||||||
|
|||||||
Generated
+1
-1
@@ -1 +1 @@
|
|||||||
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-dir","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"opener:allow-open-url","opener:allow-open-path","opener:allow-default-urls","opener:allow-reveal-item-in-dir","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-is-maximized","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-unmaximize","clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","yaak-license:default","yaak-sync:default"]}}
|
{"main":{"identifier":"main","description":"Main permissions","local":true,"windows":["*"],"permissions":["core:event:allow-emit","core:event:allow-listen","core:event:allow-unlisten","os:allow-os-type","clipboard-manager:allow-clear","clipboard-manager:allow-write-text","clipboard-manager:allow-read-text","dialog:allow-open","dialog:allow-save","fs:allow-read-dir","fs:allow-read-file","fs:allow-read-text-file",{"identifier":"fs:scope","allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]},"clipboard-manager:allow-read-text","clipboard-manager:allow-write-text","core:webview:allow-set-webview-zoom","core:window:allow-close","core:window:allow-internal-toggle-maximize","core:window:allow-is-fullscreen","core:window:allow-is-maximized","core:window:allow-maximize","core:window:allow-minimize","core:window:allow-set-decorations","core:window:allow-set-title","core:window:allow-show","core:window:allow-start-dragging","core:window:allow-theme","core:window:allow-unmaximize","opener:allow-default-urls","opener:allow-open-path","opener:allow-open-url","opener:allow-reveal-item-in-dir","shell:allow-open","yaak-license:default","yaak-sync:default"]}}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
CREATE TABLE plugin_key_values
|
||||||
|
(
|
||||||
|
model TEXT DEFAULT 'plugin_key_value' NOT NULL,
|
||||||
|
created_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
updated_at DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
deleted_at DATETIME,
|
||||||
|
plugin_name TEXT NOT NULL,
|
||||||
|
key TEXT NOT NULL,
|
||||||
|
value TEXT NOT NULL,
|
||||||
|
PRIMARY KEY (plugin_name, key)
|
||||||
|
);
|
||||||
@@ -70,7 +70,7 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
if !url_string.starts_with("http://") && !url_string.starts_with("https://") {
|
if !url_string.starts_with("http://") && !url_string.starts_with("https://") {
|
||||||
url_string = format!("http://{}", url_string);
|
url_string = format!("http://{}", url_string);
|
||||||
}
|
}
|
||||||
debug!("Sending request to {url_string}");
|
debug!("Sending request to {} {url_string}", request.method);
|
||||||
|
|
||||||
let mut client_builder = reqwest::Client::builder()
|
let mut client_builder = reqwest::Client::builder()
|
||||||
.redirect(match workspace.setting_follow_redirects {
|
.redirect(match workspace.setting_follow_redirects {
|
||||||
@@ -262,7 +262,7 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
None => {}
|
None => {}
|
||||||
Some(a) => {
|
Some(a) => {
|
||||||
for p in a {
|
for p in a {
|
||||||
let enabled = get_bool(p, "enabled");
|
let enabled = get_bool(p, "enabled", true);
|
||||||
let name = get_str(p, "name");
|
let name = get_str(p, "name");
|
||||||
if !enabled || name.is_empty() {
|
if !enabled || name.is_empty() {
|
||||||
continue;
|
continue;
|
||||||
@@ -296,7 +296,7 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
None => {}
|
None => {}
|
||||||
Some(fd) => {
|
Some(fd) => {
|
||||||
for p in fd {
|
for p in fd {
|
||||||
let enabled = get_bool(p, "enabled");
|
let enabled = get_bool(p, "enabled", true);
|
||||||
let name = get_str(p, "name").to_string();
|
let name = get_str(p, "name").to_string();
|
||||||
|
|
||||||
if !enabled || name.is_empty() {
|
if !enabled || name.is_empty() {
|
||||||
@@ -376,13 +376,11 @@ pub async fn send_http_request<R: Runtime>(
|
|||||||
|
|
||||||
if let Some(auth_name) = request.authentication_type.to_owned() {
|
if let Some(auth_name) = request.authentication_type.to_owned() {
|
||||||
let req = CallHttpAuthenticationRequest {
|
let req = CallHttpAuthenticationRequest {
|
||||||
config: serde_json::to_value(&request.authentication)
|
context_id: format!("{:x}", md5::compute(request.id)),
|
||||||
.unwrap()
|
values: serde_json::from_value(serde_json::to_value(&request.authentication).unwrap())
|
||||||
.as_object()
|
.unwrap(),
|
||||||
.unwrap()
|
|
||||||
.to_owned(),
|
|
||||||
method: sendable_req.method().to_string(),
|
|
||||||
url: sendable_req.url().to_string(),
|
url: sendable_req.url().to_string(),
|
||||||
|
method: sendable_req.method().to_string(),
|
||||||
headers: sendable_req
|
headers: sendable_req
|
||||||
.headers()
|
.headers()
|
||||||
.iter()
|
.iter()
|
||||||
@@ -604,10 +602,10 @@ fn ensure_proto(url_str: &str) -> String {
|
|||||||
format!("http://{url_str}")
|
format!("http://{url_str}")
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_bool(v: &Value, key: &str) -> bool {
|
fn get_bool(v: &Value, key: &str, fallback: bool) -> bool {
|
||||||
match v.get(key) {
|
match v.get(key) {
|
||||||
None => false,
|
None => fallback,
|
||||||
Some(v) => v.as_bool().unwrap_or_default(),
|
Some(v) => v.as_bool().unwrap_or(fallback),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+84
-335
@@ -6,33 +6,25 @@ use crate::encoding::read_response_body;
|
|||||||
use crate::grpc::metadata_to_map;
|
use crate::grpc::metadata_to_map;
|
||||||
use crate::http_request::send_http_request;
|
use crate::http_request::send_http_request;
|
||||||
use crate::notifications::YaakNotifier;
|
use crate::notifications::YaakNotifier;
|
||||||
use crate::render::{render_grpc_request, render_http_request, render_json_value, render_template};
|
use crate::render::{render_grpc_request, render_template};
|
||||||
use crate::template_callback::PluginTemplateCallback;
|
use crate::template_callback::PluginTemplateCallback;
|
||||||
use crate::updates::{UpdateMode, YaakUpdater};
|
use crate::updates::{UpdateMode, YaakUpdater};
|
||||||
use crate::window_menu::app_menu;
|
|
||||||
use chrono::Utc;
|
|
||||||
use eventsource_client::{EventParser, SSE};
|
use eventsource_client::{EventParser, SSE};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, warn};
|
||||||
use rand::random;
|
use rand::random;
|
||||||
use regex::Regex;
|
use regex::Regex;
|
||||||
use serde::Serialize;
|
|
||||||
use serde_json::{json, Value};
|
use serde_json::{json, Value};
|
||||||
use std::collections::BTreeMap;
|
use std::collections::{BTreeMap, HashMap};
|
||||||
use std::fs::{create_dir_all, File};
|
use std::fs::{create_dir_all, File};
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::process::exit;
|
|
||||||
use std::str::FromStr;
|
use std::str::FromStr;
|
||||||
use std::time::Duration;
|
use std::time::Duration;
|
||||||
use std::{fs, panic};
|
use std::{fs, panic};
|
||||||
#[cfg(target_os = "macos")]
|
use tauri::{AppHandle, Emitter, RunEvent, State, WebviewWindow};
|
||||||
use tauri::TitleBarStyle;
|
|
||||||
use tauri::{AppHandle, Emitter, LogicalSize, RunEvent, State, WebviewUrl, WebviewWindow};
|
|
||||||
use tauri::{Listener, Runtime};
|
use tauri::{Listener, Runtime};
|
||||||
use tauri::{Manager, WindowEvent};
|
use tauri::{Manager, WindowEvent};
|
||||||
use tauri_plugin_clipboard_manager::ClipboardExt;
|
|
||||||
use tauri_plugin_log::fern::colors::ColoredLevelConfig;
|
use tauri_plugin_log::fern::colors::ColoredLevelConfig;
|
||||||
use tauri_plugin_log::{Builder, Target, TargetKind};
|
use tauri_plugin_log::{Builder, Target, TargetKind};
|
||||||
use tauri_plugin_opener::OpenerExt;
|
|
||||||
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
||||||
use tokio::fs::read_to_string;
|
use tokio::fs::read_to_string;
|
||||||
use tokio::sync::Mutex;
|
use tokio::sync::Mutex;
|
||||||
@@ -51,28 +43,24 @@ use yaak_models::queries::{
|
|||||||
delete_all_http_responses_for_workspace, delete_cookie_jar, delete_environment, delete_folder,
|
delete_all_http_responses_for_workspace, delete_cookie_jar, delete_environment, delete_folder,
|
||||||
delete_grpc_connection, delete_grpc_request, delete_http_request, delete_http_response,
|
delete_grpc_connection, delete_grpc_request, delete_http_request, delete_http_response,
|
||||||
delete_plugin, delete_workspace, duplicate_folder, duplicate_grpc_request,
|
delete_plugin, delete_workspace, duplicate_folder, duplicate_grpc_request,
|
||||||
duplicate_http_request, ensure_base_environment, generate_id, generate_model_id,
|
duplicate_http_request, ensure_base_environment, generate_model_id, get_base_environment,
|
||||||
get_base_environment, get_cookie_jar, get_environment, get_folder, get_grpc_connection,
|
get_cookie_jar, get_environment, get_folder, get_grpc_connection, get_grpc_request,
|
||||||
get_grpc_request, get_http_request, get_http_response, get_key_value_raw,
|
get_http_request, get_http_response, get_key_value_raw, get_or_create_settings,
|
||||||
get_or_create_settings, get_or_create_workspace_meta, get_plugin, get_workspace,
|
get_or_create_workspace_meta, get_plugin, get_workspace, get_workspace_export_resources,
|
||||||
get_workspace_export_resources, list_cookie_jars, list_environments, list_folders,
|
list_cookie_jars, list_environments, list_folders, list_grpc_connections_for_workspace,
|
||||||
list_grpc_connections_for_workspace, list_grpc_events, list_grpc_requests, list_http_requests,
|
list_grpc_events, list_grpc_requests, list_http_requests, list_http_responses_for_workspace,
|
||||||
list_http_responses_for_request, list_http_responses_for_workspace, list_key_values_raw,
|
list_key_values_raw, list_plugins, list_workspaces, set_key_value_raw, update_response_if_id,
|
||||||
list_plugins, list_workspaces, set_key_value_raw, update_response_if_id, update_settings,
|
update_settings, upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection,
|
||||||
upsert_cookie_jar, upsert_environment, upsert_folder, upsert_grpc_connection,
|
|
||||||
upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace,
|
upsert_grpc_event, upsert_grpc_request, upsert_http_request, upsert_plugin, upsert_workspace,
|
||||||
upsert_workspace_meta, BatchUpsertResult, UpdateSource,
|
upsert_workspace_meta, BatchUpsertResult, UpdateSource,
|
||||||
};
|
};
|
||||||
use yaak_plugins::events::{
|
use yaak_plugins::events::{
|
||||||
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, Color,
|
BootResponse, CallHttpAuthenticationRequest, CallHttpRequestActionRequest, FilterResponse,
|
||||||
FilterResponse, FindHttpResponsesResponse, GetHttpAuthenticationResponse,
|
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||||
GetHttpRequestActionsResponse, GetHttpRequestByIdResponse, GetTemplateFunctionsResponse,
|
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, HttpHeader, InternalEvent,
|
||||||
HttpHeader, Icon, InternalEvent, InternalEventPayload, PromptTextResponse,
|
InternalEventPayload, JsonPrimitive, RenderPurpose, WindowContext,
|
||||||
RenderHttpRequestResponse, RenderPurpose, SendHttpRequestResponse, ShowToastRequest,
|
|
||||||
TemplateRenderResponse, WindowContext,
|
|
||||||
};
|
};
|
||||||
use yaak_plugins::manager::PluginManager;
|
use yaak_plugins::manager::PluginManager;
|
||||||
use yaak_plugins::plugin_handle::PluginHandle;
|
|
||||||
use yaak_sse::sse::ServerSentEvent;
|
use yaak_sse::sse::ServerSentEvent;
|
||||||
use yaak_templates::format::format_json;
|
use yaak_templates::format::format_json;
|
||||||
use yaak_templates::{Parser, Tokens};
|
use yaak_templates::{Parser, Tokens};
|
||||||
@@ -82,11 +70,13 @@ mod encoding;
|
|||||||
mod grpc;
|
mod grpc;
|
||||||
mod http_request;
|
mod http_request;
|
||||||
mod notifications;
|
mod notifications;
|
||||||
|
mod plugin_events;
|
||||||
mod render;
|
mod render;
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
mod tauri_plugin_mac_window;
|
mod tauri_plugin_mac_window;
|
||||||
mod template_callback;
|
mod template_callback;
|
||||||
mod updates;
|
mod updates;
|
||||||
|
mod window;
|
||||||
mod window_menu;
|
mod window_menu;
|
||||||
|
|
||||||
const DEFAULT_WINDOW_WIDTH: f64 = 1100.0;
|
const DEFAULT_WINDOW_WIDTH: f64 = 1100.0;
|
||||||
@@ -242,7 +232,8 @@ async fn cmd_grpc_go<R: Runtime>(
|
|||||||
if let Some(auth_name) = request.authentication_type.clone() {
|
if let Some(auth_name) = request.authentication_type.clone() {
|
||||||
let auth = request.authentication.clone();
|
let auth = request.authentication.clone();
|
||||||
let plugin_req = CallHttpAuthenticationRequest {
|
let plugin_req = CallHttpAuthenticationRequest {
|
||||||
config: serde_json::to_value(&auth).unwrap().as_object().unwrap().to_owned(),
|
context_id: format!("{:x}", md5::compute(request_id.to_string())),
|
||||||
|
values: serde_json::from_value(serde_json::to_value(&auth).unwrap()).unwrap(),
|
||||||
method: "POST".to_string(),
|
method: "POST".to_string(),
|
||||||
url: request.url.clone(),
|
url: request.url.clone(),
|
||||||
headers: metadata
|
headers: metadata
|
||||||
@@ -969,15 +960,31 @@ async fn cmd_template_functions<R: Runtime>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn cmd_get_http_authentication<R: Runtime>(
|
async fn cmd_get_http_authentication_summaries<R: Runtime>(
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
plugin_manager: State<'_, PluginManager>,
|
plugin_manager: State<'_, PluginManager>,
|
||||||
) -> Result<Vec<GetHttpAuthenticationResponse>, String> {
|
) -> Result<Vec<GetHttpAuthenticationSummaryResponse>, String> {
|
||||||
let results =
|
let results = plugin_manager
|
||||||
plugin_manager.get_http_authentication(&window).await.map_err(|e| e.to_string())?;
|
.get_http_authentication_summaries(&window)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())?;
|
||||||
Ok(results.into_iter().map(|(_, a)| a).collect())
|
Ok(results.into_iter().map(|(_, a)| a).collect())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn cmd_get_http_authentication_config<R: Runtime>(
|
||||||
|
window: WebviewWindow<R>,
|
||||||
|
plugin_manager: State<'_, PluginManager>,
|
||||||
|
auth_name: &str,
|
||||||
|
values: HashMap<String, JsonPrimitive>,
|
||||||
|
request_id: &str,
|
||||||
|
) -> Result<GetHttpAuthenticationConfigResponse, String> {
|
||||||
|
plugin_manager
|
||||||
|
.get_http_authentication_config(&window, auth_name, values, request_id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn cmd_call_http_request_action<R: Runtime>(
|
async fn cmd_call_http_request_action<R: Runtime>(
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
@@ -987,6 +994,21 @@ async fn cmd_call_http_request_action<R: Runtime>(
|
|||||||
plugin_manager.call_http_request_action(&window, req).await.map_err(|e| e.to_string())
|
plugin_manager.call_http_request_action(&window, req).await.map_err(|e| e.to_string())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
async fn cmd_call_http_authentication_action<R: Runtime>(
|
||||||
|
window: WebviewWindow<R>,
|
||||||
|
plugin_manager: State<'_, PluginManager>,
|
||||||
|
auth_name: &str,
|
||||||
|
action_index: i32,
|
||||||
|
values: HashMap<String, JsonPrimitive>,
|
||||||
|
request_id: &str,
|
||||||
|
) -> Result<(), String> {
|
||||||
|
plugin_manager
|
||||||
|
.call_http_authentication_action(&window, auth_name, action_index, values, request_id)
|
||||||
|
.await
|
||||||
|
.map_err(|e| e.to_string())
|
||||||
|
}
|
||||||
|
|
||||||
#[tauri::command]
|
#[tauri::command]
|
||||||
async fn cmd_curl_to_request<R: Runtime>(
|
async fn cmd_curl_to_request<R: Runtime>(
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
@@ -1175,7 +1197,7 @@ async fn cmd_install_plugin<R: Runtime>(
|
|||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
) -> Result<Plugin, String> {
|
) -> Result<Plugin, String> {
|
||||||
plugin_manager
|
plugin_manager
|
||||||
.add_plugin_by_dir(WindowContext::from_window(&window), &directory, true)
|
.add_plugin_by_dir(&WindowContext::from_window(&window), &directory, true)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
@@ -1205,7 +1227,7 @@ async fn cmd_uninstall_plugin<R: Runtime>(
|
|||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
plugin_manager
|
plugin_manager
|
||||||
.uninstall(WindowContext::from_window(&window), plugin.directory.as_str())
|
.uninstall(&WindowContext::from_window(&window), plugin.directory.as_str())
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
|
|
||||||
@@ -1458,7 +1480,7 @@ async fn cmd_reload_plugins<R: Runtime>(
|
|||||||
plugin_manager: State<'_, PluginManager>,
|
plugin_manager: State<'_, PluginManager>,
|
||||||
) -> Result<(), String> {
|
) -> Result<(), String> {
|
||||||
plugin_manager
|
plugin_manager
|
||||||
.initialize_all_plugins(window.app_handle(), WindowContext::from_window(&window))
|
.initialize_all_plugins(window.app_handle(), &WindowContext::from_window(&window))
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?;
|
.map_err(|e| e.to_string())?;
|
||||||
Ok(())
|
Ok(())
|
||||||
@@ -1656,15 +1678,17 @@ async fn cmd_new_child_window(
|
|||||||
current_pos.y + current_size.height / 2.0 - inner_size.1 / 2.0,
|
current_pos.y + current_size.height / 2.0 - inner_size.1 / 2.0,
|
||||||
);
|
);
|
||||||
|
|
||||||
let config = CreateWindowConfig {
|
let config = window::CreateWindowConfig {
|
||||||
label: label.as_str(),
|
label: label.as_str(),
|
||||||
title,
|
title,
|
||||||
url,
|
url,
|
||||||
inner_size,
|
inner_size: Some(inner_size),
|
||||||
position,
|
position: Some(position),
|
||||||
|
navigation_tx: None,
|
||||||
|
hide_titlebar: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
let child_window = create_window(&app_handle, config);
|
let child_window = window::create_window(&app_handle, config);
|
||||||
|
|
||||||
// NOTE: These listeners will remain active even when the windows close. Unfortunately,
|
// NOTE: These listeners will remain active even when the windows close. Unfortunately,
|
||||||
// there's no way to unlisten to events for now, so we just have to be defensive.
|
// there's no way to unlisten to events for now, so we just have to be defensive.
|
||||||
@@ -1830,6 +1854,7 @@ pub fn run() {
|
|||||||
Ok(())
|
Ok(())
|
||||||
})
|
})
|
||||||
.invoke_handler(tauri::generate_handler![
|
.invoke_handler(tauri::generate_handler![
|
||||||
|
cmd_call_http_authentication_action,
|
||||||
cmd_call_http_request_action,
|
cmd_call_http_request_action,
|
||||||
cmd_check_for_updates,
|
cmd_check_for_updates,
|
||||||
cmd_create_cookie_jar,
|
cmd_create_cookie_jar,
|
||||||
@@ -1859,7 +1884,8 @@ pub fn run() {
|
|||||||
cmd_get_environment,
|
cmd_get_environment,
|
||||||
cmd_get_folder,
|
cmd_get_folder,
|
||||||
cmd_get_grpc_request,
|
cmd_get_grpc_request,
|
||||||
cmd_get_http_authentication,
|
cmd_get_http_authentication_summaries,
|
||||||
|
cmd_get_http_authentication_config,
|
||||||
cmd_get_http_request,
|
cmd_get_http_request,
|
||||||
cmd_get_key_value,
|
cmd_get_key_value,
|
||||||
cmd_get_settings,
|
cmd_get_settings,
|
||||||
@@ -1998,113 +2024,21 @@ fn create_main_window(handle: &AppHandle, url: &str) -> WebviewWindow {
|
|||||||
}
|
}
|
||||||
.expect("Failed to generate label for new window");
|
.expect("Failed to generate label for new window");
|
||||||
|
|
||||||
let config = CreateWindowConfig {
|
let config = window::CreateWindowConfig {
|
||||||
url,
|
url,
|
||||||
label: label.as_str(),
|
label: label.as_str(),
|
||||||
title: "Yaak",
|
title: "Yaak",
|
||||||
inner_size: (DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT),
|
inner_size: Some((DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT)),
|
||||||
position: (
|
position: Some((
|
||||||
// Offset by random amount so it's easier to differentiate
|
// Offset by random amount so it's easier to differentiate
|
||||||
100.0 + random::<f64>() * 20.0,
|
100.0 + random::<f64>() * 20.0,
|
||||||
100.0 + random::<f64>() * 20.0,
|
100.0 + random::<f64>() * 20.0,
|
||||||
),
|
)),
|
||||||
|
navigation_tx: None,
|
||||||
|
hide_titlebar: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
create_window(handle, config)
|
window::create_window(handle, config)
|
||||||
}
|
|
||||||
|
|
||||||
struct CreateWindowConfig<'s> {
|
|
||||||
url: &'s str,
|
|
||||||
label: &'s str,
|
|
||||||
title: &'s str,
|
|
||||||
inner_size: (f64, f64),
|
|
||||||
position: (f64, f64),
|
|
||||||
}
|
|
||||||
|
|
||||||
fn create_window(handle: &AppHandle, config: CreateWindowConfig) -> WebviewWindow {
|
|
||||||
#[allow(unused_variables)]
|
|
||||||
let menu = app_menu(handle).unwrap();
|
|
||||||
|
|
||||||
// This causes the window to not be clickable (in AppImage), so disable on Linux
|
|
||||||
#[cfg(not(target_os = "linux"))]
|
|
||||||
handle.set_menu(menu).expect("Failed to set app menu");
|
|
||||||
|
|
||||||
info!("Create new window label={}", config.label);
|
|
||||||
|
|
||||||
let mut win_builder =
|
|
||||||
tauri::WebviewWindowBuilder::new(handle, config.label, WebviewUrl::App(config.url.into()))
|
|
||||||
.title(config.title)
|
|
||||||
.resizable(true)
|
|
||||||
.visible(false) // To prevent theme flashing, the frontend code calls show() immediately after configuring the theme
|
|
||||||
.fullscreen(false)
|
|
||||||
.disable_drag_drop_handler() // Required for frontend Dnd on windows
|
|
||||||
.inner_size(config.inner_size.0, config.inner_size.1)
|
|
||||||
.position(config.position.0, config.position.1)
|
|
||||||
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
|
|
||||||
|
|
||||||
// Add macOS-only things
|
|
||||||
#[cfg(target_os = "macos")]
|
|
||||||
{
|
|
||||||
win_builder = win_builder.hidden_title(true).title_bar_style(TitleBarStyle::Overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add non-MacOS things
|
|
||||||
#[cfg(not(target_os = "macos"))]
|
|
||||||
{
|
|
||||||
// Doesn't seem to work from Rust, here, so we do it in main.tsx
|
|
||||||
win_builder = win_builder.decorations(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(w) = handle.webview_windows().get(config.label) {
|
|
||||||
info!("Webview with label {} already exists. Focusing existing", config.label);
|
|
||||||
w.set_focus().unwrap();
|
|
||||||
return w.to_owned();
|
|
||||||
}
|
|
||||||
|
|
||||||
let win = win_builder.build().unwrap();
|
|
||||||
|
|
||||||
let webview_window = win.clone();
|
|
||||||
win.on_menu_event(move |w, event| {
|
|
||||||
if !w.is_focused().unwrap() {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
let event_id = event.id().0.as_str();
|
|
||||||
match event_id {
|
|
||||||
"quit" => exit(0),
|
|
||||||
"close" => w.close().unwrap(),
|
|
||||||
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
|
|
||||||
"zoom_in" => w.emit("zoom_in", true).unwrap(),
|
|
||||||
"zoom_out" => w.emit("zoom_out", true).unwrap(),
|
|
||||||
"settings" => w.emit("settings", true).unwrap(),
|
|
||||||
"open_feedback" => {
|
|
||||||
if let Err(e) =
|
|
||||||
w.app_handle().opener().open_url("https://yaak.app/feedback", None::<&str>)
|
|
||||||
{
|
|
||||||
warn!("Failed to open feedback {e:?}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Commands for development
|
|
||||||
"dev.reset_size" => webview_window
|
|
||||||
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
|
|
||||||
.unwrap(),
|
|
||||||
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
|
|
||||||
"dev.generate_theme_css" => {
|
|
||||||
w.emit("generate_theme_css", true).unwrap();
|
|
||||||
}
|
|
||||||
"dev.toggle_devtools" => {
|
|
||||||
if webview_window.is_devtools_open() {
|
|
||||||
webview_window.close_devtools();
|
|
||||||
} else {
|
|
||||||
webview_window.open_devtools();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
win
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn get_update_mode(h: &AppHandle) -> UpdateMode {
|
async fn get_update_mode(h: &AppHandle) -> UpdateMode {
|
||||||
@@ -2140,36 +2074,24 @@ fn monitor_plugin_events<R: Runtime>(app_handle: &AppHandle<R>) {
|
|||||||
// We might have recursive back-and-forth calls between app and plugin, so we don't
|
// We might have recursive back-and-forth calls between app and plugin, so we don't
|
||||||
// want to block here
|
// want to block here
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
handle_plugin_event(&app_handle, &event, &plugin).await;
|
crate::plugin_events::handle_plugin_event(&app_handle, &event, &plugin).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
plugin_manager.unsubscribe(rx_id.as_str()).await;
|
plugin_manager.unsubscribe(rx_id.as_str()).await;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize)]
|
async fn call_frontend<R: Runtime>(
|
||||||
#[serde(rename_all = "camelCase")]
|
|
||||||
struct FrontendCall<T: Serialize + Clone> {
|
|
||||||
args: T,
|
|
||||||
reply_id: String,
|
|
||||||
}
|
|
||||||
|
|
||||||
async fn call_frontend<T: Serialize + Clone, R: Runtime>(
|
|
||||||
window: WebviewWindow<R>,
|
window: WebviewWindow<R>,
|
||||||
event_name: &str,
|
event: &InternalEvent,
|
||||||
args: T,
|
) -> Option<InternalEventPayload> {
|
||||||
) -> PromptTextResponse {
|
window.emit_to(window.label(), "plugin_event", event.clone()).unwrap();
|
||||||
let reply_id = format!("{event_name}_reply_{}", generate_id());
|
let (tx, mut rx) = tokio::sync::watch::channel(None);
|
||||||
let payload = FrontendCall {
|
|
||||||
args,
|
|
||||||
reply_id: reply_id.clone(),
|
|
||||||
};
|
|
||||||
window.emit_to(window.label(), event_name, payload).unwrap();
|
|
||||||
let (tx, mut rx) = tokio::sync::watch::channel(PromptTextResponse::default());
|
|
||||||
|
|
||||||
|
let reply_id = event.id.clone();
|
||||||
let event_id = window.clone().listen(reply_id, move |ev| {
|
let event_id = window.clone().listen(reply_id, move |ev| {
|
||||||
let resp: PromptTextResponse = serde_json::from_str(ev.payload()).unwrap();
|
let resp: InternalEvent = serde_json::from_str(ev.payload()).unwrap();
|
||||||
if let Err(e) = tx.send(resp) {
|
if let Err(e) = tx.send(Some(resp.payload)) {
|
||||||
warn!("Failed to prompt for text {e:?}");
|
warn!("Failed to prompt for text {e:?}");
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -2181,180 +2103,7 @@ async fn call_frontend<T: Serialize + Clone, R: Runtime>(
|
|||||||
window.unlisten(event_id);
|
window.unlisten(event_id);
|
||||||
|
|
||||||
let v = rx.borrow();
|
let v = rx.borrow();
|
||||||
v.clone()
|
v.to_owned()
|
||||||
}
|
|
||||||
|
|
||||||
async fn handle_plugin_event<R: Runtime>(
|
|
||||||
app_handle: &AppHandle<R>,
|
|
||||||
event: &InternalEvent,
|
|
||||||
plugin_handle: &PluginHandle,
|
|
||||||
) {
|
|
||||||
// info!("Got event to app {}", event.id);
|
|
||||||
let window_context = event.window_context.to_owned();
|
|
||||||
let response_event: Option<InternalEventPayload> = match event.clone().payload {
|
|
||||||
InternalEventPayload::CopyTextRequest(req) => {
|
|
||||||
app_handle
|
|
||||||
.clipboard()
|
|
||||||
.write_text(req.text.as_str())
|
|
||||||
.expect("Failed to write text to clipboard");
|
|
||||||
None
|
|
||||||
}
|
|
||||||
InternalEventPayload::ShowToastRequest(req) => {
|
|
||||||
match window_context {
|
|
||||||
WindowContext::Label { label } => app_handle
|
|
||||||
.emit_to(label, "show_toast", req)
|
|
||||||
.expect("Failed to emit show_toast to window"),
|
|
||||||
_ => app_handle.emit("show_toast", req).expect("Failed to emit show_toast"),
|
|
||||||
};
|
|
||||||
None
|
|
||||||
}
|
|
||||||
InternalEventPayload::PromptTextRequest(req) => {
|
|
||||||
let window = get_window_from_window_context(app_handle, &window_context)
|
|
||||||
.expect("Failed to find window for render");
|
|
||||||
let resp = call_frontend(window, "show_prompt", req).await;
|
|
||||||
Some(InternalEventPayload::PromptTextResponse(resp))
|
|
||||||
}
|
|
||||||
InternalEventPayload::FindHttpResponsesRequest(req) => {
|
|
||||||
let http_responses = list_http_responses_for_request(
|
|
||||||
app_handle,
|
|
||||||
req.request_id.as_str(),
|
|
||||||
req.limit.map(|l| l as i64),
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap_or_default();
|
|
||||||
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
|
||||||
http_responses,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
InternalEventPayload::GetHttpRequestByIdRequest(req) => {
|
|
||||||
let http_request = get_http_request(app_handle, req.id.as_str()).await.unwrap();
|
|
||||||
Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse {
|
|
||||||
http_request,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
InternalEventPayload::RenderHttpRequestRequest(req) => {
|
|
||||||
let window = get_window_from_window_context(app_handle, &window_context)
|
|
||||||
.expect("Failed to find window for render http request");
|
|
||||||
|
|
||||||
let workspace = workspace_from_window(&window)
|
|
||||||
.await
|
|
||||||
.expect("Failed to get workspace_id from window URL");
|
|
||||||
let environment = environment_from_window(&window).await;
|
|
||||||
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
|
||||||
.await
|
|
||||||
.expect("Failed to get base environment");
|
|
||||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
|
||||||
let http_request = render_http_request(
|
|
||||||
&req.http_request,
|
|
||||||
&base_environment,
|
|
||||||
environment.as_ref(),
|
|
||||||
&cb,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
|
||||||
http_request,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
InternalEventPayload::TemplateRenderRequest(req) => {
|
|
||||||
let window = get_window_from_window_context(app_handle, &window_context)
|
|
||||||
.expect("Failed to find window for render");
|
|
||||||
|
|
||||||
let workspace = workspace_from_window(&window)
|
|
||||||
.await
|
|
||||||
.expect("Failed to get workspace_id from window URL");
|
|
||||||
let environment = environment_from_window(&window).await;
|
|
||||||
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
|
||||||
.await
|
|
||||||
.expect("Failed to get base environment");
|
|
||||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
|
||||||
let data =
|
|
||||||
render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await;
|
|
||||||
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
|
||||||
}
|
|
||||||
InternalEventPayload::ErrorResponse(resp) => {
|
|
||||||
let window = get_window_from_window_context(app_handle, &window_context)
|
|
||||||
.expect("Failed to find window for plugin reload");
|
|
||||||
let toast_event = plugin_handle.build_event_to_send(
|
|
||||||
WindowContext::from_window(&window),
|
|
||||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
|
||||||
message: resp.error,
|
|
||||||
color: Some(Color::Danger),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
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();
|
|
||||||
for plugin in plugins {
|
|
||||||
if plugin.directory != plugin_handle.dir {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let new_plugin = Plugin {
|
|
||||||
updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead
|
|
||||||
..plugin
|
|
||||||
};
|
|
||||||
upsert_plugin(&window, new_plugin, &UpdateSource::Plugin).await.unwrap();
|
|
||||||
}
|
|
||||||
let toast_event = plugin_handle.build_event_to_send(
|
|
||||||
WindowContext::from_window(&window),
|
|
||||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
|
||||||
message: format!("Reloaded plugin {}", plugin_handle.dir),
|
|
||||||
icon: Some(Icon::Info),
|
|
||||||
..Default::default()
|
|
||||||
}),
|
|
||||||
None,
|
|
||||||
);
|
|
||||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
|
||||||
None
|
|
||||||
}
|
|
||||||
InternalEventPayload::SendHttpRequestRequest(req) => {
|
|
||||||
let window = get_window_from_window_context(app_handle, &window_context)
|
|
||||||
.expect("Failed to find window for sending HTTP request");
|
|
||||||
let cookie_jar = cookie_jar_from_window(&window).await;
|
|
||||||
let environment = environment_from_window(&window).await;
|
|
||||||
|
|
||||||
let resp = create_default_http_response(
|
|
||||||
&window,
|
|
||||||
req.http_request.id.as_str(),
|
|
||||||
&UpdateSource::Plugin,
|
|
||||||
)
|
|
||||||
.await
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let result = send_http_request(
|
|
||||||
&window,
|
|
||||||
&req.http_request,
|
|
||||||
&resp,
|
|
||||||
environment,
|
|
||||||
cookie_jar,
|
|
||||||
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
let http_response = match result {
|
|
||||||
Ok(r) => r,
|
|
||||||
Err(_e) => return,
|
|
||||||
};
|
|
||||||
|
|
||||||
Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse {
|
|
||||||
http_response,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
_ => None,
|
|
||||||
};
|
|
||||||
|
|
||||||
if let Some(e) = response_event {
|
|
||||||
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
|
||||||
if let Err(e) = plugin_manager.reply(&event, &e).await {
|
|
||||||
warn!("Failed to reply to plugin manager: {:?}", e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_window_from_window_context<R: Runtime>(
|
fn get_window_from_window_context<R: Runtime>(
|
||||||
|
|||||||
@@ -0,0 +1,265 @@
|
|||||||
|
use crate::http_request::send_http_request;
|
||||||
|
use crate::render::{render_http_request, render_json_value};
|
||||||
|
use crate::template_callback::PluginTemplateCallback;
|
||||||
|
use crate::window::{create_window, CreateWindowConfig};
|
||||||
|
use crate::{
|
||||||
|
call_frontend, cookie_jar_from_window, environment_from_window, get_window_from_window_context,
|
||||||
|
workspace_from_window,
|
||||||
|
};
|
||||||
|
use chrono::Utc;
|
||||||
|
use log::warn;
|
||||||
|
use tauri::{AppHandle, Emitter, Manager, Runtime, State};
|
||||||
|
use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||||
|
use yaak_models::models::{HttpResponse, Plugin};
|
||||||
|
use yaak_models::queries::{
|
||||||
|
create_default_http_response, delete_plugin_key_value, get_base_environment, get_http_request,
|
||||||
|
get_plugin_key_value, list_http_responses_for_request, list_plugins, set_plugin_key_value,
|
||||||
|
upsert_plugin, UpdateSource,
|
||||||
|
};
|
||||||
|
use yaak_plugins::events::{
|
||||||
|
Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse,
|
||||||
|
GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload,
|
||||||
|
RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest,
|
||||||
|
TemplateRenderResponse, WindowContext, WindowNavigateEvent,
|
||||||
|
};
|
||||||
|
use yaak_plugins::manager::PluginManager;
|
||||||
|
use yaak_plugins::plugin_handle::PluginHandle;
|
||||||
|
|
||||||
|
pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||||
|
app_handle: &AppHandle<R>,
|
||||||
|
event: &InternalEvent,
|
||||||
|
plugin_handle: &PluginHandle,
|
||||||
|
) {
|
||||||
|
// info!("Got event to app {}", event.id);
|
||||||
|
let window_context = event.window_context.to_owned();
|
||||||
|
let response_event: Option<InternalEventPayload> = match event.clone().payload {
|
||||||
|
InternalEventPayload::CopyTextRequest(req) => {
|
||||||
|
app_handle
|
||||||
|
.clipboard()
|
||||||
|
.write_text(req.text.as_str())
|
||||||
|
.expect("Failed to write text to clipboard");
|
||||||
|
Some(InternalEventPayload::CopyTextResponse(EmptyPayload {}))
|
||||||
|
}
|
||||||
|
InternalEventPayload::ShowToastRequest(req) => {
|
||||||
|
match window_context {
|
||||||
|
WindowContext::Label { label } => app_handle
|
||||||
|
.emit_to(label, "show_toast", req)
|
||||||
|
.expect("Failed to emit show_toast to window"),
|
||||||
|
_ => app_handle.emit("show_toast", req).expect("Failed to emit show_toast"),
|
||||||
|
};
|
||||||
|
Some(InternalEventPayload::ShowToastResponse(EmptyPayload {}))
|
||||||
|
}
|
||||||
|
InternalEventPayload::PromptTextRequest(_) => {
|
||||||
|
let window = get_window_from_window_context(app_handle, &window_context)
|
||||||
|
.expect("Failed to find window for render");
|
||||||
|
call_frontend(window, event).await
|
||||||
|
}
|
||||||
|
InternalEventPayload::FindHttpResponsesRequest(req) => {
|
||||||
|
let http_responses = list_http_responses_for_request(
|
||||||
|
app_handle,
|
||||||
|
req.request_id.as_str(),
|
||||||
|
req.limit.map(|l| l as i64),
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap_or_default();
|
||||||
|
Some(InternalEventPayload::FindHttpResponsesResponse(FindHttpResponsesResponse {
|
||||||
|
http_responses,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
InternalEventPayload::GetHttpRequestByIdRequest(req) => {
|
||||||
|
let http_request = get_http_request(app_handle, req.id.as_str()).await.unwrap();
|
||||||
|
Some(InternalEventPayload::GetHttpRequestByIdResponse(GetHttpRequestByIdResponse {
|
||||||
|
http_request,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
InternalEventPayload::RenderHttpRequestRequest(req) => {
|
||||||
|
let window = get_window_from_window_context(app_handle, &window_context)
|
||||||
|
.expect("Failed to find window for render http request");
|
||||||
|
|
||||||
|
let workspace = workspace_from_window(&window)
|
||||||
|
.await
|
||||||
|
.expect("Failed to get workspace_id from window URL");
|
||||||
|
let environment = environment_from_window(&window).await;
|
||||||
|
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
||||||
|
.await
|
||||||
|
.expect("Failed to get base environment");
|
||||||
|
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||||
|
let http_request = render_http_request(
|
||||||
|
&req.http_request,
|
||||||
|
&base_environment,
|
||||||
|
environment.as_ref(),
|
||||||
|
&cb,
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||||
|
http_request,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
InternalEventPayload::TemplateRenderRequest(req) => {
|
||||||
|
let window = get_window_from_window_context(app_handle, &window_context)
|
||||||
|
.expect("Failed to find window for render");
|
||||||
|
|
||||||
|
let workspace = workspace_from_window(&window)
|
||||||
|
.await
|
||||||
|
.expect("Failed to get workspace_id from window URL");
|
||||||
|
let environment = environment_from_window(&window).await;
|
||||||
|
let base_environment = get_base_environment(&window, workspace.id.as_str())
|
||||||
|
.await
|
||||||
|
.expect("Failed to get base environment");
|
||||||
|
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||||
|
let data =
|
||||||
|
render_json_value(req.data, &base_environment, environment.as_ref(), &cb).await;
|
||||||
|
Some(InternalEventPayload::TemplateRenderResponse(TemplateRenderResponse { data }))
|
||||||
|
}
|
||||||
|
InternalEventPayload::ErrorResponse(resp) => {
|
||||||
|
let window = get_window_from_window_context(app_handle, &window_context)
|
||||||
|
.expect("Failed to find window for plugin reload");
|
||||||
|
let toast_event = plugin_handle.build_event_to_send(
|
||||||
|
&WindowContext::from_window(&window),
|
||||||
|
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||||
|
message: format!(
|
||||||
|
"Plugin error from {}: {}",
|
||||||
|
plugin_handle.name().await,
|
||||||
|
resp.error
|
||||||
|
),
|
||||||
|
color: Some(Color::Danger),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
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();
|
||||||
|
for plugin in plugins {
|
||||||
|
if plugin.directory != plugin_handle.dir {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let new_plugin = Plugin {
|
||||||
|
updated_at: Utc::now().naive_utc(), // TODO: Add reloaded_at field to use instead
|
||||||
|
..plugin
|
||||||
|
};
|
||||||
|
upsert_plugin(&window, new_plugin, &UpdateSource::Plugin).await.unwrap();
|
||||||
|
}
|
||||||
|
let toast_event = plugin_handle.build_event_to_send(
|
||||||
|
&WindowContext::from_window(&window),
|
||||||
|
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||||
|
message: format!("Reloaded plugin {}", plugin_handle.dir),
|
||||||
|
icon: Some(Icon::Info),
|
||||||
|
..Default::default()
|
||||||
|
}),
|
||||||
|
None,
|
||||||
|
);
|
||||||
|
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||||
|
None
|
||||||
|
}
|
||||||
|
InternalEventPayload::SendHttpRequestRequest(req) => {
|
||||||
|
let window = get_window_from_window_context(app_handle, &window_context)
|
||||||
|
.expect("Failed to find window for sending HTTP request");
|
||||||
|
let mut http_request = req.http_request;
|
||||||
|
let workspace = workspace_from_window(&window)
|
||||||
|
.await
|
||||||
|
.expect("Failed to get workspace_id from window URL");
|
||||||
|
let cookie_jar = cookie_jar_from_window(&window).await;
|
||||||
|
let environment = environment_from_window(&window).await;
|
||||||
|
|
||||||
|
if http_request.workspace_id.is_empty() {
|
||||||
|
http_request.workspace_id = workspace.id;
|
||||||
|
}
|
||||||
|
|
||||||
|
let resp = if http_request.id.is_empty() {
|
||||||
|
HttpResponse::new()
|
||||||
|
} else {
|
||||||
|
create_default_http_response(
|
||||||
|
&window,
|
||||||
|
http_request.id.as_str(),
|
||||||
|
&UpdateSource::Plugin,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
};
|
||||||
|
|
||||||
|
let result = send_http_request(
|
||||||
|
&window,
|
||||||
|
&http_request,
|
||||||
|
&resp,
|
||||||
|
environment,
|
||||||
|
cookie_jar,
|
||||||
|
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
||||||
|
)
|
||||||
|
.await;
|
||||||
|
|
||||||
|
let http_response = match result {
|
||||||
|
Ok(r) => r,
|
||||||
|
Err(_e) => return,
|
||||||
|
};
|
||||||
|
|
||||||
|
Some(InternalEventPayload::SendHttpRequestResponse(SendHttpRequestResponse {
|
||||||
|
http_response,
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
InternalEventPayload::OpenWindowRequest(req) => {
|
||||||
|
let label = req.label;
|
||||||
|
let (tx, mut rx) = tokio::sync::mpsc::channel(128);
|
||||||
|
let win_config = CreateWindowConfig {
|
||||||
|
url: &req.url,
|
||||||
|
label: &label.clone(),
|
||||||
|
title: &req.title.unwrap_or_default(),
|
||||||
|
navigation_tx: Some(tx),
|
||||||
|
inner_size: req.size.map(|s| (s.width, s.height)),
|
||||||
|
position: None,
|
||||||
|
hide_titlebar: false,
|
||||||
|
};
|
||||||
|
create_window(app_handle, win_config);
|
||||||
|
|
||||||
|
let event_id = event.id.clone();
|
||||||
|
let plugin_handle = plugin_handle.clone();
|
||||||
|
tauri::async_runtime::spawn(async move {
|
||||||
|
while let Some(url) = rx.recv().await {
|
||||||
|
let label = label.clone();
|
||||||
|
let url = url.to_string();
|
||||||
|
let event_to_send = plugin_handle.build_event_to_send(
|
||||||
|
&WindowContext::Label { label },
|
||||||
|
&InternalEventPayload::WindowNavigateEvent(WindowNavigateEvent { url }),
|
||||||
|
Some(event_id.clone()),
|
||||||
|
);
|
||||||
|
plugin_handle.send(&event_to_send).await.unwrap();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
None
|
||||||
|
}
|
||||||
|
InternalEventPayload::CloseWindowRequest(req) => {
|
||||||
|
if let Some(window) = app_handle.webview_windows().get(&req.label) {
|
||||||
|
window.close().expect("Failed to close window");
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
InternalEventPayload::SetKeyValueRequest(req) => {
|
||||||
|
let name = plugin_handle.name().await;
|
||||||
|
set_plugin_key_value(app_handle, &name, &req.key, &req.value).await;
|
||||||
|
Some(InternalEventPayload::SetKeyValueResponse(SetKeyValueResponse {}))
|
||||||
|
}
|
||||||
|
InternalEventPayload::GetKeyValueRequest(req) => {
|
||||||
|
let name = plugin_handle.name().await;
|
||||||
|
let value = get_plugin_key_value(app_handle, &name, &req.key).await.map(|v| v.value);
|
||||||
|
Some(InternalEventPayload::GetKeyValueResponse(GetKeyValueResponse { value }))
|
||||||
|
}
|
||||||
|
InternalEventPayload::DeleteKeyValueRequest(req) => {
|
||||||
|
let name = plugin_handle.name().await;
|
||||||
|
let deleted = delete_plugin_key_value(app_handle, &name, &req.key).await;
|
||||||
|
Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse { deleted }))
|
||||||
|
}
|
||||||
|
_ => None,
|
||||||
|
};
|
||||||
|
|
||||||
|
if let Some(e) = response_event {
|
||||||
|
let plugin_manager: State<'_, PluginManager> = app_handle.state();
|
||||||
|
if let Err(e) = plugin_manager.reply(&event, &e).await {
|
||||||
|
warn!("Failed to reply to plugin manager: {:?}", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -28,14 +28,13 @@ impl PluginTemplateCallback {
|
|||||||
|
|
||||||
impl TemplateCallback for PluginTemplateCallback {
|
impl TemplateCallback for PluginTemplateCallback {
|
||||||
async fn run(&self, fn_name: &str, args: HashMap<String, String>) -> Result<String, String> {
|
async fn run(&self, fn_name: &str, args: HashMap<String, String>) -> Result<String, String> {
|
||||||
let window_context = self.window_context.to_owned();
|
|
||||||
// The beta named the function `Response` but was changed in stable.
|
// The beta named the function `Response` but was changed in stable.
|
||||||
// Keep this here for a while because there's no easy way to migrate
|
// Keep this here for a while because there's no easy way to migrate
|
||||||
let fn_name = if fn_name == "Response" { "response" } else { fn_name };
|
let fn_name = if fn_name == "Response" { "response" } else { fn_name };
|
||||||
|
|
||||||
let function = self
|
let function = self
|
||||||
.plugin_manager
|
.plugin_manager
|
||||||
.get_template_functions_with_context(window_context.to_owned())
|
.get_template_functions_with_context(&self.window_context)
|
||||||
.await
|
.await
|
||||||
.map_err(|e| e.to_string())?
|
.map_err(|e| e.to_string())?
|
||||||
.iter()
|
.iter()
|
||||||
@@ -54,6 +53,9 @@ impl TemplateCallback for PluginTemplateCallback {
|
|||||||
FormInput::Checkbox(a) => a.base,
|
FormInput::Checkbox(a) => a.base,
|
||||||
FormInput::File(a) => a.base,
|
FormInput::File(a) => a.base,
|
||||||
FormInput::HttpRequest(a) => a.base,
|
FormInput::HttpRequest(a) => a.base,
|
||||||
|
FormInput::Accordion(_) => continue,
|
||||||
|
FormInput::Banner(_) => continue,
|
||||||
|
FormInput::Markdown(_) => continue,
|
||||||
};
|
};
|
||||||
if let None = args_with_defaults.get(base.name.as_str()) {
|
if let None = args_with_defaults.get(base.name.as_str()) {
|
||||||
args_with_defaults.insert(base.name, base.default_value.unwrap_or_default());
|
args_with_defaults.insert(base.name, base.default_value.unwrap_or_default());
|
||||||
@@ -63,7 +65,7 @@ impl TemplateCallback for PluginTemplateCallback {
|
|||||||
let resp = self
|
let resp = self
|
||||||
.plugin_manager
|
.plugin_manager
|
||||||
.call_template_function(
|
.call_template_function(
|
||||||
window_context,
|
&self.window_context,
|
||||||
fn_name,
|
fn_name,
|
||||||
args_with_defaults,
|
args_with_defaults,
|
||||||
self.render_purpose.to_owned(),
|
self.render_purpose.to_owned(),
|
||||||
|
|||||||
@@ -0,0 +1,129 @@
|
|||||||
|
use crate::window_menu::app_menu;
|
||||||
|
use crate::{DEFAULT_WINDOW_HEIGHT, DEFAULT_WINDOW_WIDTH, MIN_WINDOW_HEIGHT, MIN_WINDOW_WIDTH};
|
||||||
|
use log::{info, warn};
|
||||||
|
use std::process::exit;
|
||||||
|
use tauri::{
|
||||||
|
AppHandle, Emitter, LogicalSize, Manager, Runtime, TitleBarStyle, WebviewUrl, WebviewWindow,
|
||||||
|
};
|
||||||
|
use tauri_plugin_opener::OpenerExt;
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
#[derive(Default, Debug)]
|
||||||
|
pub(crate) struct CreateWindowConfig<'s> {
|
||||||
|
pub url: &'s str,
|
||||||
|
pub label: &'s str,
|
||||||
|
pub title: &'s str,
|
||||||
|
pub inner_size: Option<(f64, f64)>,
|
||||||
|
pub position: Option<(f64, f64)>,
|
||||||
|
pub navigation_tx: Option<mpsc::Sender<String>>,
|
||||||
|
pub hide_titlebar: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(crate) fn create_window<R: Runtime>(
|
||||||
|
handle: &AppHandle<R>,
|
||||||
|
config: CreateWindowConfig,
|
||||||
|
) -> WebviewWindow<R> {
|
||||||
|
#[allow(unused_variables)]
|
||||||
|
let menu = app_menu(handle).unwrap();
|
||||||
|
|
||||||
|
// This causes the window to not be clickable (in AppImage), so disable on Linux
|
||||||
|
#[cfg(not(target_os = "linux"))]
|
||||||
|
handle.set_menu(menu).expect("Failed to set app menu");
|
||||||
|
|
||||||
|
info!("Create new window label={}", config.label);
|
||||||
|
|
||||||
|
let mut win_builder =
|
||||||
|
tauri::WebviewWindowBuilder::new(handle, config.label, WebviewUrl::App(config.url.into()))
|
||||||
|
.title(config.title)
|
||||||
|
.resizable(true)
|
||||||
|
.visible(false) // To prevent theme flashing, the frontend code calls show() immediately after configuring the theme
|
||||||
|
.fullscreen(false)
|
||||||
|
.disable_drag_drop_handler() // Required for frontend Dnd on windows
|
||||||
|
.min_inner_size(MIN_WINDOW_WIDTH, MIN_WINDOW_HEIGHT);
|
||||||
|
|
||||||
|
if let Some((w, h)) = config.inner_size {
|
||||||
|
win_builder = win_builder.inner_size(w, h);
|
||||||
|
} else {
|
||||||
|
win_builder = win_builder.inner_size(600.0, 600.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some((x, y)) = config.position {
|
||||||
|
win_builder = win_builder.position(x, y);
|
||||||
|
} else {
|
||||||
|
win_builder = win_builder.center();
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(tx) = config.navigation_tx {
|
||||||
|
win_builder = win_builder.on_navigation(move |url| {
|
||||||
|
let url = url.to_string();
|
||||||
|
let tx = tx.clone();
|
||||||
|
tauri::async_runtime::block_on(async move {
|
||||||
|
tx.send(url).await.unwrap();
|
||||||
|
});
|
||||||
|
true
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if config.hide_titlebar {
|
||||||
|
#[cfg(target_os = "macos")]
|
||||||
|
{
|
||||||
|
win_builder = win_builder.hidden_title(true).title_bar_style(TitleBarStyle::Overlay);
|
||||||
|
}
|
||||||
|
#[cfg(not(target_os = "macos"))]
|
||||||
|
{
|
||||||
|
// Doesn't seem to work from Rust, here, so we do it in main.tsx
|
||||||
|
win_builder = win_builder.decorations(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Some(w) = handle.webview_windows().get(config.label) {
|
||||||
|
info!("Webview with label {} already exists. Focusing existing", config.label);
|
||||||
|
w.set_focus().unwrap();
|
||||||
|
return w.to_owned();
|
||||||
|
}
|
||||||
|
|
||||||
|
let win = win_builder.build().unwrap();
|
||||||
|
|
||||||
|
let webview_window = win.clone();
|
||||||
|
win.on_menu_event(move |w, event| {
|
||||||
|
if !w.is_focused().unwrap() {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let event_id = event.id().0.as_str();
|
||||||
|
match event_id {
|
||||||
|
"quit" => exit(0),
|
||||||
|
"close" => w.close().unwrap(),
|
||||||
|
"zoom_reset" => w.emit("zoom_reset", true).unwrap(),
|
||||||
|
"zoom_in" => w.emit("zoom_in", true).unwrap(),
|
||||||
|
"zoom_out" => w.emit("zoom_out", true).unwrap(),
|
||||||
|
"settings" => w.emit("settings", true).unwrap(),
|
||||||
|
"open_feedback" => {
|
||||||
|
if let Err(e) =
|
||||||
|
w.app_handle().opener().open_url("https://yaak.app/feedback", None::<&str>)
|
||||||
|
{
|
||||||
|
warn!("Failed to open feedback {e:?}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Commands for development
|
||||||
|
"dev.reset_size" => webview_window
|
||||||
|
.set_size(LogicalSize::new(DEFAULT_WINDOW_WIDTH, DEFAULT_WINDOW_HEIGHT))
|
||||||
|
.unwrap(),
|
||||||
|
"dev.refresh" => webview_window.eval("location.reload()").unwrap(),
|
||||||
|
"dev.generate_theme_css" => {
|
||||||
|
w.emit("generate_theme_css", true).unwrap();
|
||||||
|
}
|
||||||
|
"dev.toggle_devtools" => {
|
||||||
|
if webview_window.is_devtools_open() {
|
||||||
|
webview_window.close_devtools();
|
||||||
|
} else {
|
||||||
|
webview_window.open_devtools();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
win
|
||||||
|
}
|
||||||
@@ -3,9 +3,9 @@ use tauri::menu::{
|
|||||||
WINDOW_SUBMENU_ID,
|
WINDOW_SUBMENU_ID,
|
||||||
};
|
};
|
||||||
pub use tauri::AppHandle;
|
pub use tauri::AppHandle;
|
||||||
use tauri::Wry;
|
use tauri::Runtime;
|
||||||
|
|
||||||
pub fn app_menu(app_handle: &AppHandle) -> tauri::Result<Menu<Wry>> {
|
pub fn app_menu<R: Runtime>(app_handle: &AppHandle<R>) -> tauri::Result<Menu<R>> {
|
||||||
let pkg_info = app_handle.package_info();
|
let pkg_info = app_handle.package_info();
|
||||||
let config = app_handle.config();
|
let config = app_handle.config();
|
||||||
let about_metadata = AboutMetadata {
|
let about_metadata = AboutMetadata {
|
||||||
@@ -37,7 +37,7 @@ pub fn app_menu(app_handle: &AppHandle) -> tauri::Result<Menu<Wry>> {
|
|||||||
true,
|
true,
|
||||||
&[
|
&[
|
||||||
#[cfg(not(target_os = "macos"))]
|
#[cfg(not(target_os = "macos"))]
|
||||||
&PredefinedMenuItem::about(app_handle, None, Some(about_metadata))?,
|
&PredefinedMenuItem::about(app_handle, None, Some(about_metadata.clone()))?,
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
&MenuItemBuilder::with_id("open_feedback".to_string(), "Give Feedback")
|
&MenuItemBuilder::with_id("open_feedback".to_string(), "Give Feedback")
|
||||||
.build(app_handle)?,
|
.build(app_handle)?,
|
||||||
|
|||||||
+3
-3
@@ -28,7 +28,7 @@ var plugin = {
|
|||||||
name: "basic",
|
name: "basic",
|
||||||
label: "Basic Auth",
|
label: "Basic Auth",
|
||||||
shortLabel: "Basic",
|
shortLabel: "Basic",
|
||||||
config: [{
|
args: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
name: "username",
|
name: "username",
|
||||||
label: "Username",
|
label: "Username",
|
||||||
@@ -40,8 +40,8 @@ var plugin = {
|
|||||||
optional: true,
|
optional: true,
|
||||||
password: true
|
password: true
|
||||||
}],
|
}],
|
||||||
async onApply(_ctx, args) {
|
async onApply(_ctx, { values }) {
|
||||||
const { username, password } = args.config;
|
const { username, password } = values;
|
||||||
const value = "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
|
const value = "Basic " + Buffer.from(`${username}:${password}`).toString("base64");
|
||||||
return { setHeaders: [{ name: "Authorization", value }] };
|
return { setHeaders: [{ name: "Authorization", value }] };
|
||||||
}
|
}
|
||||||
|
|||||||
+3
-3
@@ -28,15 +28,15 @@ var plugin = {
|
|||||||
name: "bearer",
|
name: "bearer",
|
||||||
label: "Bearer Token",
|
label: "Bearer Token",
|
||||||
shortLabel: "Bearer",
|
shortLabel: "Bearer",
|
||||||
config: [{
|
args: [{
|
||||||
type: "text",
|
type: "text",
|
||||||
name: "token",
|
name: "token",
|
||||||
label: "Token",
|
label: "Token",
|
||||||
optional: true,
|
optional: true,
|
||||||
password: true
|
password: true
|
||||||
}],
|
}],
|
||||||
async onApply(_ctx, args) {
|
async onApply(_ctx, { values }) {
|
||||||
const { token } = args.config;
|
const { token } = values;
|
||||||
const value = `Bearer ${token}`.trim();
|
const value = `Bearer ${token}`.trim();
|
||||||
return { setHeaders: [{ name: "Authorization", value }] };
|
return { setHeaders: [{ name: "Authorization", value }] };
|
||||||
}
|
}
|
||||||
|
|||||||
+7
-6
@@ -3814,21 +3814,22 @@ var plugin = {
|
|||||||
name: "jwt",
|
name: "jwt",
|
||||||
label: "JWT Bearer",
|
label: "JWT Bearer",
|
||||||
shortLabel: "JWT",
|
shortLabel: "JWT",
|
||||||
config: [
|
args: [
|
||||||
{
|
{
|
||||||
type: "select",
|
type: "select",
|
||||||
name: "algorithm",
|
name: "algorithm",
|
||||||
label: "Algorithm",
|
label: "Algorithm",
|
||||||
hideLabel: true,
|
hideLabel: true,
|
||||||
defaultValue: defaultAlgorithm,
|
defaultValue: defaultAlgorithm,
|
||||||
options: algorithms.map((value) => ({ name: value === "none" ? "None" : value, value }))
|
options: algorithms.map((value) => ({ label: value === "none" ? "None" : value, value }))
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "editor",
|
type: "text",
|
||||||
name: "secret",
|
name: "secret",
|
||||||
label: "Secret or Private Key",
|
label: "Secret or Private Key",
|
||||||
|
password: true,
|
||||||
optional: true,
|
optional: true,
|
||||||
hideGutter: true
|
multiLine: true
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
type: "checkbox",
|
type: "checkbox",
|
||||||
@@ -3844,8 +3845,8 @@ var plugin = {
|
|||||||
placeholder: "{ }"
|
placeholder: "{ }"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
async onApply(_ctx, args) {
|
async onApply(_ctx, { values }) {
|
||||||
const { algorithm, secret: _secret, secretBase64, payload } = args.config;
|
const { algorithm, secret: _secret, secretBase64, payload } = values;
|
||||||
const secret = secretBase64 ? Buffer.from(`${_secret}`, "base64") : `${_secret}`;
|
const secret = secretBase64 ? Buffer.from(`${_secret}`, "base64") : `${_secret}`;
|
||||||
const token = import_jsonwebtoken.default.sign(`${payload}`, secret, { algorithm });
|
const token = import_jsonwebtoken.default.sign(`${payload}`, secret, { algorithm });
|
||||||
const value = `Bearer ${token}`;
|
const value = `Bearer ${token}`;
|
||||||
|
|||||||
+673
@@ -0,0 +1,673 @@
|
|||||||
|
"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);
|
||||||
|
|
||||||
|
// src/grants/authorizationCode.ts
|
||||||
|
var import_node_crypto = require("node:crypto");
|
||||||
|
|
||||||
|
// src/getAccessToken.ts
|
||||||
|
var import_node_fs = require("node:fs");
|
||||||
|
async function getAccessToken(ctx, {
|
||||||
|
accessTokenUrl,
|
||||||
|
scope,
|
||||||
|
params,
|
||||||
|
grantType,
|
||||||
|
credentialsInBody,
|
||||||
|
clientId,
|
||||||
|
clientSecret
|
||||||
|
}) {
|
||||||
|
console.log("Getting access token", accessTokenUrl);
|
||||||
|
const httpRequest = {
|
||||||
|
method: "POST",
|
||||||
|
url: accessTokenUrl,
|
||||||
|
bodyType: "application/x-www-form-urlencoded",
|
||||||
|
body: {
|
||||||
|
form: [
|
||||||
|
{ name: "grant_type", value: grantType },
|
||||||
|
...params
|
||||||
|
]
|
||||||
|
},
|
||||||
|
headers: [
|
||||||
|
{ name: "User-Agent", value: "yaak" },
|
||||||
|
{ name: "Accept", value: "application/x-www-form-urlencoded, application/json" },
|
||||||
|
{ name: "Content-Type", value: "application/x-www-form-urlencoded" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
if (scope) httpRequest.body.form.push({ name: "scope", value: scope });
|
||||||
|
if (credentialsInBody) {
|
||||||
|
httpRequest.body.form.push({ name: "client_id", value: clientId });
|
||||||
|
httpRequest.body.form.push({ name: "client_secret", value: clientSecret });
|
||||||
|
} else {
|
||||||
|
const value = "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
||||||
|
httpRequest.headers.push({ name: "Authorization", value });
|
||||||
|
}
|
||||||
|
const resp = await ctx.httpRequest.send({ httpRequest });
|
||||||
|
if (resp.status < 200 || resp.status >= 300) {
|
||||||
|
throw new Error("Failed to fetch access token with status=" + resp.status);
|
||||||
|
}
|
||||||
|
const body = (0, import_node_fs.readFileSync)(resp.bodyPath ?? "", "utf8");
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = JSON.parse(body);
|
||||||
|
} catch {
|
||||||
|
response = Object.fromEntries(new URLSearchParams(body));
|
||||||
|
}
|
||||||
|
if (response.error) {
|
||||||
|
throw new Error("Failed to fetch access token with " + response.error);
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/getOrRefreshAccessToken.ts
|
||||||
|
var import_node_fs2 = require("node:fs");
|
||||||
|
|
||||||
|
// src/store.ts
|
||||||
|
async function storeToken(ctx, contextId, response) {
|
||||||
|
if (!response.access_token) {
|
||||||
|
throw new Error(`Token not found in response`);
|
||||||
|
}
|
||||||
|
const expiresAt = response.expires_in ? Date.now() + response.expires_in * 1e3 : null;
|
||||||
|
const token = {
|
||||||
|
response,
|
||||||
|
expiresAt
|
||||||
|
};
|
||||||
|
await ctx.store.set(tokenStoreKey(contextId), token);
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
async function getToken(ctx, contextId) {
|
||||||
|
return ctx.store.get(tokenStoreKey(contextId));
|
||||||
|
}
|
||||||
|
async function deleteToken(ctx, contextId) {
|
||||||
|
return ctx.store.delete(tokenStoreKey(contextId));
|
||||||
|
}
|
||||||
|
function tokenStoreKey(context_id) {
|
||||||
|
return ["token", context_id].join("::");
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/getOrRefreshAccessToken.ts
|
||||||
|
async function getOrRefreshAccessToken(ctx, contextId, {
|
||||||
|
scope,
|
||||||
|
accessTokenUrl,
|
||||||
|
credentialsInBody,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
forceRefresh
|
||||||
|
}) {
|
||||||
|
const token = await getToken(ctx, contextId);
|
||||||
|
if (token == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const now = Date.now() / 1e3;
|
||||||
|
const isExpired = token.expiresAt && now > token.expiresAt;
|
||||||
|
if (!isExpired && !forceRefresh) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
if (!token.response.refresh_token) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
const httpRequest = {
|
||||||
|
method: "POST",
|
||||||
|
url: accessTokenUrl,
|
||||||
|
bodyType: "application/x-www-form-urlencoded",
|
||||||
|
body: {
|
||||||
|
form: [
|
||||||
|
{ name: "grant_type", value: "refresh_token" },
|
||||||
|
{ name: "refresh_token", value: token.response.refresh_token }
|
||||||
|
]
|
||||||
|
},
|
||||||
|
headers: [
|
||||||
|
{ name: "User-Agent", value: "yaak" },
|
||||||
|
{ name: "Accept", value: "application/x-www-form-urlencoded, application/json" },
|
||||||
|
{ name: "Content-Type", value: "application/x-www-form-urlencoded" }
|
||||||
|
]
|
||||||
|
};
|
||||||
|
if (scope) httpRequest.body.form.push({ name: "scope", value: scope });
|
||||||
|
if (credentialsInBody) {
|
||||||
|
httpRequest.body.form.push({ name: "client_id", value: clientId });
|
||||||
|
httpRequest.body.form.push({ name: "client_secret", value: clientSecret });
|
||||||
|
} else {
|
||||||
|
const value = "Basic " + Buffer.from(`${clientId}:${clientSecret}`).toString("base64");
|
||||||
|
httpRequest.headers.push({ name: "Authorization", value });
|
||||||
|
}
|
||||||
|
const resp = await ctx.httpRequest.send({ httpRequest });
|
||||||
|
if (resp.status === 401) {
|
||||||
|
console.log("Unauthorized refresh_token request");
|
||||||
|
await deleteToken(ctx, contextId);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
if (resp.status < 200 || resp.status >= 300) {
|
||||||
|
throw new Error("Failed to fetch access token with status=" + resp.status);
|
||||||
|
}
|
||||||
|
const body = (0, import_node_fs2.readFileSync)(resp.bodyPath ?? "", "utf8");
|
||||||
|
let response;
|
||||||
|
try {
|
||||||
|
response = JSON.parse(body);
|
||||||
|
} catch {
|
||||||
|
response = Object.fromEntries(new URLSearchParams(body));
|
||||||
|
}
|
||||||
|
if (response.error) {
|
||||||
|
throw new Error(`Failed to fetch access token with ${response.error} -> ${response.error_description}`);
|
||||||
|
}
|
||||||
|
const newResponse = {
|
||||||
|
...response,
|
||||||
|
// Assign a new one or keep the old one,
|
||||||
|
refresh_token: response.refresh_token ?? token.response.refresh_token
|
||||||
|
};
|
||||||
|
return storeToken(ctx, contextId, newResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/grants/authorizationCode.ts
|
||||||
|
var PKCE_SHA256 = "S256";
|
||||||
|
var PKCE_PLAIN = "plain";
|
||||||
|
var DEFAULT_PKCE_METHOD = PKCE_SHA256;
|
||||||
|
async function getAuthorizationCode(ctx, contextId, {
|
||||||
|
authorizationUrl: authorizationUrlRaw,
|
||||||
|
accessTokenUrl,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
redirectUri,
|
||||||
|
scope,
|
||||||
|
state,
|
||||||
|
credentialsInBody,
|
||||||
|
pkce
|
||||||
|
}) {
|
||||||
|
const token = await getOrRefreshAccessToken(ctx, contextId, {
|
||||||
|
accessTokenUrl,
|
||||||
|
scope,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
credentialsInBody
|
||||||
|
});
|
||||||
|
if (token != null) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
const authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`);
|
||||||
|
authorizationUrl.searchParams.set("response_type", "code");
|
||||||
|
authorizationUrl.searchParams.set("client_id", clientId);
|
||||||
|
if (redirectUri) authorizationUrl.searchParams.set("redirect_uri", redirectUri);
|
||||||
|
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
||||||
|
if (state) authorizationUrl.searchParams.set("state", state);
|
||||||
|
if (pkce) {
|
||||||
|
const verifier = pkce.codeVerifier || createPkceCodeVerifier();
|
||||||
|
const challengeMethod = pkce.challengeMethod || DEFAULT_PKCE_METHOD;
|
||||||
|
authorizationUrl.searchParams.set("code_challenge", createPkceCodeChallenge(verifier, challengeMethod));
|
||||||
|
authorizationUrl.searchParams.set("code_challenge_method", challengeMethod);
|
||||||
|
}
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const authorizationUrlStr = authorizationUrl.toString();
|
||||||
|
console.log("Authorizing", authorizationUrlStr);
|
||||||
|
let { close } = await ctx.window.openUrl({
|
||||||
|
url: authorizationUrlStr,
|
||||||
|
label: "oauth-authorization-url",
|
||||||
|
async onNavigate({ url: urlStr }) {
|
||||||
|
const url = new URL(urlStr);
|
||||||
|
if (url.searchParams.has("error")) {
|
||||||
|
return reject(new Error(`Failed to authorize: ${url.searchParams.get("error")}`));
|
||||||
|
}
|
||||||
|
const code = url.searchParams.get("code");
|
||||||
|
if (!code) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
const response = await getAccessToken(ctx, {
|
||||||
|
grantType: "authorization_code",
|
||||||
|
accessTokenUrl,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
scope,
|
||||||
|
credentialsInBody,
|
||||||
|
params: [
|
||||||
|
{ name: "code", value: code },
|
||||||
|
...redirectUri ? [{ name: "redirect_uri", value: redirectUri }] : []
|
||||||
|
]
|
||||||
|
});
|
||||||
|
try {
|
||||||
|
resolve(await storeToken(ctx, contextId, response));
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
function createPkceCodeVerifier() {
|
||||||
|
return encodeForPkce((0, import_node_crypto.randomBytes)(32));
|
||||||
|
}
|
||||||
|
function createPkceCodeChallenge(verifier, method) {
|
||||||
|
if (method === "plain") {
|
||||||
|
return verifier;
|
||||||
|
}
|
||||||
|
const hash = encodeForPkce((0, import_node_crypto.createHash)("sha256").update(verifier).digest());
|
||||||
|
return hash.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||||
|
}
|
||||||
|
function encodeForPkce(bytes) {
|
||||||
|
return bytes.toString("base64").replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/grants/clientCredentials.ts
|
||||||
|
async function getClientCredentials(ctx, contextId, {
|
||||||
|
accessTokenUrl,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
scope,
|
||||||
|
credentialsInBody
|
||||||
|
}) {
|
||||||
|
const token = await getToken(ctx, contextId);
|
||||||
|
if (token) {
|
||||||
|
}
|
||||||
|
const response = await getAccessToken(ctx, {
|
||||||
|
grantType: "client_credentials",
|
||||||
|
accessTokenUrl,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
scope,
|
||||||
|
credentialsInBody,
|
||||||
|
params: []
|
||||||
|
});
|
||||||
|
return storeToken(ctx, contextId, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/grants/implicit.ts
|
||||||
|
function getImplicit(ctx, contextId, {
|
||||||
|
authorizationUrl: authorizationUrlRaw,
|
||||||
|
responseType,
|
||||||
|
clientId,
|
||||||
|
redirectUri,
|
||||||
|
scope,
|
||||||
|
state
|
||||||
|
}) {
|
||||||
|
return new Promise(async (resolve, reject) => {
|
||||||
|
const token = await getToken(ctx, contextId);
|
||||||
|
if (token) {
|
||||||
|
}
|
||||||
|
const authorizationUrl = new URL(`${authorizationUrlRaw ?? ""}`);
|
||||||
|
authorizationUrl.searchParams.set("response_type", "code");
|
||||||
|
authorizationUrl.searchParams.set("client_id", clientId);
|
||||||
|
if (redirectUri) authorizationUrl.searchParams.set("redirect_uri", redirectUri);
|
||||||
|
if (scope) authorizationUrl.searchParams.set("scope", scope);
|
||||||
|
if (state) authorizationUrl.searchParams.set("state", state);
|
||||||
|
if (responseType.includes("id_token")) {
|
||||||
|
authorizationUrl.searchParams.set("nonce", String(Math.floor(Math.random() * 9999999999999) + 1));
|
||||||
|
}
|
||||||
|
const authorizationUrlStr = authorizationUrl.toString();
|
||||||
|
let { close } = await ctx.window.openUrl({
|
||||||
|
url: authorizationUrlStr,
|
||||||
|
label: "oauth-authorization-url",
|
||||||
|
async onNavigate({ url: urlStr }) {
|
||||||
|
const url = new URL(urlStr);
|
||||||
|
if (url.searchParams.has("error")) {
|
||||||
|
return reject(Error(`Failed to authorize: ${url.searchParams.get("error")}`));
|
||||||
|
}
|
||||||
|
close();
|
||||||
|
const hash = url.hash.slice(1);
|
||||||
|
const params = new URLSearchParams(hash);
|
||||||
|
const idToken = params.get("id_token");
|
||||||
|
if (idToken) {
|
||||||
|
params.set("access_token", idToken);
|
||||||
|
params.delete("id_token");
|
||||||
|
}
|
||||||
|
const response = Object.fromEntries(params);
|
||||||
|
try {
|
||||||
|
resolve(await storeToken(ctx, contextId, response));
|
||||||
|
} catch (err) {
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/grants/password.ts
|
||||||
|
async function getPassword(ctx, contextId, {
|
||||||
|
accessTokenUrl,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
username,
|
||||||
|
password,
|
||||||
|
credentialsInBody,
|
||||||
|
scope
|
||||||
|
}) {
|
||||||
|
const token = await getOrRefreshAccessToken(ctx, contextId, {
|
||||||
|
accessTokenUrl,
|
||||||
|
scope,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
credentialsInBody
|
||||||
|
});
|
||||||
|
if (token != null) {
|
||||||
|
return token;
|
||||||
|
}
|
||||||
|
const response = await getAccessToken(ctx, {
|
||||||
|
accessTokenUrl,
|
||||||
|
clientId,
|
||||||
|
clientSecret,
|
||||||
|
scope,
|
||||||
|
grantType: "password",
|
||||||
|
credentialsInBody,
|
||||||
|
params: [
|
||||||
|
{ name: "username", value: username },
|
||||||
|
{ name: "password", value: password }
|
||||||
|
]
|
||||||
|
});
|
||||||
|
return storeToken(ctx, contextId, response);
|
||||||
|
}
|
||||||
|
|
||||||
|
// src/index.ts
|
||||||
|
var grantTypes = [
|
||||||
|
{ label: "Authorization Code", value: "authorization_code" },
|
||||||
|
{ label: "Implicit", value: "implicit" },
|
||||||
|
{ label: "Resource Owner Password Credential", value: "password" },
|
||||||
|
{ label: "Client Credentials", value: "client_credentials" }
|
||||||
|
];
|
||||||
|
var defaultGrantType = grantTypes[0].value;
|
||||||
|
function hiddenIfNot(grantTypes2, ...other) {
|
||||||
|
return (_ctx, { values }) => {
|
||||||
|
const hasGrantType = grantTypes2.find((t) => t === String(values.grantType ?? defaultGrantType));
|
||||||
|
const hasOtherBools = other.every((t) => t(values));
|
||||||
|
const show = hasGrantType && hasOtherBools;
|
||||||
|
return { hidden: !show };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
var authorizationUrls = [
|
||||||
|
"https://github.com/login/oauth/authorize",
|
||||||
|
"https://account.box.com/api/oauth2/authorize",
|
||||||
|
"https://accounts.google.com/o/oauth2/v2/auth",
|
||||||
|
"https://api.imgur.com/oauth2/authorize",
|
||||||
|
"https://bitly.com/oauth/authorize",
|
||||||
|
"https://gitlab.example.com/oauth/authorize",
|
||||||
|
"https://medium.com/m/oauth/authorize",
|
||||||
|
"https://public-api.wordpress.com/oauth2/authorize",
|
||||||
|
"https://slack.com/oauth/authorize",
|
||||||
|
"https://todoist.com/oauth/authorize",
|
||||||
|
"https://www.dropbox.com/oauth2/authorize",
|
||||||
|
"https://www.linkedin.com/oauth/v2/authorization",
|
||||||
|
"https://MY_SHOP.myshopify.com/admin/oauth/access_token"
|
||||||
|
];
|
||||||
|
var accessTokenUrls = [
|
||||||
|
"https://github.com/login/oauth/access_token",
|
||||||
|
"https://api-ssl.bitly.com/oauth/access_token",
|
||||||
|
"https://api.box.com/oauth2/token",
|
||||||
|
"https://api.dropboxapi.com/oauth2/token",
|
||||||
|
"https://api.imgur.com/oauth2/token",
|
||||||
|
"https://api.medium.com/v1/tokens",
|
||||||
|
"https://gitlab.example.com/oauth/token",
|
||||||
|
"https://public-api.wordpress.com/oauth2/token",
|
||||||
|
"https://slack.com/api/oauth.access",
|
||||||
|
"https://todoist.com/oauth/access_token",
|
||||||
|
"https://www.googleapis.com/oauth2/v4/token",
|
||||||
|
"https://www.linkedin.com/oauth/v2/accessToken",
|
||||||
|
"https://MY_SHOP.myshopify.com/admin/oauth/authorize"
|
||||||
|
];
|
||||||
|
var plugin = {
|
||||||
|
authentication: {
|
||||||
|
name: "oauth2",
|
||||||
|
label: "OAuth 2.0",
|
||||||
|
shortLabel: "OAuth 2",
|
||||||
|
actions: [
|
||||||
|
{
|
||||||
|
label: "Copy Current Token",
|
||||||
|
name: "copyCurrentToken",
|
||||||
|
icon: "copy",
|
||||||
|
async onSelect(ctx, { contextId }) {
|
||||||
|
const token = await getToken(ctx, contextId);
|
||||||
|
if (token == null) {
|
||||||
|
await ctx.toast.show({ message: "No token to copy", color: "warning" });
|
||||||
|
} else {
|
||||||
|
await ctx.clipboard.copyText(token.response.access_token);
|
||||||
|
await ctx.toast.show({ message: "Token copied to clipboard", icon: "copy", color: "success" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
label: "Delete Token",
|
||||||
|
name: "clearToken",
|
||||||
|
icon: "trash",
|
||||||
|
async onSelect(ctx, { contextId }) {
|
||||||
|
if (await deleteToken(ctx, contextId)) {
|
||||||
|
await ctx.toast.show({ message: "Token deleted", color: "success" });
|
||||||
|
} else {
|
||||||
|
await ctx.toast.show({ message: "No token to delete", color: "warning" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
args: [
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "grantType",
|
||||||
|
label: "Grant Type",
|
||||||
|
hideLabel: true,
|
||||||
|
defaultValue: defaultGrantType,
|
||||||
|
options: grantTypes
|
||||||
|
},
|
||||||
|
// Always-present fields
|
||||||
|
{ type: "text", name: "clientId", label: "Client ID" },
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "clientSecret",
|
||||||
|
label: "Client Secret",
|
||||||
|
password: true,
|
||||||
|
dynamic: hiddenIfNot(["authorization_code", "password", "client_credentials"])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "authorizationUrl",
|
||||||
|
label: "Authorization URL",
|
||||||
|
dynamic: hiddenIfNot(["authorization_code", "implicit"]),
|
||||||
|
placeholder: authorizationUrls[0],
|
||||||
|
completionOptions: authorizationUrls.map((url) => ({ label: url, value: url }))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "accessTokenUrl",
|
||||||
|
label: "Access Token URL",
|
||||||
|
placeholder: accessTokenUrls[0],
|
||||||
|
dynamic: hiddenIfNot(["authorization_code", "password", "client_credentials"]),
|
||||||
|
completionOptions: accessTokenUrls.map((url) => ({ label: url, value: url }))
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "redirectUri",
|
||||||
|
label: "Redirect URI",
|
||||||
|
optional: true,
|
||||||
|
dynamic: hiddenIfNot(["authorization_code", "implicit"])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "state",
|
||||||
|
label: "State",
|
||||||
|
optional: true,
|
||||||
|
dynamic: hiddenIfNot(["authorization_code", "implicit"])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "checkbox",
|
||||||
|
name: "usePkce",
|
||||||
|
label: "Use PKCE",
|
||||||
|
dynamic: hiddenIfNot(["authorization_code"])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "pkceChallengeMethod",
|
||||||
|
label: "Code Challenge Method",
|
||||||
|
options: [{ label: "SHA-256", value: PKCE_SHA256 }, { label: "Plain", value: PKCE_PLAIN }],
|
||||||
|
defaultValue: DEFAULT_PKCE_METHOD,
|
||||||
|
dynamic: hiddenIfNot(["authorization_code"], ({ usePkce }) => !!usePkce)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "pkceCodeVerifier",
|
||||||
|
label: "Code Verifier",
|
||||||
|
placeholder: "Automatically generated if not provided",
|
||||||
|
optional: true,
|
||||||
|
dynamic: hiddenIfNot(["authorization_code"], ({ usePkce }) => !!usePkce)
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "username",
|
||||||
|
label: "Username",
|
||||||
|
optional: true,
|
||||||
|
dynamic: hiddenIfNot(["password"])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "text",
|
||||||
|
name: "password",
|
||||||
|
label: "Password",
|
||||||
|
password: true,
|
||||||
|
optional: true,
|
||||||
|
dynamic: hiddenIfNot(["password"])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "responseType",
|
||||||
|
label: "Response Type",
|
||||||
|
defaultValue: "token",
|
||||||
|
options: [
|
||||||
|
{ label: "Access Token", value: "token" },
|
||||||
|
{ label: "ID Token", value: "id_token" },
|
||||||
|
{ label: "ID and Access Token", value: "id_token token" }
|
||||||
|
],
|
||||||
|
dynamic: hiddenIfNot(["implicit"])
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "accordion",
|
||||||
|
label: "Advanced",
|
||||||
|
inputs: [
|
||||||
|
{ type: "text", name: "scope", label: "Scope", optional: true },
|
||||||
|
{ type: "text", name: "headerPrefix", label: "Header Prefix", optional: true, defaultValue: "Bearer" },
|
||||||
|
{
|
||||||
|
type: "select",
|
||||||
|
name: "credentials",
|
||||||
|
label: "Send Credentials",
|
||||||
|
defaultValue: "body",
|
||||||
|
options: [
|
||||||
|
{ label: "In Request Body", value: "body" },
|
||||||
|
{ label: "As Basic Authentication", value: "basic" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
type: "accordion",
|
||||||
|
label: "Access Token Response",
|
||||||
|
async dynamic(ctx, { contextId }) {
|
||||||
|
const token = await getToken(ctx, contextId);
|
||||||
|
if (token == null) {
|
||||||
|
return { hidden: true };
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
label: "Access Token Response",
|
||||||
|
inputs: [
|
||||||
|
{
|
||||||
|
type: "editor",
|
||||||
|
defaultValue: JSON.stringify(token.response, null, 2),
|
||||||
|
hideLabel: true,
|
||||||
|
readOnly: true,
|
||||||
|
language: "json"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
async onApply(ctx, { values, contextId }) {
|
||||||
|
const headerPrefix = optionalString(values, "headerPrefix") ?? "";
|
||||||
|
const grantType = requiredString(values, "grantType");
|
||||||
|
const credentialsInBody = values.credentials === "body";
|
||||||
|
console.log("Performing OAuth", values);
|
||||||
|
let token;
|
||||||
|
if (grantType === "authorization_code") {
|
||||||
|
const authorizationUrl = requiredString(values, "authorizationUrl");
|
||||||
|
const accessTokenUrl = requiredString(values, "accessTokenUrl");
|
||||||
|
token = await getAuthorizationCode(ctx, contextId, {
|
||||||
|
accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`,
|
||||||
|
authorizationUrl: authorizationUrl.match(/^https?:\/\//) ? authorizationUrl : `https://${authorizationUrl}`,
|
||||||
|
clientId: requiredString(values, "clientId"),
|
||||||
|
clientSecret: requiredString(values, "clientSecret"),
|
||||||
|
redirectUri: optionalString(values, "redirectUri"),
|
||||||
|
scope: optionalString(values, "scope"),
|
||||||
|
state: optionalString(values, "state"),
|
||||||
|
credentialsInBody,
|
||||||
|
pkce: values.usePkce ? {
|
||||||
|
challengeMethod: requiredString(values, "pkceChallengeMethod"),
|
||||||
|
codeVerifier: optionalString(values, "pkceCodeVerifier")
|
||||||
|
} : null
|
||||||
|
});
|
||||||
|
} else if (grantType === "implicit") {
|
||||||
|
const authorizationUrl = requiredString(values, "authorizationUrl");
|
||||||
|
token = await getImplicit(ctx, contextId, {
|
||||||
|
authorizationUrl: authorizationUrl.match(/^https?:\/\//) ? authorizationUrl : `https://${authorizationUrl}`,
|
||||||
|
clientId: requiredString(values, "clientId"),
|
||||||
|
redirectUri: optionalString(values, "redirectUri"),
|
||||||
|
responseType: requiredString(values, "responseType"),
|
||||||
|
scope: optionalString(values, "scope"),
|
||||||
|
state: optionalString(values, "state")
|
||||||
|
});
|
||||||
|
} else if (grantType === "client_credentials") {
|
||||||
|
const accessTokenUrl = requiredString(values, "accessTokenUrl");
|
||||||
|
token = await getClientCredentials(ctx, contextId, {
|
||||||
|
accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`,
|
||||||
|
clientId: requiredString(values, "clientId"),
|
||||||
|
clientSecret: requiredString(values, "clientSecret"),
|
||||||
|
scope: optionalString(values, "scope"),
|
||||||
|
credentialsInBody
|
||||||
|
});
|
||||||
|
} else if (grantType === "password") {
|
||||||
|
const accessTokenUrl = requiredString(values, "accessTokenUrl");
|
||||||
|
token = await getPassword(ctx, contextId, {
|
||||||
|
accessTokenUrl: accessTokenUrl.match(/^https?:\/\//) ? accessTokenUrl : `https://${accessTokenUrl}`,
|
||||||
|
clientId: requiredString(values, "clientId"),
|
||||||
|
clientSecret: requiredString(values, "clientSecret"),
|
||||||
|
username: requiredString(values, "username"),
|
||||||
|
password: requiredString(values, "password"),
|
||||||
|
scope: optionalString(values, "scope"),
|
||||||
|
credentialsInBody
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
throw new Error("Invalid grant type " + grantType);
|
||||||
|
}
|
||||||
|
const headerValue = `${headerPrefix} ${token.response.access_token}`.trim();
|
||||||
|
return {
|
||||||
|
setHeaders: [{
|
||||||
|
name: "Authorization",
|
||||||
|
value: headerValue
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function optionalString(values, name) {
|
||||||
|
const arg = values[name];
|
||||||
|
if (arg == null || arg == "") return null;
|
||||||
|
return `${arg}`;
|
||||||
|
}
|
||||||
|
function requiredString(values, name) {
|
||||||
|
const arg = optionalString(values, name);
|
||||||
|
if (!arg) throw new Error(`Missing required argument ${name}`);
|
||||||
|
return arg;
|
||||||
|
}
|
||||||
|
// Annotate the CommonJS export names for ESM import in node:
|
||||||
|
0 && (module.exports = {
|
||||||
|
plugin
|
||||||
|
});
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"name": "@yaakapp/auth-oauth2",
|
||||||
|
"private": true,
|
||||||
|
"version": "0.0.1",
|
||||||
|
"scripts": {
|
||||||
|
"build": "yaakcli build ./src/index.ts",
|
||||||
|
"dev": "yaakcli dev ./src/index.js"
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
-3
@@ -27,14 +27,14 @@ module.exports = __toCommonJS(src_exports);
|
|||||||
var NEWLINE = "\\\n ";
|
var NEWLINE = "\\\n ";
|
||||||
var plugin = {
|
var plugin = {
|
||||||
httpRequestActions: [{
|
httpRequestActions: [{
|
||||||
key: "export-curl",
|
name: "export-curl",
|
||||||
label: "Copy as Curl",
|
label: "Copy as Curl",
|
||||||
icon: "copy",
|
icon: "copy",
|
||||||
async onSelect(ctx, args) {
|
async onSelect(ctx, args) {
|
||||||
const rendered_request = await ctx.httpRequest.render({ httpRequest: args.httpRequest, purpose: "preview" });
|
const rendered_request = await ctx.httpRequest.render({ httpRequest: args.httpRequest, purpose: "preview" });
|
||||||
const data = await convertToCurl(rendered_request);
|
const data = await convertToCurl(rendered_request);
|
||||||
ctx.clipboard.copyText(data);
|
await ctx.clipboard.copyText(data);
|
||||||
ctx.toast.show({ message: "Curl copied to clipboard", icon: "copy" });
|
await ctx.toast.show({ message: "Curl copied to clipboard", icon: "copy", color: "success" });
|
||||||
}
|
}
|
||||||
}]
|
}]
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8824,8 +8824,8 @@ var behaviorArg = {
|
|||||||
label: "Sending Behavior",
|
label: "Sending Behavior",
|
||||||
defaultValue: "smart",
|
defaultValue: "smart",
|
||||||
options: [
|
options: [
|
||||||
{ name: "When no responses", value: "smart" },
|
{ label: "When no responses", value: "smart" },
|
||||||
{ name: "Always", value: "always" }
|
{ label: "Always", value: "always" }
|
||||||
]
|
]
|
||||||
};
|
};
|
||||||
var requestArg = {
|
var requestArg = {
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
|
||||||
|
export type CheckActivationRequestPayload = { activationId: string, };
|
||||||
@@ -15,7 +15,7 @@ const TRIAL_SECONDS: u64 = 3600 * 24 * 14;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct CheckActivationRequestPayload {
|
pub struct CheckActivationRequestPayload {
|
||||||
pub activation_id: String,
|
pub activation_id: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ sea-query = { version = "0.32.1", features = ["with-chrono", "attr"] }
|
|||||||
sea-query-rusqlite = { version = "0.7.0", features = ["with-chrono"] }
|
sea-query-rusqlite = { version = "0.7.0", features = ["with-chrono"] }
|
||||||
serde = { version = "1.0.204", features = ["derive"] }
|
serde = { version = "1.0.204", features = ["derive"] }
|
||||||
serde_json = "1.0.122"
|
serde_json = "1.0.122"
|
||||||
sqlx = { version = "0.8.0", features = ["sqlite", "runtime-tokio-rustls"] }
|
sqlx = { version = "0.8.0", default-features = false, features = ["migrate", "sqlite", "runtime-tokio-rustls"] }
|
||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
thiserror = "1.0.63"
|
thiserror = "2.0.11"
|
||||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
export * from './bindings/models';
|
export * from './bindings/gen_models';
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use ts_rs::TS;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase", tag = "type")]
|
#[serde(rename_all = "camelCase", tag = "type")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum ProxySetting {
|
pub enum ProxySetting {
|
||||||
Enabled {
|
Enabled {
|
||||||
http: String,
|
http: String,
|
||||||
@@ -22,7 +22,7 @@ pub enum ProxySetting {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct ProxySettingAuth {
|
pub struct ProxySettingAuth {
|
||||||
pub user: String,
|
pub user: String,
|
||||||
pub password: String,
|
pub password: String,
|
||||||
@@ -30,7 +30,7 @@ pub struct ProxySettingAuth {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum EditorKeymap {
|
pub enum EditorKeymap {
|
||||||
Default,
|
Default,
|
||||||
Vim,
|
Vim,
|
||||||
@@ -72,7 +72,7 @@ impl Default for EditorKeymap {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct Settings {
|
pub struct Settings {
|
||||||
#[ts(type = "\"settings\"")]
|
#[ts(type = "\"settings\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -149,7 +149,7 @@ impl<'s> TryFrom<&Row<'s>> for Settings {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct Workspace {
|
pub struct Workspace {
|
||||||
#[ts(type = "\"workspace\"")]
|
#[ts(type = "\"workspace\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -215,7 +215,7 @@ impl Workspace {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct WorkspaceMeta {
|
pub struct WorkspaceMeta {
|
||||||
#[ts(type = "\"workspace_meta\"")]
|
#[ts(type = "\"workspace_meta\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -255,7 +255,7 @@ impl<'s> TryFrom<&Row<'s>> for WorkspaceMeta {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
enum CookieDomain {
|
enum CookieDomain {
|
||||||
HostOnly(String),
|
HostOnly(String),
|
||||||
Suffix(String),
|
Suffix(String),
|
||||||
@@ -264,14 +264,14 @@ enum CookieDomain {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
enum CookieExpires {
|
enum CookieExpires {
|
||||||
AtUtc(String),
|
AtUtc(String),
|
||||||
SessionEnd,
|
SessionEnd,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct Cookie {
|
pub struct Cookie {
|
||||||
raw_cookie: String,
|
raw_cookie: String,
|
||||||
domain: CookieDomain,
|
domain: CookieDomain,
|
||||||
@@ -281,7 +281,7 @@ pub struct Cookie {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct CookieJar {
|
pub struct CookieJar {
|
||||||
#[ts(type = "\"cookie_jar\"")]
|
#[ts(type = "\"cookie_jar\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -327,7 +327,7 @@ impl<'s> TryFrom<&Row<'s>> for CookieJar {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct Environment {
|
pub struct Environment {
|
||||||
#[ts(type = "\"environment\"")]
|
#[ts(type = "\"environment\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -376,7 +376,7 @@ impl<'s> TryFrom<&Row<'s>> for Environment {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct EnvironmentVariable {
|
pub struct EnvironmentVariable {
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
#[ts(optional, as = "Option<bool>")]
|
#[ts(optional, as = "Option<bool>")]
|
||||||
@@ -389,7 +389,7 @@ pub struct EnvironmentVariable {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct Folder {
|
pub struct Folder {
|
||||||
#[ts(type = "\"folder\"")]
|
#[ts(type = "\"folder\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -440,7 +440,7 @@ impl<'s> TryFrom<&Row<'s>> for Folder {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct HttpRequestHeader {
|
pub struct HttpRequestHeader {
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
#[ts(optional, as = "Option<bool>")]
|
#[ts(optional, as = "Option<bool>")]
|
||||||
@@ -453,7 +453,7 @@ pub struct HttpRequestHeader {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct HttpUrlParameter {
|
pub struct HttpUrlParameter {
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
#[ts(optional, as = "Option<bool>")]
|
#[ts(optional, as = "Option<bool>")]
|
||||||
@@ -466,7 +466,7 @@ pub struct HttpUrlParameter {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct HttpRequest {
|
pub struct HttpRequest {
|
||||||
#[ts(type = "\"http_request\"")]
|
#[ts(type = "\"http_request\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -548,7 +548,7 @@ impl<'s> TryFrom<&Row<'s>> for HttpRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct HttpResponseHeader {
|
pub struct HttpResponseHeader {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
@@ -556,7 +556,7 @@ pub struct HttpResponseHeader {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum HttpResponseState {
|
pub enum HttpResponseState {
|
||||||
Initialized,
|
Initialized,
|
||||||
Connected,
|
Connected,
|
||||||
@@ -571,7 +571,7 @@ impl Default for HttpResponseState {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct HttpResponse {
|
pub struct HttpResponse {
|
||||||
#[ts(type = "\"http_response\"")]
|
#[ts(type = "\"http_response\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -660,7 +660,7 @@ impl HttpResponse {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct GrpcMetadataEntry {
|
pub struct GrpcMetadataEntry {
|
||||||
#[serde(default = "default_true")]
|
#[serde(default = "default_true")]
|
||||||
#[ts(optional, as = "Option<bool>")]
|
#[ts(optional, as = "Option<bool>")]
|
||||||
@@ -673,7 +673,7 @@ pub struct GrpcMetadataEntry {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct GrpcRequest {
|
pub struct GrpcRequest {
|
||||||
#[ts(type = "\"grpc_request\"")]
|
#[ts(type = "\"grpc_request\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -748,7 +748,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum GrpcConnectionState {
|
pub enum GrpcConnectionState {
|
||||||
Initialized,
|
Initialized,
|
||||||
Connected,
|
Connected,
|
||||||
@@ -763,7 +763,7 @@ impl Default for GrpcConnectionState {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct GrpcConnection {
|
pub struct GrpcConnection {
|
||||||
#[ts(type = "\"grpc_connection\"")]
|
#[ts(type = "\"grpc_connection\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -831,7 +831,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcConnection {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Eq, PartialEq, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum GrpcEventType {
|
pub enum GrpcEventType {
|
||||||
Info,
|
Info,
|
||||||
Error,
|
Error,
|
||||||
@@ -849,7 +849,7 @@ impl Default for GrpcEventType {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct GrpcEvent {
|
pub struct GrpcEvent {
|
||||||
#[ts(type = "\"grpc_event\"")]
|
#[ts(type = "\"grpc_event\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -911,7 +911,7 @@ impl<'s> TryFrom<&Row<'s>> for GrpcEvent {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct Plugin {
|
pub struct Plugin {
|
||||||
#[ts(type = "\"plugin\"")]
|
#[ts(type = "\"plugin\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -959,7 +959,7 @@ impl<'s> TryFrom<&Row<'s>> for Plugin {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct SyncState {
|
pub struct SyncState {
|
||||||
#[ts(type = "\"sync_state\"")]
|
#[ts(type = "\"sync_state\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -977,7 +977,7 @@ pub struct SyncState {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct SyncHistory {
|
pub struct SyncHistory {
|
||||||
#[ts(type = "\"sync_history\"")]
|
#[ts(type = "\"sync_history\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -1029,7 +1029,7 @@ impl<'s> TryFrom<&Row<'s>> for SyncState {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct KeyValue {
|
pub struct KeyValue {
|
||||||
#[ts(type = "\"key_value\"")]
|
#[ts(type = "\"key_value\"")]
|
||||||
pub model: String,
|
pub model: String,
|
||||||
@@ -1069,6 +1069,48 @@ impl<'s> TryFrom<&Row<'s>> for KeyValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, Default, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
|
pub struct PluginKeyValue {
|
||||||
|
#[ts(type = "\"plugin_key_value\"")]
|
||||||
|
pub model: String,
|
||||||
|
pub created_at: NaiveDateTime,
|
||||||
|
pub updated_at: NaiveDateTime,
|
||||||
|
|
||||||
|
pub plugin_name: String,
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Iden)]
|
||||||
|
pub enum PluginKeyValueIden {
|
||||||
|
#[iden = "plugin_key_values"]
|
||||||
|
Table,
|
||||||
|
Model,
|
||||||
|
CreatedAt,
|
||||||
|
UpdatedAt,
|
||||||
|
|
||||||
|
PluginName,
|
||||||
|
Key,
|
||||||
|
Value,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'s> TryFrom<&Row<'s>> for PluginKeyValue {
|
||||||
|
type Error = rusqlite::Error;
|
||||||
|
|
||||||
|
fn try_from(r: &Row<'s>) -> Result<Self, Self::Error> {
|
||||||
|
Ok(PluginKeyValue {
|
||||||
|
model: r.get("model")?,
|
||||||
|
created_at: r.get("created_at")?,
|
||||||
|
updated_at: r.get("updated_at")?,
|
||||||
|
plugin_name: r.get("plugin_name")?,
|
||||||
|
key: r.get("key")?,
|
||||||
|
value: r.get("value")?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn default_true() -> bool {
|
fn default_true() -> bool {
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
@@ -1114,7 +1156,7 @@ impl ModelType {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, TS)]
|
#[derive(Debug, Clone, Serialize, TS)]
|
||||||
#[serde(rename_all = "camelCase", untagged)]
|
#[serde(rename_all = "camelCase", untagged)]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum AnyModel {
|
pub enum AnyModel {
|
||||||
CookieJar(CookieJar),
|
CookieJar(CookieJar),
|
||||||
Environment(Environment),
|
Environment(Environment),
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
use crate::error::Error::ModelNotFound;
|
use crate::error::Error::ModelNotFound;
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::models::{
|
use crate::models::{AnyModel, CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden, GrpcConnection, GrpcConnectionIden, GrpcConnectionState, GrpcEvent, GrpcEventIden, GrpcRequest, GrpcRequestIden, HttpRequest, HttpRequestIden, HttpResponse, HttpResponseHeader, HttpResponseIden, HttpResponseState, KeyValue, KeyValueIden, ModelType, Plugin, PluginIden, PluginKeyValue, PluginKeyValueIden, Settings, SettingsIden, SyncState, SyncStateIden, Workspace, WorkspaceIden, WorkspaceMeta, WorkspaceMetaIden};
|
||||||
AnyModel, CookieJar, CookieJarIden, Environment, EnvironmentIden, Folder, FolderIden,
|
|
||||||
GrpcConnection, GrpcConnectionIden, GrpcConnectionState, GrpcEvent, GrpcEventIden, GrpcRequest,
|
|
||||||
GrpcRequestIden, HttpRequest, HttpRequestIden, HttpResponse, HttpResponseHeader,
|
|
||||||
HttpResponseIden, HttpResponseState, KeyValue, KeyValueIden, ModelType, Plugin, PluginIden,
|
|
||||||
Settings, SettingsIden, SyncState, SyncStateIden, Workspace, WorkspaceIden, WorkspaceMeta,
|
|
||||||
WorkspaceMetaIden,
|
|
||||||
};
|
|
||||||
use crate::plugin::SqliteConnection;
|
use crate::plugin::SqliteConnection;
|
||||||
use chrono::{NaiveDateTime, Utc};
|
use chrono::{NaiveDateTime, Utc};
|
||||||
use log::{debug, error, info, warn};
|
use log::{debug, error, info, warn};
|
||||||
@@ -165,6 +158,90 @@ pub async fn get_key_value_raw<R: Runtime>(
|
|||||||
db.query_row(sql.as_str(), &*params.as_params(), |row| row.try_into()).ok()
|
db.query_row(sql.as_str(), &*params.as_params(), |row| row.try_into()).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_plugin_key_value<R: Runtime>(
|
||||||
|
mgr: &impl Manager<R>,
|
||||||
|
plugin_name: &str,
|
||||||
|
key: &str,
|
||||||
|
) -> Option<PluginKeyValue> {
|
||||||
|
let dbm = &*mgr.state::<SqliteConnection>();
|
||||||
|
let db = dbm.0.lock().await.get().unwrap();
|
||||||
|
let (sql, params) = Query::select()
|
||||||
|
.from(PluginKeyValueIden::Table)
|
||||||
|
.column(Asterisk)
|
||||||
|
.cond_where(
|
||||||
|
Cond::all()
|
||||||
|
.add(Expr::col(PluginKeyValueIden::PluginName).eq(plugin_name))
|
||||||
|
.add(Expr::col(PluginKeyValueIden::Key).eq(key)),
|
||||||
|
)
|
||||||
|
.build_rusqlite(SqliteQueryBuilder);
|
||||||
|
|
||||||
|
db.query_row(sql.as_str(), &*params.as_params(), |row| row.try_into()).ok()
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn set_plugin_key_value<R: Runtime>(
|
||||||
|
mgr: &impl Manager<R>,
|
||||||
|
plugin_name: &str,
|
||||||
|
key: &str,
|
||||||
|
value: &str,
|
||||||
|
) -> (PluginKeyValue, bool) {
|
||||||
|
let existing = get_plugin_key_value(mgr, plugin_name, key).await;
|
||||||
|
|
||||||
|
let dbm = &*mgr.state::<SqliteConnection>();
|
||||||
|
let db = dbm.0.lock().await.get().unwrap();
|
||||||
|
let (sql, params) = Query::insert()
|
||||||
|
.into_table(PluginKeyValueIden::Table)
|
||||||
|
.columns([
|
||||||
|
PluginKeyValueIden::CreatedAt,
|
||||||
|
PluginKeyValueIden::UpdatedAt,
|
||||||
|
PluginKeyValueIden::PluginName,
|
||||||
|
PluginKeyValueIden::Key,
|
||||||
|
PluginKeyValueIden::Value,
|
||||||
|
])
|
||||||
|
.values_panic([
|
||||||
|
CurrentTimestamp.into(),
|
||||||
|
CurrentTimestamp.into(),
|
||||||
|
plugin_name.into(),
|
||||||
|
key.into(),
|
||||||
|
value.into(),
|
||||||
|
])
|
||||||
|
.on_conflict(
|
||||||
|
OnConflict::new()
|
||||||
|
.update_columns([PluginKeyValueIden::UpdatedAt, PluginKeyValueIden::Value])
|
||||||
|
.to_owned(),
|
||||||
|
)
|
||||||
|
.returning_all()
|
||||||
|
.build_rusqlite(SqliteQueryBuilder);
|
||||||
|
|
||||||
|
let mut stmt = db.prepare(sql.as_str()).expect("Failed to prepare PluginKeyValue upsert");
|
||||||
|
let m: PluginKeyValue = stmt
|
||||||
|
.query_row(&*params.as_params(), |row| row.try_into())
|
||||||
|
.expect("Failed to upsert KeyValue");
|
||||||
|
(m, existing.is_none())
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn delete_plugin_key_value<R: Runtime>(
|
||||||
|
mgr: &impl Manager<R>,
|
||||||
|
plugin_name: &str,
|
||||||
|
key: &str,
|
||||||
|
) -> bool {
|
||||||
|
if let None = get_plugin_key_value(mgr, plugin_name, key).await {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
let dbm = &*mgr.state::<SqliteConnection>();
|
||||||
|
let db = dbm.0.lock().await.get().unwrap();
|
||||||
|
let (sql, params) = Query::delete()
|
||||||
|
.from_table(PluginKeyValueIden::Table)
|
||||||
|
.cond_where(
|
||||||
|
Cond::all()
|
||||||
|
.add(Expr::col(PluginKeyValueIden::PluginName).eq(plugin_name))
|
||||||
|
.add(Expr::col(PluginKeyValueIden::Key).eq(key)),
|
||||||
|
)
|
||||||
|
.build_rusqlite(SqliteQueryBuilder);
|
||||||
|
db.execute(sql.as_str(), &*params.as_params()).expect("Failed to delete PluginKeyValue");
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn list_workspaces<R: Runtime>(mgr: &impl Manager<R>) -> Result<Vec<Workspace>> {
|
pub async fn list_workspaces<R: Runtime>(mgr: &impl Manager<R>) -> Result<Vec<Workspace>> {
|
||||||
let dbm = &*mgr.state::<SqliteConnection>();
|
let dbm = &*mgr.state::<SqliteConnection>();
|
||||||
let db = dbm.0.lock().await.get().unwrap();
|
let db = dbm.0.lock().await.get().unwrap();
|
||||||
@@ -1999,7 +2076,7 @@ pub fn generate_id() -> String {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub struct ModelPayload {
|
pub struct ModelPayload {
|
||||||
pub model: AnyModel,
|
pub model: AnyModel,
|
||||||
pub window_label: String,
|
pub window_label: String,
|
||||||
@@ -2008,7 +2085,7 @@ pub struct ModelPayload {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum UpdateSource {
|
pub enum UpdateSource {
|
||||||
Sync,
|
Sync,
|
||||||
Window,
|
Window,
|
||||||
|
|||||||
@@ -6,17 +6,18 @@ publish = false
|
|||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
dunce = "1.0.4"
|
dunce = "1.0.4"
|
||||||
|
futures-util = "0.3.30"
|
||||||
log = "0.4.21"
|
log = "0.4.21"
|
||||||
|
md5 = "0.7.0"
|
||||||
|
path-slash = "0.2.1"
|
||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
|
regex = "1.10.6"
|
||||||
serde = { version = "1.0.198", features = ["derive"] }
|
serde = { version = "1.0.198", features = ["derive"] }
|
||||||
serde_json = "1.0.113"
|
serde_json = "1.0.113"
|
||||||
tauri = { workspace = true }
|
tauri = { workspace = true }
|
||||||
tauri-plugin-shell = { workspace = true }
|
tauri-plugin-shell = { workspace = true }
|
||||||
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "process"] }
|
|
||||||
ts-rs = { workspace = true, features = ["import-esm"] }
|
|
||||||
thiserror = "2.0.7"
|
thiserror = "2.0.7"
|
||||||
yaak-models = { workspace = true }
|
tokio = { version = "1.42.0", features = ["macros", "rt-multi-thread", "process"] }
|
||||||
regex = "1.10.6"
|
|
||||||
path-slash = "0.2.1"
|
|
||||||
tokio-tungstenite = "0.26.1"
|
tokio-tungstenite = "0.26.1"
|
||||||
futures-util = "0.3.30"
|
ts-rs = { workspace = true, features = ["import-esm"] }
|
||||||
|
yaak-models = { workspace = true }
|
||||||
|
|||||||
@@ -1,290 +0,0 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
|
||||||
import type { Environment } from "./models.js";
|
|
||||||
import type { Folder } from "./models.js";
|
|
||||||
import type { GrpcRequest } from "./models.js";
|
|
||||||
import type { HttpRequest } from "./models.js";
|
|
||||||
import type { HttpResponse } from "./models.js";
|
|
||||||
import type { JsonValue } from "./serde_json/JsonValue.js";
|
|
||||||
import type { Workspace } from "./models.js";
|
|
||||||
|
|
||||||
export type BootRequest = { dir: string, watch: boolean, };
|
|
||||||
|
|
||||||
export type BootResponse = { name: string, version: string, };
|
|
||||||
|
|
||||||
export type CallHttpAuthenticationRequest = { config: { [key in string]?: JsonValue }, method: string, url: string, headers: Array<HttpHeader>, };
|
|
||||||
|
|
||||||
export type CallHttpAuthenticationResponse = {
|
|
||||||
/**
|
|
||||||
* HTTP headers to add to the request. Existing headers will be replaced, while
|
|
||||||
* new headers will be added.
|
|
||||||
*/
|
|
||||||
setHeaders: Array<HttpHeader>, };
|
|
||||||
|
|
||||||
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type CallHttpRequestActionRequest = { key: string, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
|
||||||
|
|
||||||
export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, };
|
|
||||||
|
|
||||||
export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, };
|
|
||||||
|
|
||||||
export type CallTemplateFunctionResponse = { value: string | null, };
|
|
||||||
|
|
||||||
export type Color = "custom" | "default" | "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
|
||||||
|
|
||||||
export type CopyTextRequest = { text: string, };
|
|
||||||
|
|
||||||
export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown";
|
|
||||||
|
|
||||||
export type EmptyPayload = {};
|
|
||||||
|
|
||||||
export type ErrorResponse = { error: string, };
|
|
||||||
|
|
||||||
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type ExportHttpRequestResponse = { content: string, };
|
|
||||||
|
|
||||||
export type FileFilter = { name: string,
|
|
||||||
/**
|
|
||||||
* File extensions to require
|
|
||||||
*/
|
|
||||||
extensions: Array<string>, };
|
|
||||||
|
|
||||||
export type FilterRequest = { content: string, filter: string, };
|
|
||||||
|
|
||||||
export type FilterResponse = { content: string, };
|
|
||||||
|
|
||||||
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
|
||||||
|
|
||||||
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
|
||||||
|
|
||||||
export type FormInput = { "type": "text" } & FormInputText | { "type": "editor" } & FormInputEditor | { "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,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* 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,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type FormInputEditor = {
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
placeholder?: string | null,
|
|
||||||
/**
|
|
||||||
* Don't show the editor gutter (line numbers, folds, etc.)
|
|
||||||
*/
|
|
||||||
hideGutter?: boolean,
|
|
||||||
/**
|
|
||||||
* Language for syntax highlighting
|
|
||||||
*/
|
|
||||||
language?: EditorLanguage, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* 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<FileFilter>, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* 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,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type FormInputSelect = {
|
|
||||||
/**
|
|
||||||
* The options that will be available in the select input
|
|
||||||
*/
|
|
||||||
options: Array<FormInputSelectOption>, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type FormInputSelectOption = { name: string, value: string, };
|
|
||||||
|
|
||||||
export type FormInputText = {
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
placeholder?: string | null,
|
|
||||||
/**
|
|
||||||
* Placeholder for the text input
|
|
||||||
*/
|
|
||||||
password?: boolean, name: string,
|
|
||||||
/**
|
|
||||||
* Whether the user must fill in the argument
|
|
||||||
*/
|
|
||||||
optional?: boolean,
|
|
||||||
/**
|
|
||||||
* The label of the input
|
|
||||||
*/
|
|
||||||
label?: string,
|
|
||||||
/**
|
|
||||||
* Visually hide the label of the input
|
|
||||||
*/
|
|
||||||
hideLabel?: boolean,
|
|
||||||
/**
|
|
||||||
* The default value
|
|
||||||
*/
|
|
||||||
defaultValue?: string, };
|
|
||||||
|
|
||||||
export type GetHttpAuthenticationResponse = { name: string, label: string, shortLabel: string, config: Array<FormInput>, };
|
|
||||||
|
|
||||||
export type GetHttpRequestActionsRequest = Record<string, never>;
|
|
||||||
|
|
||||||
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
|
||||||
|
|
||||||
export type GetHttpRequestByIdRequest = { id: string, };
|
|
||||||
|
|
||||||
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
|
||||||
|
|
||||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, 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";
|
|
||||||
|
|
||||||
export type ImportRequest = { content: string, };
|
|
||||||
|
|
||||||
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
|
||||||
|
|
||||||
export type ImportResponse = { resources: ImportResources, };
|
|
||||||
|
|
||||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: WindowContext, payload: InternalEventPayload, };
|
|
||||||
|
|
||||||
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 | { "type": "error_response" } & ErrorResponse;
|
|
||||||
|
|
||||||
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
|
|
||||||
*/
|
|
||||||
required?: boolean, };
|
|
||||||
|
|
||||||
export type PromptTextResponse = { value: string | null, };
|
|
||||||
|
|
||||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
|
||||||
|
|
||||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type RenderPurpose = "send" | "preview";
|
|
||||||
|
|
||||||
export type SendHttpRequestRequest = { httpRequest: HttpRequest, };
|
|
||||||
|
|
||||||
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
|
||||||
|
|
||||||
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<string>, args: Array<FormInput>, };
|
|
||||||
|
|
||||||
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
|
||||||
|
|
||||||
export type TemplateRenderResponse = { data: JsonValue, };
|
|
||||||
|
|
||||||
export type WindowContext = { "type": "none" } | { "type": "label", label: string, };
|
|
||||||
@@ -0,0 +1,405 @@
|
|||||||
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
|
import type { Environment } from "./gen_models.js";
|
||||||
|
import type { Folder } from "./gen_models.js";
|
||||||
|
import type { GrpcRequest } from "./gen_models.js";
|
||||||
|
import type { HttpRequest } from "./gen_models.js";
|
||||||
|
import type { HttpResponse } from "./gen_models.js";
|
||||||
|
import type { JsonValue } from "./serde_json/JsonValue.js";
|
||||||
|
import type { Workspace } from "./gen_models.js";
|
||||||
|
|
||||||
|
export type BootRequest = { dir: string, watch: boolean, };
|
||||||
|
|
||||||
|
export type BootResponse = { name: string, version: string, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationActionArgs = { contextId: string, values: { [key in string]?: JsonPrimitive }, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationActionRequest = { index: number, pluginRefId: string, args: CallHttpAuthenticationActionArgs, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationRequest = { contextId: string, values: { [key in string]?: JsonPrimitive }, method: string, url: string, headers: Array<HttpHeader>, };
|
||||||
|
|
||||||
|
export type CallHttpAuthenticationResponse = {
|
||||||
|
/**
|
||||||
|
* HTTP headers to add to the request. Existing headers will be replaced, while
|
||||||
|
* new headers will be added.
|
||||||
|
*/
|
||||||
|
setHeaders: Array<HttpHeader>, };
|
||||||
|
|
||||||
|
export type CallHttpRequestActionArgs = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
|
export type CallHttpRequestActionRequest = { index: number, pluginRefId: string, args: CallHttpRequestActionArgs, };
|
||||||
|
|
||||||
|
export type CallTemplateFunctionArgs = { purpose: RenderPurpose, values: { [key in string]?: string }, };
|
||||||
|
|
||||||
|
export type CallTemplateFunctionRequest = { name: string, args: CallTemplateFunctionArgs, };
|
||||||
|
|
||||||
|
export type CallTemplateFunctionResponse = { value: string | null, };
|
||||||
|
|
||||||
|
export type CloseWindowRequest = { label: string, };
|
||||||
|
|
||||||
|
export type Color = "primary" | "secondary" | "info" | "success" | "notice" | "warning" | "danger";
|
||||||
|
|
||||||
|
export type CompletionOptionType = "constant" | "variable";
|
||||||
|
|
||||||
|
export type Content = { "type": "text", content: string, } | { "type": "markdown", content: string, };
|
||||||
|
|
||||||
|
export type CopyTextRequest = { text: string, };
|
||||||
|
|
||||||
|
export type DeleteKeyValueRequest = { key: string, };
|
||||||
|
|
||||||
|
export type DeleteKeyValueResponse = { deleted: boolean, };
|
||||||
|
|
||||||
|
export type EditorLanguage = "text" | "javascript" | "json" | "html" | "xml" | "graphql" | "markdown";
|
||||||
|
|
||||||
|
export type EmptyPayload = {};
|
||||||
|
|
||||||
|
export type ErrorResponse = { error: string, };
|
||||||
|
|
||||||
|
export type ExportHttpRequestRequest = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
|
export type ExportHttpRequestResponse = { content: string, };
|
||||||
|
|
||||||
|
export type FileFilter = { name: string,
|
||||||
|
/**
|
||||||
|
* File extensions to require
|
||||||
|
*/
|
||||||
|
extensions: Array<string>, };
|
||||||
|
|
||||||
|
export type FilterRequest = { content: string, filter: string, };
|
||||||
|
|
||||||
|
export type FilterResponse = { content: string, };
|
||||||
|
|
||||||
|
export type FindHttpResponsesRequest = { requestId: string, limit?: number, };
|
||||||
|
|
||||||
|
export type FindHttpResponsesResponse = { httpResponses: Array<HttpResponse>, };
|
||||||
|
|
||||||
|
export type FormInput = { "type": "text" } & FormInputText | { "type": "editor" } & FormInputEditor | { "type": "select" } & FormInputSelect | { "type": "checkbox" } & FormInputCheckbox | { "type": "file" } & FormInputFile | { "type": "http_request" } & FormInputHttpRequest | { "type": "accordion" } & FormInputAccordion | { "type": "banner" } & FormInputBanner | { "type": "markdown" } & FormInputMarkdown;
|
||||||
|
|
||||||
|
export type FormInputAccordion = { label: string, inputs?: Array<FormInput>, hidden?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputBanner = { inputs?: Array<FormInput>, hidden?: boolean, color?: Color, };
|
||||||
|
|
||||||
|
export type FormInputBase = {
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputCheckbox = {
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputEditor = {
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
placeholder?: string | null,
|
||||||
|
/**
|
||||||
|
* Don't show the editor gutter (line numbers, folds, etc.)
|
||||||
|
*/
|
||||||
|
hideGutter?: boolean,
|
||||||
|
/**
|
||||||
|
* Language for syntax highlighting
|
||||||
|
*/
|
||||||
|
language?: EditorLanguage, readOnly?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputFile = {
|
||||||
|
/**
|
||||||
|
* The title of the file selection window
|
||||||
|
*/
|
||||||
|
title: string,
|
||||||
|
/**
|
||||||
|
* Allow selecting multiple files
|
||||||
|
*/
|
||||||
|
multiple?: boolean, directory?: boolean, defaultPath?: string, filters?: Array<FileFilter>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputHttpRequest = {
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputMarkdown = { content: string, hidden?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputSelect = {
|
||||||
|
/**
|
||||||
|
* The options that will be available in the select input
|
||||||
|
*/
|
||||||
|
options: Array<FormInputSelectOption>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type FormInputSelectOption = { label: string, value: string, };
|
||||||
|
|
||||||
|
export type FormInputText = {
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
placeholder?: string | null,
|
||||||
|
/**
|
||||||
|
* Placeholder for the text input
|
||||||
|
*/
|
||||||
|
password?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether to allow newlines in the input, like a <textarea/>
|
||||||
|
*/
|
||||||
|
multiLine?: boolean, completionOptions?: Array<GenericCompletionOption>,
|
||||||
|
/**
|
||||||
|
* The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
|
*/
|
||||||
|
name: string,
|
||||||
|
/**
|
||||||
|
* Whether this input is visible for the given configuration. Use this to
|
||||||
|
* make branching forms.
|
||||||
|
*/
|
||||||
|
hidden?: boolean,
|
||||||
|
/**
|
||||||
|
* Whether the user must fill in the argument
|
||||||
|
*/
|
||||||
|
optional?: boolean,
|
||||||
|
/**
|
||||||
|
* The label of the input
|
||||||
|
*/
|
||||||
|
label?: string,
|
||||||
|
/**
|
||||||
|
* Visually hide the label of the input
|
||||||
|
*/
|
||||||
|
hideLabel?: boolean,
|
||||||
|
/**
|
||||||
|
* The default value
|
||||||
|
*/
|
||||||
|
defaultValue?: string, disabled?: boolean, };
|
||||||
|
|
||||||
|
export type GenericCompletionOption = { label: string, detail?: string, info?: string, type?: CompletionOptionType, boost?: number, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationConfigRequest = { contextId: string, values: { [key in string]?: JsonPrimitive }, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationConfigResponse = { args: Array<FormInput>, pluginRefId: string, actions?: Array<HttpAuthenticationAction>, };
|
||||||
|
|
||||||
|
export type GetHttpAuthenticationSummaryResponse = { name: string, label: string, shortLabel: string, };
|
||||||
|
|
||||||
|
export type GetHttpRequestActionsRequest = Record<string, never>;
|
||||||
|
|
||||||
|
export type GetHttpRequestActionsResponse = { actions: Array<HttpRequestAction>, pluginRefId: string, };
|
||||||
|
|
||||||
|
export type GetHttpRequestByIdRequest = { id: string, };
|
||||||
|
|
||||||
|
export type GetHttpRequestByIdResponse = { httpRequest: HttpRequest | null, };
|
||||||
|
|
||||||
|
export type GetKeyValueRequest = { key: string, };
|
||||||
|
|
||||||
|
export type GetKeyValueResponse = { value?: string, };
|
||||||
|
|
||||||
|
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||||
|
|
||||||
|
export type HttpAuthenticationAction = { label: string, icon?: Icon, };
|
||||||
|
|
||||||
|
export type HttpHeader = { name: string, value: string, };
|
||||||
|
|
||||||
|
export type HttpRequestAction = { label: string, icon?: Icon, };
|
||||||
|
|
||||||
|
export type Icon = "alert_triangle" | "check" | "check_circle" | "chevron_down" | "copy" | "info" | "pin" | "search" | "trash" | "_unknown";
|
||||||
|
|
||||||
|
export type ImportRequest = { content: string, };
|
||||||
|
|
||||||
|
export type ImportResources = { workspaces: Array<Workspace>, environments: Array<Environment>, folders: Array<Folder>, httpRequests: Array<HttpRequest>, grpcRequests: Array<GrpcRequest>, };
|
||||||
|
|
||||||
|
export type ImportResponse = { resources: ImportResources, };
|
||||||
|
|
||||||
|
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: WindowContext, payload: InternalEventPayload, };
|
||||||
|
|
||||||
|
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_summary_request" } & EmptyPayload | { "type": "get_http_authentication_summary_response" } & GetHttpAuthenticationSummaryResponse | { "type": "get_http_authentication_config_request" } & GetHttpAuthenticationConfigRequest | { "type": "get_http_authentication_config_response" } & GetHttpAuthenticationConfigResponse | { "type": "call_http_authentication_request" } & CallHttpAuthenticationRequest | { "type": "call_http_authentication_response" } & CallHttpAuthenticationResponse | { "type": "call_http_authentication_action_request" } & CallHttpAuthenticationActionRequest | { "type": "call_http_authentication_action_response" } & EmptyPayload | { "type": "copy_text_request" } & CopyTextRequest | { "type": "copy_text_response" } & EmptyPayload | { "type": "render_http_request_request" } & RenderHttpRequestRequest | { "type": "render_http_request_response" } & RenderHttpRequestResponse | { "type": "get_key_value_request" } & GetKeyValueRequest | { "type": "get_key_value_response" } & GetKeyValueResponse | { "type": "set_key_value_request" } & SetKeyValueRequest | { "type": "set_key_value_response" } & SetKeyValueResponse | { "type": "delete_key_value_request" } & DeleteKeyValueRequest | { "type": "delete_key_value_response" } & DeleteKeyValueResponse | { "type": "open_window_request" } & OpenWindowRequest | { "type": "window_navigate_event" } & WindowNavigateEvent | { "type": "close_window_request" } & CloseWindowRequest | { "type": "template_render_request" } & TemplateRenderRequest | { "type": "template_render_response" } & TemplateRenderResponse | { "type": "show_toast_request" } & ShowToastRequest | { "type": "show_toast_response" } & EmptyPayload | { "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 | { "type": "error_response" } & ErrorResponse;
|
||||||
|
|
||||||
|
export type JsonPrimitive = string | number | boolean | null;
|
||||||
|
|
||||||
|
export type OpenWindowRequest = { url: string,
|
||||||
|
/**
|
||||||
|
* Label for the window. If not provided, a random one will be generated.
|
||||||
|
*/
|
||||||
|
label: string, title?: string, size?: WindowSize, };
|
||||||
|
|
||||||
|
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
|
||||||
|
*/
|
||||||
|
required?: boolean, };
|
||||||
|
|
||||||
|
export type PromptTextResponse = { value: string | null, };
|
||||||
|
|
||||||
|
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||||
|
|
||||||
|
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||||
|
|
||||||
|
export type RenderPurpose = "send" | "preview";
|
||||||
|
|
||||||
|
export type SendHttpRequestRequest = { httpRequest: Partial<HttpRequest>, };
|
||||||
|
|
||||||
|
export type SendHttpRequestResponse = { httpResponse: HttpResponse, };
|
||||||
|
|
||||||
|
export type SetKeyValueRequest = { key: string, value: string, };
|
||||||
|
|
||||||
|
export type SetKeyValueResponse = {};
|
||||||
|
|
||||||
|
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<string>, args: Array<FormInput>, };
|
||||||
|
|
||||||
|
export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, };
|
||||||
|
|
||||||
|
export type TemplateRenderResponse = { data: JsonValue, };
|
||||||
|
|
||||||
|
export type WindowContext = { "type": "none" } | { "type": "label", label: string, };
|
||||||
|
|
||||||
|
export type WindowNavigateEvent = { url: string, };
|
||||||
|
|
||||||
|
export type WindowSize = { width: number, height: number, };
|
||||||
@@ -1,2 +1,2 @@
|
|||||||
export * from './bindings/models';
|
export * from './bindings/gen_models';
|
||||||
export * from './bindings/events';
|
export * from './bindings/gen_events';
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ pub enum Error {
|
|||||||
#[error("JSON error: {0}")]
|
#[error("JSON error: {0}")]
|
||||||
JsonErr(#[from] serde_json::Error),
|
JsonErr(#[from] serde_json::Error),
|
||||||
|
|
||||||
|
#[error("Timeout elapsed: {0}")]
|
||||||
|
TimeoutElapsed(#[from] tokio::time::error::Elapsed),
|
||||||
|
|
||||||
#[error("Plugin not found: {0}")]
|
#[error("Plugin not found: {0}")]
|
||||||
PluginNotFoundErr(String),
|
PluginNotFoundErr(String),
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use serde_json::Value;
|
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use tauri::{Runtime, WebviewWindow};
|
use tauri::{Runtime, WebviewWindow};
|
||||||
use ts_rs::TS;
|
use ts_rs::TS;
|
||||||
@@ -8,7 +7,7 @@ use yaak_models::models::{Environment, Folder, GrpcRequest, HttpRequest, HttpRes
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct InternalEvent {
|
pub struct InternalEvent {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
pub plugin_ref_id: String,
|
pub plugin_ref_id: String,
|
||||||
@@ -29,12 +28,12 @@ pub(crate) struct InternalEventRawPayload {
|
|||||||
pub plugin_name: String,
|
pub plugin_name: String,
|
||||||
pub reply_id: Option<String>,
|
pub reply_id: Option<String>,
|
||||||
pub window_context: WindowContext,
|
pub window_context: WindowContext,
|
||||||
pub payload: Value,
|
pub payload: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub enum WindowContext {
|
pub enum WindowContext {
|
||||||
None,
|
None,
|
||||||
Label { label: String },
|
Label { label: String },
|
||||||
@@ -50,7 +49,7 @@ impl WindowContext {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub enum InternalEventPayload {
|
pub enum InternalEventPayload {
|
||||||
BootRequest(BootRequest),
|
BootRequest(BootRequest),
|
||||||
BootResponse(BootResponse),
|
BootResponse(BootResponse),
|
||||||
@@ -84,20 +83,38 @@ pub enum InternalEventPayload {
|
|||||||
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
|
CallTemplateFunctionRequest(CallTemplateFunctionRequest),
|
||||||
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
|
CallTemplateFunctionResponse(CallTemplateFunctionResponse),
|
||||||
|
|
||||||
GetHttpAuthenticationRequest(EmptyPayload),
|
// Http Authentication
|
||||||
GetHttpAuthenticationResponse(GetHttpAuthenticationResponse),
|
GetHttpAuthenticationSummaryRequest(EmptyPayload),
|
||||||
|
GetHttpAuthenticationSummaryResponse(GetHttpAuthenticationSummaryResponse),
|
||||||
|
GetHttpAuthenticationConfigRequest(GetHttpAuthenticationConfigRequest),
|
||||||
|
GetHttpAuthenticationConfigResponse(GetHttpAuthenticationConfigResponse),
|
||||||
CallHttpAuthenticationRequest(CallHttpAuthenticationRequest),
|
CallHttpAuthenticationRequest(CallHttpAuthenticationRequest),
|
||||||
CallHttpAuthenticationResponse(CallHttpAuthenticationResponse),
|
CallHttpAuthenticationResponse(CallHttpAuthenticationResponse),
|
||||||
|
CallHttpAuthenticationActionRequest(CallHttpAuthenticationActionRequest),
|
||||||
|
CallHttpAuthenticationActionResponse(EmptyPayload),
|
||||||
|
|
||||||
CopyTextRequest(CopyTextRequest),
|
CopyTextRequest(CopyTextRequest),
|
||||||
|
CopyTextResponse(EmptyPayload),
|
||||||
|
|
||||||
RenderHttpRequestRequest(RenderHttpRequestRequest),
|
RenderHttpRequestRequest(RenderHttpRequestRequest),
|
||||||
RenderHttpRequestResponse(RenderHttpRequestResponse),
|
RenderHttpRequestResponse(RenderHttpRequestResponse),
|
||||||
|
|
||||||
|
GetKeyValueRequest(GetKeyValueRequest),
|
||||||
|
GetKeyValueResponse(GetKeyValueResponse),
|
||||||
|
SetKeyValueRequest(SetKeyValueRequest),
|
||||||
|
SetKeyValueResponse(SetKeyValueResponse),
|
||||||
|
DeleteKeyValueRequest(DeleteKeyValueRequest),
|
||||||
|
DeleteKeyValueResponse(DeleteKeyValueResponse),
|
||||||
|
|
||||||
|
OpenWindowRequest(OpenWindowRequest),
|
||||||
|
WindowNavigateEvent(WindowNavigateEvent),
|
||||||
|
CloseWindowRequest(CloseWindowRequest),
|
||||||
|
|
||||||
TemplateRenderRequest(TemplateRenderRequest),
|
TemplateRenderRequest(TemplateRenderRequest),
|
||||||
TemplateRenderResponse(TemplateRenderResponse),
|
TemplateRenderResponse(TemplateRenderResponse),
|
||||||
|
|
||||||
ShowToastRequest(ShowToastRequest),
|
ShowToastRequest(ShowToastRequest),
|
||||||
|
ShowToastResponse(EmptyPayload),
|
||||||
|
|
||||||
PromptTextRequest(PromptTextRequest),
|
PromptTextRequest(PromptTextRequest),
|
||||||
PromptTextResponse(PromptTextResponse),
|
PromptTextResponse(PromptTextResponse),
|
||||||
@@ -117,19 +134,19 @@ pub enum InternalEventPayload {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[ts(export, type = "{}", export_to = "events.ts")]
|
#[ts(export, type = "{}", export_to = "gen_events.ts")]
|
||||||
pub struct EmptyPayload {}
|
pub struct EmptyPayload {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct ErrorResponse {
|
pub struct ErrorResponse {
|
||||||
pub error: String,
|
pub error: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct BootRequest {
|
pub struct BootRequest {
|
||||||
pub dir: String,
|
pub dir: String,
|
||||||
pub watch: bool,
|
pub watch: bool,
|
||||||
@@ -137,7 +154,7 @@ pub struct BootRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct BootResponse {
|
pub struct BootResponse {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub version: String,
|
pub version: String,
|
||||||
@@ -145,21 +162,21 @@ pub struct BootResponse {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct ImportRequest {
|
pub struct ImportRequest {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct ImportResponse {
|
pub struct ImportResponse {
|
||||||
pub resources: ImportResources,
|
pub resources: ImportResources,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FilterRequest {
|
pub struct FilterRequest {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
pub filter: String,
|
pub filter: String,
|
||||||
@@ -167,49 +184,50 @@ pub struct FilterRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FilterResponse {
|
pub struct FilterResponse {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct ExportHttpRequestRequest {
|
pub struct ExportHttpRequestRequest {
|
||||||
pub http_request: HttpRequest,
|
pub http_request: HttpRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct ExportHttpRequestResponse {
|
pub struct ExportHttpRequestResponse {
|
||||||
pub content: String,
|
pub content: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct SendHttpRequestRequest {
|
pub struct SendHttpRequestRequest {
|
||||||
|
#[ts(type = "Partial<HttpRequest>")]
|
||||||
pub http_request: HttpRequest,
|
pub http_request: HttpRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct SendHttpRequestResponse {
|
pub struct SendHttpRequestResponse {
|
||||||
pub http_response: HttpResponse,
|
pub http_response: HttpResponse,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CopyTextRequest {
|
pub struct CopyTextRequest {
|
||||||
pub text: String,
|
pub text: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct RenderHttpRequestRequest {
|
pub struct RenderHttpRequestRequest {
|
||||||
pub http_request: HttpRequest,
|
pub http_request: HttpRequest,
|
||||||
pub purpose: RenderPurpose,
|
pub purpose: RenderPurpose,
|
||||||
@@ -217,14 +235,14 @@ pub struct RenderHttpRequestRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct RenderHttpRequestResponse {
|
pub struct RenderHttpRequestResponse {
|
||||||
pub http_request: HttpRequest,
|
pub http_request: HttpRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct TemplateRenderRequest {
|
pub struct TemplateRenderRequest {
|
||||||
pub data: serde_json::Value,
|
pub data: serde_json::Value,
|
||||||
pub purpose: RenderPurpose,
|
pub purpose: RenderPurpose,
|
||||||
@@ -232,25 +250,62 @@ pub struct TemplateRenderRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct TemplateRenderResponse {
|
pub struct TemplateRenderResponse {
|
||||||
pub data: serde_json::Value,
|
pub data: serde_json::Value,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct OpenWindowRequest {
|
||||||
|
pub url: String,
|
||||||
|
/// Label for the window. If not provided, a random one will be generated.
|
||||||
|
pub label: String,
|
||||||
|
#[ts(optional)]
|
||||||
|
pub title: Option<String>,
|
||||||
|
#[ts(optional)]
|
||||||
|
pub size: Option<WindowSize>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct WindowSize {
|
||||||
|
pub width: f64,
|
||||||
|
pub height: f64,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct CloseWindowRequest {
|
||||||
|
pub label: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct WindowNavigateEvent {
|
||||||
|
pub url: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct ShowToastRequest {
|
pub struct ShowToastRequest {
|
||||||
pub message: String,
|
pub message: String,
|
||||||
|
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub color: Option<Color>,
|
pub color: Option<Color>,
|
||||||
|
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub icon: Option<Icon>,
|
pub icon: Option<Icon>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct PromptTextRequest {
|
pub struct PromptTextRequest {
|
||||||
// A unique ID to identify the prompt (eg. "enter-password")
|
// A unique ID to identify the prompt (eg. "enter-password")
|
||||||
pub id: String,
|
pub id: String,
|
||||||
@@ -277,17 +332,15 @@ pub struct PromptTextRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct PromptTextResponse {
|
pub struct PromptTextResponse {
|
||||||
pub value: Option<String>,
|
pub value: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub enum Color {
|
pub enum Color {
|
||||||
Custom,
|
|
||||||
Default,
|
|
||||||
Primary,
|
Primary,
|
||||||
Secondary,
|
Secondary,
|
||||||
Info,
|
Info,
|
||||||
@@ -299,18 +352,23 @@ pub enum Color {
|
|||||||
|
|
||||||
impl Default for Color {
|
impl Default for Color {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Color::Default
|
Color::Secondary
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub enum Icon {
|
pub enum Icon {
|
||||||
|
AlertTriangle,
|
||||||
|
Check,
|
||||||
|
CheckCircle,
|
||||||
|
ChevronDown,
|
||||||
Copy,
|
Copy,
|
||||||
Info,
|
Info,
|
||||||
CheckCircle,
|
Pin,
|
||||||
AlertTriangle,
|
Search,
|
||||||
|
Trash,
|
||||||
|
|
||||||
#[serde(untagged)]
|
#[serde(untagged)]
|
||||||
#[ts(type = "\"_unknown\"")]
|
#[ts(type = "\"_unknown\"")]
|
||||||
@@ -319,17 +377,45 @@ pub enum Icon {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct GetHttpAuthenticationResponse {
|
pub struct GetHttpAuthenticationSummaryResponse {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub label: String,
|
pub label: String,
|
||||||
pub short_label: String,
|
pub short_label: String,
|
||||||
pub config: Vec<FormInput>,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct HttpAuthenticationAction {
|
||||||
|
pub label: String,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub icon: Option<Icon>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct GetHttpAuthenticationConfigRequest {
|
||||||
|
pub context_id: String,
|
||||||
|
pub values: HashMap<String, JsonPrimitive>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct GetHttpAuthenticationConfigResponse {
|
||||||
|
pub args: Vec<FormInput>,
|
||||||
|
pub plugin_ref_id: String,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub actions: Option<Vec<HttpAuthenticationAction>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct HttpHeader {
|
pub struct HttpHeader {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
@@ -337,9 +423,10 @@ pub struct HttpHeader {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CallHttpAuthenticationRequest {
|
pub struct CallHttpAuthenticationRequest {
|
||||||
pub config: serde_json::Map<String, serde_json::Value>,
|
pub context_id: String,
|
||||||
|
pub values: HashMap<String, JsonPrimitive>,
|
||||||
pub method: String,
|
pub method: String,
|
||||||
pub url: String,
|
pub url: String,
|
||||||
pub headers: Vec<HttpHeader>,
|
pub headers: Vec<HttpHeader>,
|
||||||
@@ -347,7 +434,34 @@ pub struct CallHttpAuthenticationRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct CallHttpAuthenticationActionRequest {
|
||||||
|
pub index: i32,
|
||||||
|
pub plugin_ref_id: String,
|
||||||
|
pub args: CallHttpAuthenticationActionArgs,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct CallHttpAuthenticationActionArgs {
|
||||||
|
pub context_id: String,
|
||||||
|
pub values: HashMap<String, JsonPrimitive>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(untagged)]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub enum JsonPrimitive {
|
||||||
|
String(String),
|
||||||
|
Number(f64),
|
||||||
|
Boolean(bool),
|
||||||
|
Null,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CallHttpAuthenticationResponse {
|
pub struct CallHttpAuthenticationResponse {
|
||||||
/// HTTP headers to add to the request. Existing headers will be replaced, while
|
/// HTTP headers to add to the request. Existing headers will be replaced, while
|
||||||
/// new headers will be added.
|
/// new headers will be added.
|
||||||
@@ -356,7 +470,7 @@ pub struct CallHttpAuthenticationResponse {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct GetTemplateFunctionsResponse {
|
pub struct GetTemplateFunctionsResponse {
|
||||||
pub functions: Vec<TemplateFunction>,
|
pub functions: Vec<TemplateFunction>,
|
||||||
pub plugin_ref_id: String,
|
pub plugin_ref_id: String,
|
||||||
@@ -364,9 +478,10 @@ pub struct GetTemplateFunctionsResponse {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct TemplateFunction {
|
pub struct TemplateFunction {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub description: Option<String>,
|
pub description: Option<String>,
|
||||||
|
|
||||||
@@ -379,7 +494,7 @@ pub struct TemplateFunction {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub enum FormInput {
|
pub enum FormInput {
|
||||||
Text(FormInputText),
|
Text(FormInputText),
|
||||||
Editor(FormInputEditor),
|
Editor(FormInputEditor),
|
||||||
@@ -387,14 +502,23 @@ pub enum FormInput {
|
|||||||
Checkbox(FormInputCheckbox),
|
Checkbox(FormInputCheckbox),
|
||||||
File(FormInputFile),
|
File(FormInputFile),
|
||||||
HttpRequest(FormInputHttpRequest),
|
HttpRequest(FormInputHttpRequest),
|
||||||
|
Accordion(FormInputAccordion),
|
||||||
|
Banner(FormInputBanner),
|
||||||
|
Markdown(FormInputMarkdown),
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputBase {
|
pub struct FormInputBase {
|
||||||
|
/// The name of the input. The value will be stored at this object attribute in the resulting data
|
||||||
pub name: String,
|
pub name: String,
|
||||||
|
|
||||||
|
/// Whether this input is visible for the given configuration. Use this to
|
||||||
|
/// make branching forms.
|
||||||
|
#[ts(optional)]
|
||||||
|
pub hidden: Option<bool>,
|
||||||
|
|
||||||
/// Whether the user must fill in the argument
|
/// Whether the user must fill in the argument
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub optional: Option<bool>,
|
pub optional: Option<bool>,
|
||||||
@@ -410,11 +534,14 @@ pub struct FormInputBase {
|
|||||||
/// The default value
|
/// The default value
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub default_value: Option<String>,
|
pub default_value: Option<String>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub disabled: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputText {
|
pub struct FormInputText {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: FormInputBase,
|
pub base: FormInputBase,
|
||||||
@@ -426,11 +553,18 @@ pub struct FormInputText {
|
|||||||
/// Placeholder for the text input
|
/// Placeholder for the text input
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub password: Option<bool>,
|
pub password: Option<bool>,
|
||||||
|
|
||||||
|
/// Whether to allow newlines in the input, like a <textarea/>
|
||||||
|
#[ts(optional)]
|
||||||
|
pub multi_line: Option<bool>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub completion_options: Option<Vec<GenericCompletionOption>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub enum EditorLanguage {
|
pub enum EditorLanguage {
|
||||||
Text,
|
Text,
|
||||||
Javascript,
|
Javascript,
|
||||||
@@ -449,7 +583,7 @@ impl Default for EditorLanguage {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputEditor {
|
pub struct FormInputEditor {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: FormInputBase,
|
pub base: FormInputBase,
|
||||||
@@ -465,11 +599,45 @@ pub struct FormInputEditor {
|
|||||||
/// Language for syntax highlighting
|
/// Language for syntax highlighting
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub language: Option<EditorLanguage>,
|
pub language: Option<EditorLanguage>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub read_only: Option<bool>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub completion_options: Option<Vec<GenericCompletionOption>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct GenericCompletionOption {
|
||||||
|
label: String,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
detail: Option<String>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
info: Option<String>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
#[serde(rename = "type")]
|
||||||
|
pub type_: Option<CompletionOptionType>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub boost: Option<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(rename_all = "snake_case")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub enum CompletionOptionType {
|
||||||
|
Constant,
|
||||||
|
Variable,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputHttpRequest {
|
pub struct FormInputHttpRequest {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: FormInputBase,
|
pub base: FormInputBase,
|
||||||
@@ -477,7 +645,7 @@ pub struct FormInputHttpRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputFile {
|
pub struct FormInputFile {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: FormInputBase,
|
pub base: FormInputBase,
|
||||||
@@ -504,7 +672,7 @@ pub struct FormInputFile {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FileFilter {
|
pub struct FileFilter {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
/// File extensions to require
|
/// File extensions to require
|
||||||
@@ -513,7 +681,7 @@ pub struct FileFilter {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputSelect {
|
pub struct FormInputSelect {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: FormInputBase,
|
pub base: FormInputBase,
|
||||||
@@ -524,7 +692,7 @@ pub struct FormInputSelect {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputCheckbox {
|
pub struct FormInputCheckbox {
|
||||||
#[serde(flatten)]
|
#[serde(flatten)]
|
||||||
pub base: FormInputBase,
|
pub base: FormInputBase,
|
||||||
@@ -532,15 +700,68 @@ pub struct FormInputCheckbox {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FormInputSelectOption {
|
pub struct FormInputSelectOption {
|
||||||
pub name: String,
|
pub label: String,
|
||||||
pub value: String,
|
pub value: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct FormInputAccordion {
|
||||||
|
pub label: String,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub inputs: Option<Vec<FormInput>>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub hidden: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct FormInputBanner {
|
||||||
|
#[ts(optional)]
|
||||||
|
pub inputs: Option<Vec<FormInput>>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub hidden: Option<bool>,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub color: Option<Color>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct FormInputMarkdown {
|
||||||
|
pub content: String,
|
||||||
|
|
||||||
|
#[ts(optional)]
|
||||||
|
pub hidden: Option<bool>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub enum Content {
|
||||||
|
Text { content: String },
|
||||||
|
Markdown { content: String },
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Content {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self::Text {
|
||||||
|
content: String::default(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CallTemplateFunctionRequest {
|
pub struct CallTemplateFunctionRequest {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub args: CallTemplateFunctionArgs,
|
pub args: CallTemplateFunctionArgs,
|
||||||
@@ -548,14 +769,14 @@ pub struct CallTemplateFunctionRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CallTemplateFunctionResponse {
|
pub struct CallTemplateFunctionResponse {
|
||||||
pub value: Option<String>,
|
pub value: Option<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CallTemplateFunctionArgs {
|
pub struct CallTemplateFunctionArgs {
|
||||||
pub purpose: RenderPurpose,
|
pub purpose: RenderPurpose,
|
||||||
pub values: HashMap<String, String>,
|
pub values: HashMap<String, String>,
|
||||||
@@ -563,7 +784,7 @@ pub struct CallTemplateFunctionArgs {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case")]
|
#[serde(rename_all = "snake_case")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub enum RenderPurpose {
|
pub enum RenderPurpose {
|
||||||
Send,
|
Send,
|
||||||
Preview,
|
Preview,
|
||||||
@@ -577,12 +798,12 @@ impl Default for RenderPurpose {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct GetHttpRequestActionsRequest {}
|
pub struct GetHttpRequestActionsRequest {}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct GetHttpRequestActionsResponse {
|
pub struct GetHttpRequestActionsResponse {
|
||||||
pub actions: Vec<HttpRequestAction>,
|
pub actions: Vec<HttpRequestAction>,
|
||||||
pub plugin_ref_id: String,
|
pub plugin_ref_id: String,
|
||||||
@@ -590,9 +811,8 @@ pub struct GetHttpRequestActionsResponse {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct HttpRequestAction {
|
pub struct HttpRequestAction {
|
||||||
pub key: String,
|
|
||||||
pub label: String,
|
pub label: String,
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
pub icon: Option<Icon>,
|
pub icon: Option<Icon>,
|
||||||
@@ -600,37 +820,37 @@ pub struct HttpRequestAction {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CallHttpRequestActionRequest {
|
pub struct CallHttpRequestActionRequest {
|
||||||
pub key: String,
|
pub index: i32,
|
||||||
pub plugin_ref_id: String,
|
pub plugin_ref_id: String,
|
||||||
pub args: CallHttpRequestActionArgs,
|
pub args: CallHttpRequestActionArgs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct CallHttpRequestActionArgs {
|
pub struct CallHttpRequestActionArgs {
|
||||||
pub http_request: HttpRequest,
|
pub http_request: HttpRequest,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct GetHttpRequestByIdRequest {
|
pub struct GetHttpRequestByIdRequest {
|
||||||
pub id: String,
|
pub id: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct GetHttpRequestByIdResponse {
|
pub struct GetHttpRequestByIdResponse {
|
||||||
pub http_request: Option<HttpRequest>,
|
pub http_request: Option<HttpRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FindHttpResponsesRequest {
|
pub struct FindHttpResponsesRequest {
|
||||||
pub request_id: String,
|
pub request_id: String,
|
||||||
#[ts(optional)]
|
#[ts(optional)]
|
||||||
@@ -639,14 +859,14 @@ pub struct FindHttpResponsesRequest {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct FindHttpResponsesResponse {
|
pub struct FindHttpResponsesResponse {
|
||||||
pub http_responses: Vec<HttpResponse>,
|
pub http_responses: Vec<HttpResponse>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
#[serde(default, rename_all = "camelCase")]
|
#[serde(default, rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "events.ts")]
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
pub struct ImportResources {
|
pub struct ImportResources {
|
||||||
pub workspaces: Vec<Workspace>,
|
pub workspaces: Vec<Workspace>,
|
||||||
pub environments: Vec<Environment>,
|
pub environments: Vec<Environment>,
|
||||||
@@ -654,3 +874,45 @@ pub struct ImportResources {
|
|||||||
pub http_requests: Vec<HttpRequest>,
|
pub http_requests: Vec<HttpRequest>,
|
||||||
pub grpc_requests: Vec<GrpcRequest>,
|
pub grpc_requests: Vec<GrpcRequest>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct GetKeyValueRequest {
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct GetKeyValueResponse {
|
||||||
|
#[ts(optional)]
|
||||||
|
pub value: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct SetKeyValueRequest {
|
||||||
|
pub key: String,
|
||||||
|
pub value: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default)]
|
||||||
|
#[ts(export, type = "{}", export_to = "gen_events.ts")]
|
||||||
|
pub struct SetKeyValueResponse {}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default, rename_all = "camelCase")]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct DeleteKeyValueRequest {
|
||||||
|
pub key: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
|
||||||
|
#[serde(default)]
|
||||||
|
#[ts(export, export_to = "gen_events.ts")]
|
||||||
|
pub struct DeleteKeyValueResponse {
|
||||||
|
pub deleted: bool,
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,12 +3,13 @@ use crate::error::Error::{
|
|||||||
};
|
};
|
||||||
use crate::error::Result;
|
use crate::error::Result;
|
||||||
use crate::events::{
|
use crate::events::{
|
||||||
BootRequest, CallHttpAuthenticationRequest, CallHttpAuthenticationResponse,
|
BootRequest, CallHttpAuthenticationActionArgs, CallHttpAuthenticationActionRequest,
|
||||||
CallHttpRequestActionRequest, CallTemplateFunctionArgs, CallTemplateFunctionRequest,
|
CallHttpAuthenticationRequest, CallHttpAuthenticationResponse, CallHttpRequestActionRequest,
|
||||||
CallTemplateFunctionResponse, EmptyPayload, FilterRequest, FilterResponse, FormInput,
|
CallTemplateFunctionArgs, CallTemplateFunctionRequest, CallTemplateFunctionResponse,
|
||||||
GetHttpAuthenticationResponse, GetHttpRequestActionsResponse, GetTemplateFunctionsResponse,
|
EmptyPayload, FilterRequest, FilterResponse, GetHttpAuthenticationConfigRequest,
|
||||||
ImportRequest, ImportResponse, InternalEvent, InternalEventPayload, RenderPurpose,
|
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||||
WindowContext,
|
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, ImportRequest, ImportResponse,
|
||||||
|
InternalEvent, InternalEventPayload, JsonPrimitive, RenderPurpose, WindowContext,
|
||||||
};
|
};
|
||||||
use crate::nodejs::start_nodejs_plugin_runtime;
|
use crate::nodejs::start_nodejs_plugin_runtime;
|
||||||
use crate::plugin_handle::PluginHandle;
|
use crate::plugin_handle::PluginHandle;
|
||||||
@@ -24,6 +25,7 @@ use tauri::{AppHandle, Manager, Runtime, WebviewWindow};
|
|||||||
use tokio::fs::read_dir;
|
use tokio::fs::read_dir;
|
||||||
use tokio::net::TcpListener;
|
use tokio::net::TcpListener;
|
||||||
use tokio::sync::{mpsc, Mutex};
|
use tokio::sync::{mpsc, Mutex};
|
||||||
|
use tokio::time::timeout;
|
||||||
use yaak_models::queries::{generate_id, list_plugins};
|
use yaak_models::queries::{generate_id, list_plugins};
|
||||||
|
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
@@ -86,7 +88,7 @@ impl PluginManager {
|
|||||||
let addr = listener.local_addr().expect("Failed to get local address");
|
let addr = listener.local_addr().expect("Failed to get local address");
|
||||||
|
|
||||||
// 1. Reload all plugins when the Node.js runtime connects
|
// 1. Reload all plugins when the Node.js runtime connects
|
||||||
{
|
let init_plugins_task = {
|
||||||
let plugin_manager = plugin_manager.clone();
|
let plugin_manager = plugin_manager.clone();
|
||||||
let app_handle = app_handle.clone();
|
let app_handle = app_handle.clone();
|
||||||
tauri::async_runtime::spawn(async move {
|
tauri::async_runtime::spawn(async move {
|
||||||
@@ -94,7 +96,7 @@ impl PluginManager {
|
|||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
info!("Plugin runtime client connected!");
|
info!("Plugin runtime client connected!");
|
||||||
plugin_manager
|
plugin_manager
|
||||||
.initialize_all_plugins(&app_handle, WindowContext::None)
|
.initialize_all_plugins(&app_handle, &WindowContext::None)
|
||||||
.await
|
.await
|
||||||
.expect("Failed to reload plugins");
|
.expect("Failed to reload plugins");
|
||||||
}
|
}
|
||||||
@@ -102,7 +104,7 @@ impl PluginManager {
|
|||||||
warn!("Failed to receive from client connection rx {e:?}");
|
warn!("Failed to receive from client connection rx {e:?}");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
};
|
};
|
||||||
|
|
||||||
// 1. Spawn server in the background
|
// 1. Spawn server in the background
|
||||||
@@ -114,8 +116,13 @@ impl PluginManager {
|
|||||||
// 2. Start Node.js runtime and initialize plugins
|
// 2. Start Node.js runtime and initialize plugins
|
||||||
tauri::async_runtime::block_on(async move {
|
tauri::async_runtime::block_on(async move {
|
||||||
start_nodejs_plugin_runtime(&app_handle, addr, &kill_server_rx).await.unwrap();
|
start_nodejs_plugin_runtime(&app_handle, addr, &kill_server_rx).await.unwrap();
|
||||||
|
info!("Waiting for plugins to initialize");
|
||||||
|
init_plugins_task.await.unwrap();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 3. Block waiting for plugins to initialize
|
||||||
|
tauri::async_runtime::block_on(async move {});
|
||||||
|
|
||||||
plugin_manager
|
plugin_manager
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -160,14 +167,14 @@ impl PluginManager {
|
|||||||
[bundled_plugin_dirs, installed_plugin_dirs].concat()
|
[bundled_plugin_dirs, installed_plugin_dirs].concat()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn uninstall(&self, window_context: WindowContext, dir: &str) -> Result<()> {
|
pub async fn uninstall(&self, window_context: &WindowContext, dir: &str) -> Result<()> {
|
||||||
let plugin = self.get_plugin_by_dir(dir).await.ok_or(PluginNotFoundErr(dir.to_string()))?;
|
let plugin = self.get_plugin_by_dir(dir).await.ok_or(PluginNotFoundErr(dir.to_string()))?;
|
||||||
self.remove_plugin(window_context, &plugin).await
|
self.remove_plugin(window_context, &plugin).await
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn remove_plugin(
|
async fn remove_plugin(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
plugin: &PluginHandle,
|
plugin: &PluginHandle,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
// Terminate the plugin
|
// Terminate the plugin
|
||||||
@@ -185,7 +192,7 @@ impl PluginManager {
|
|||||||
|
|
||||||
pub async fn add_plugin_by_dir(
|
pub async fn add_plugin_by_dir(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
dir: &str,
|
dir: &str,
|
||||||
watch: bool,
|
watch: bool,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
@@ -197,20 +204,22 @@ impl PluginManager {
|
|||||||
};
|
};
|
||||||
let plugin_handle = PluginHandle::new(dir, tx.clone());
|
let plugin_handle = PluginHandle::new(dir, tx.clone());
|
||||||
|
|
||||||
// Add the new plugin
|
|
||||||
self.plugins.lock().await.push(plugin_handle.clone());
|
|
||||||
|
|
||||||
// Boot the plugin
|
// Boot the plugin
|
||||||
let event = self
|
let event = timeout(
|
||||||
.send_to_plugin_and_wait(
|
Duration::from_secs(1),
|
||||||
|
self.send_to_plugin_and_wait(
|
||||||
window_context,
|
window_context,
|
||||||
&plugin_handle,
|
&plugin_handle,
|
||||||
&InternalEventPayload::BootRequest(BootRequest {
|
&InternalEventPayload::BootRequest(BootRequest {
|
||||||
dir: dir.to_string(),
|
dir: dir.to_string(),
|
||||||
watch,
|
watch,
|
||||||
}),
|
}),
|
||||||
)
|
),
|
||||||
.await?;
|
)
|
||||||
|
.await??;
|
||||||
|
|
||||||
|
// Add the new plugin
|
||||||
|
self.plugins.lock().await.push(plugin_handle.clone());
|
||||||
|
|
||||||
let resp = match event.payload {
|
let resp = match event.payload {
|
||||||
InternalEventPayload::BootResponse(resp) => resp,
|
InternalEventPayload::BootResponse(resp) => resp,
|
||||||
@@ -226,20 +235,21 @@ impl PluginManager {
|
|||||||
pub async fn initialize_all_plugins<R: Runtime>(
|
pub async fn initialize_all_plugins<R: Runtime>(
|
||||||
&self,
|
&self,
|
||||||
app_handle: &AppHandle<R>,
|
app_handle: &AppHandle<R>,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let dirs = self.list_plugin_dirs(app_handle).await;
|
let candidates = self.list_plugin_dirs(app_handle).await;
|
||||||
for d in dirs.clone() {
|
for candidate in candidates.clone() {
|
||||||
// First remove the plugin if it exists
|
// First remove the plugin if it exists
|
||||||
if let Some(plugin) = self.get_plugin_by_dir(d.dir.as_str()).await {
|
if let Some(plugin) = self.get_plugin_by_dir(candidate.dir.as_str()).await {
|
||||||
if let Err(e) = self.remove_plugin(window_context.to_owned(), &plugin).await {
|
if let Err(e) = self.remove_plugin(window_context, &plugin).await {
|
||||||
warn!("Failed to remove plugin {} {e:?}", d.dir);
|
warn!("Failed to remove plugin {} {e:?}", candidate.dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Err(e) =
|
if let Err(e) = self
|
||||||
self.add_plugin_by_dir(window_context.to_owned(), d.dir.as_str(), d.watch).await
|
.add_plugin_by_dir(window_context, candidate.dir.as_str(), candidate.watch)
|
||||||
|
.await
|
||||||
{
|
{
|
||||||
warn!("Failed to add plugin {} {e:?}", d.dir);
|
warn!("Failed to add plugin {} {e:?}", candidate.dir);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -280,13 +290,13 @@ impl PluginManager {
|
|||||||
source_event: &InternalEvent,
|
source_event: &InternalEvent,
|
||||||
payload: &InternalEventPayload,
|
payload: &InternalEventPayload,
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let window_label = source_event.to_owned().window_context;
|
let window_context = source_event.to_owned().window_context;
|
||||||
let reply_id = Some(source_event.to_owned().id);
|
let reply_id = Some(source_event.to_owned().id);
|
||||||
let plugin = self
|
let plugin = self
|
||||||
.get_plugin_by_ref_id(source_event.plugin_ref_id.as_str())
|
.get_plugin_by_ref_id(source_event.plugin_ref_id.as_str())
|
||||||
.await
|
.await
|
||||||
.ok_or(PluginNotFoundErr(source_event.plugin_ref_id.to_string()))?;
|
.ok_or(PluginNotFoundErr(source_event.plugin_ref_id.to_string()))?;
|
||||||
let event = plugin.build_event_to_send_raw(window_label, &payload, reply_id);
|
let event = plugin.build_event_to_send_raw(&window_context, &payload, reply_id);
|
||||||
plugin.send(&event).await
|
plugin.send(&event).await
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -310,7 +320,7 @@ impl PluginManager {
|
|||||||
|
|
||||||
async fn send_to_plugin_and_wait(
|
async fn send_to_plugin_and_wait(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
plugin: &PluginHandle,
|
plugin: &PluginHandle,
|
||||||
payload: &InternalEventPayload,
|
payload: &InternalEventPayload,
|
||||||
) -> Result<InternalEvent> {
|
) -> Result<InternalEvent> {
|
||||||
@@ -321,7 +331,7 @@ impl PluginManager {
|
|||||||
|
|
||||||
async fn send_and_wait(
|
async fn send_and_wait(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
payload: &InternalEventPayload,
|
payload: &InternalEventPayload,
|
||||||
) -> Result<Vec<InternalEvent>> {
|
) -> Result<Vec<InternalEvent>> {
|
||||||
let plugins = { self.plugins.lock().await.clone() };
|
let plugins = { self.plugins.lock().await.clone() };
|
||||||
@@ -330,7 +340,7 @@ impl PluginManager {
|
|||||||
|
|
||||||
async fn send_to_plugins_and_wait(
|
async fn send_to_plugins_and_wait(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
payload: &InternalEventPayload,
|
payload: &InternalEventPayload,
|
||||||
plugins: Vec<PluginHandle>,
|
plugins: Vec<PluginHandle>,
|
||||||
) -> Result<Vec<InternalEvent>> {
|
) -> Result<Vec<InternalEvent>> {
|
||||||
@@ -340,7 +350,7 @@ impl PluginManager {
|
|||||||
// 1. Build the events with IDs and everything
|
// 1. Build the events with IDs and everything
|
||||||
let events_to_send = plugins
|
let events_to_send = plugins
|
||||||
.iter()
|
.iter()
|
||||||
.map(|p| p.build_event_to_send(window_context.to_owned(), payload, None))
|
.map(|p| p.build_event_to_send(window_context, payload, None))
|
||||||
.collect::<Vec<InternalEvent>>();
|
.collect::<Vec<InternalEvent>>();
|
||||||
|
|
||||||
// 2. Spawn thread to subscribe to incoming events and check reply ids
|
// 2. Spawn thread to subscribe to incoming events and check reply ids
|
||||||
@@ -358,9 +368,9 @@ impl PluginManager {
|
|||||||
if matched_sent_event {
|
if matched_sent_event {
|
||||||
found_events.push(event.clone());
|
found_events.push(event.clone());
|
||||||
};
|
};
|
||||||
|
|
||||||
let found_them_all = found_events.len() == events_to_send.len();
|
let found_them_all = found_events.len() == events_to_send.len();
|
||||||
if found_them_all{
|
if found_them_all {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -393,7 +403,7 @@ impl PluginManager {
|
|||||||
) -> Result<Vec<GetHttpRequestActionsResponse>> {
|
) -> Result<Vec<GetHttpRequestActionsResponse>> {
|
||||||
let reply_events = self
|
let reply_events = self
|
||||||
.send_and_wait(
|
.send_and_wait(
|
||||||
WindowContext::from_window(window),
|
&WindowContext::from_window(window),
|
||||||
&InternalEventPayload::GetHttpRequestActionsRequest(EmptyPayload {}),
|
&InternalEventPayload::GetHttpRequestActionsRequest(EmptyPayload {}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
@@ -412,12 +422,12 @@ impl PluginManager {
|
|||||||
&self,
|
&self,
|
||||||
window: &WebviewWindow<R>,
|
window: &WebviewWindow<R>,
|
||||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||||
self.get_template_functions_with_context(WindowContext::from_window(window)).await
|
self.get_template_functions_with_context(&WindowContext::from_window(window)).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_template_functions_with_context(
|
pub async fn get_template_functions_with_context(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
) -> Result<Vec<GetTemplateFunctionsResponse>> {
|
||||||
let reply_events = self
|
let reply_events = self
|
||||||
.send_and_wait(window_context, &InternalEventPayload::GetTemplateFunctionsRequest)
|
.send_and_wait(window_context, &InternalEventPayload::GetTemplateFunctionsRequest)
|
||||||
@@ -442,7 +452,7 @@ impl PluginManager {
|
|||||||
let plugin =
|
let plugin =
|
||||||
self.get_plugin_by_ref_id(ref_id.as_str()).await.ok_or(PluginNotFoundErr(ref_id))?;
|
self.get_plugin_by_ref_id(ref_id.as_str()).await.ok_or(PluginNotFoundErr(ref_id))?;
|
||||||
let event = plugin.build_event_to_send(
|
let event = plugin.build_event_to_send(
|
||||||
WindowContext::from_window(window),
|
&WindowContext::from_window(window),
|
||||||
&InternalEventPayload::CallHttpRequestActionRequest(req),
|
&InternalEventPayload::CallHttpRequestActionRequest(req),
|
||||||
None,
|
None,
|
||||||
);
|
);
|
||||||
@@ -450,21 +460,22 @@ impl PluginManager {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_http_authentication<R: Runtime>(
|
pub async fn get_http_authentication_summaries<R: Runtime>(
|
||||||
&self,
|
&self,
|
||||||
window: &WebviewWindow<R>,
|
window: &WebviewWindow<R>,
|
||||||
) -> Result<Vec<(PluginHandle, GetHttpAuthenticationResponse)>> {
|
) -> Result<Vec<(PluginHandle, GetHttpAuthenticationSummaryResponse)>> {
|
||||||
let window_context = WindowContext::from_window(window);
|
let window_context = WindowContext::from_window(window);
|
||||||
let reply_events = self
|
let reply_events = self
|
||||||
.send_and_wait(
|
.send_and_wait(
|
||||||
window_context,
|
&window_context,
|
||||||
&InternalEventPayload::GetHttpAuthenticationRequest(EmptyPayload {}),
|
&InternalEventPayload::GetHttpAuthenticationSummaryRequest(EmptyPayload {}),
|
||||||
)
|
)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
let mut results = Vec::new();
|
let mut results = Vec::new();
|
||||||
for event in reply_events {
|
for event in reply_events {
|
||||||
if let InternalEventPayload::GetHttpAuthenticationResponse(resp) = event.payload {
|
if let InternalEventPayload::GetHttpAuthenticationSummaryResponse(resp) = event.payload
|
||||||
|
{
|
||||||
let plugin = self
|
let plugin = self
|
||||||
.get_plugin_by_ref_id(&event.plugin_ref_id)
|
.get_plugin_by_ref_id(&event.plugin_ref_id)
|
||||||
.await
|
.await
|
||||||
@@ -476,43 +487,84 @@ impl PluginManager {
|
|||||||
Ok(results)
|
Ok(results)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn get_http_authentication_config<R: Runtime>(
|
||||||
|
&self,
|
||||||
|
window: &WebviewWindow<R>,
|
||||||
|
auth_name: &str,
|
||||||
|
values: HashMap<String, JsonPrimitive>,
|
||||||
|
request_id: &str,
|
||||||
|
) -> Result<GetHttpAuthenticationConfigResponse> {
|
||||||
|
let results = self.get_http_authentication_summaries(window).await?;
|
||||||
|
let plugin = results
|
||||||
|
.iter()
|
||||||
|
.find_map(|(p, r)| if r.name == auth_name { Some(p) } else { None })
|
||||||
|
.ok_or(PluginNotFoundErr(auth_name.into()))?;
|
||||||
|
|
||||||
|
let context_id = format!("{:x}", md5::compute(request_id.to_string()));
|
||||||
|
let event = self
|
||||||
|
.send_to_plugin_and_wait(
|
||||||
|
&WindowContext::from_window(window),
|
||||||
|
&plugin,
|
||||||
|
&InternalEventPayload::GetHttpAuthenticationConfigRequest(
|
||||||
|
GetHttpAuthenticationConfigRequest { values, context_id },
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
match event.payload {
|
||||||
|
InternalEventPayload::GetHttpAuthenticationConfigResponse(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_http_authentication_action<R: Runtime>(
|
||||||
|
&self,
|
||||||
|
window: &WebviewWindow<R>,
|
||||||
|
auth_name: &str,
|
||||||
|
action_index: i32,
|
||||||
|
values: HashMap<String, JsonPrimitive>,
|
||||||
|
request_id: &str,
|
||||||
|
) -> Result<()> {
|
||||||
|
let results = self.get_http_authentication_summaries(window).await?;
|
||||||
|
let plugin = results
|
||||||
|
.iter()
|
||||||
|
.find_map(|(p, r)| if r.name == auth_name { Some(p) } else { None })
|
||||||
|
.ok_or(PluginNotFoundErr(auth_name.into()))?;
|
||||||
|
|
||||||
|
let context_id = format!("{:x}", md5::compute(request_id.to_string()));
|
||||||
|
self
|
||||||
|
.send_to_plugin_and_wait(
|
||||||
|
&WindowContext::from_window(window),
|
||||||
|
&plugin,
|
||||||
|
&InternalEventPayload::CallHttpAuthenticationActionRequest(
|
||||||
|
CallHttpAuthenticationActionRequest {
|
||||||
|
index: action_index,
|
||||||
|
plugin_ref_id: plugin.clone().ref_id,
|
||||||
|
args: CallHttpAuthenticationActionArgs { context_id, values },
|
||||||
|
},
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn call_http_authentication<R: Runtime>(
|
pub async fn call_http_authentication<R: Runtime>(
|
||||||
&self,
|
&self,
|
||||||
window: &WebviewWindow<R>,
|
window: &WebviewWindow<R>,
|
||||||
auth_name: &str,
|
auth_name: &str,
|
||||||
req: CallHttpAuthenticationRequest,
|
req: CallHttpAuthenticationRequest,
|
||||||
) -> Result<CallHttpAuthenticationResponse> {
|
) -> Result<CallHttpAuthenticationResponse> {
|
||||||
let handlers = self.get_http_authentication(window).await?;
|
let handlers = self.get_http_authentication_summaries(window).await?;
|
||||||
let (plugin, authentication) = handlers
|
let (plugin, _) = handlers
|
||||||
.iter()
|
.iter()
|
||||||
.find(|(_, a)| a.name == auth_name)
|
.find(|(_, a)| a.name == auth_name)
|
||||||
.ok_or(AuthPluginNotFound(auth_name.to_string()))?;
|
.ok_or(AuthPluginNotFound(auth_name.to_string()))?;
|
||||||
|
|
||||||
// Clone for mutability
|
|
||||||
let mut req = req.clone();
|
|
||||||
|
|
||||||
// Fill in default values
|
|
||||||
for arg in authentication.config.clone() {
|
|
||||||
let base = match arg {
|
|
||||||
FormInput::Text(a) => a.base,
|
|
||||||
FormInput::Editor(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 = req.config.get(base.name.as_str()) {
|
|
||||||
let default = match base.default_value {
|
|
||||||
None => serde_json::Value::Null,
|
|
||||||
Some(s) => serde_json::Value::String(s),
|
|
||||||
};
|
|
||||||
req.config.insert(base.name, default);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let event = self
|
let event = self
|
||||||
.send_to_plugin_and_wait(
|
.send_to_plugin_and_wait(
|
||||||
WindowContext::from_window(window),
|
&WindowContext::from_window(window),
|
||||||
&plugin,
|
&plugin,
|
||||||
&InternalEventPayload::CallHttpAuthenticationRequest(req),
|
&InternalEventPayload::CallHttpAuthenticationRequest(req),
|
||||||
)
|
)
|
||||||
@@ -528,7 +580,7 @@ impl PluginManager {
|
|||||||
|
|
||||||
pub async fn call_template_function(
|
pub async fn call_template_function(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
args: HashMap<String, String>,
|
args: HashMap<String, String>,
|
||||||
purpose: RenderPurpose,
|
purpose: RenderPurpose,
|
||||||
@@ -562,7 +614,7 @@ impl PluginManager {
|
|||||||
) -> Result<(ImportResponse, String)> {
|
) -> Result<(ImportResponse, String)> {
|
||||||
let reply_events = self
|
let reply_events = self
|
||||||
.send_and_wait(
|
.send_and_wait(
|
||||||
WindowContext::from_window(window),
|
&WindowContext::from_window(window),
|
||||||
&InternalEventPayload::ImportRequest(ImportRequest {
|
&InternalEventPayload::ImportRequest(ImportRequest {
|
||||||
content: content.to_string(),
|
content: content.to_string(),
|
||||||
}),
|
}),
|
||||||
@@ -607,7 +659,7 @@ impl PluginManager {
|
|||||||
|
|
||||||
let event = self
|
let event = self
|
||||||
.send_to_plugin_and_wait(
|
.send_to_plugin_and_wait(
|
||||||
WindowContext::from_window(window),
|
&WindowContext::from_window(window),
|
||||||
&plugin,
|
&plugin,
|
||||||
&InternalEventPayload::FilterRequest(FilterRequest {
|
&InternalEventPayload::FilterRequest(FilterRequest {
|
||||||
filter: filter.to_string(),
|
filter: filter.to_string(),
|
||||||
|
|||||||
@@ -26,6 +26,10 @@ impl PluginHandle {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn name(&self) -> String {
|
||||||
|
self.boot_resp.lock().await.name.clone()
|
||||||
|
}
|
||||||
|
|
||||||
pub async fn info(&self) -> BootResponse {
|
pub async fn info(&self) -> BootResponse {
|
||||||
let resp = &*self.boot_resp.lock().await;
|
let resp = &*self.boot_resp.lock().await;
|
||||||
resp.clone()
|
resp.clone()
|
||||||
@@ -33,7 +37,7 @@ impl PluginHandle {
|
|||||||
|
|
||||||
pub fn build_event_to_send(
|
pub fn build_event_to_send(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
payload: &InternalEventPayload,
|
payload: &InternalEventPayload,
|
||||||
reply_id: Option<String>,
|
reply_id: Option<String>,
|
||||||
) -> InternalEvent {
|
) -> InternalEvent {
|
||||||
@@ -42,7 +46,7 @@ impl PluginHandle {
|
|||||||
|
|
||||||
pub(crate) fn build_event_to_send_raw(
|
pub(crate) fn build_event_to_send_raw(
|
||||||
&self,
|
&self,
|
||||||
window_context: WindowContext,
|
window_context: &WindowContext,
|
||||||
payload: &InternalEventPayload,
|
payload: &InternalEventPayload,
|
||||||
reply_id: Option<String>,
|
reply_id: Option<String>,
|
||||||
) -> InternalEvent {
|
) -> InternalEvent {
|
||||||
@@ -53,18 +57,18 @@ impl PluginHandle {
|
|||||||
plugin_name: dir.file_name().unwrap().to_str().unwrap().to_string(),
|
plugin_name: dir.file_name().unwrap().to_str().unwrap().to_string(),
|
||||||
reply_id,
|
reply_id,
|
||||||
payload: payload.clone(),
|
payload: payload.clone(),
|
||||||
window_context,
|
window_context: window_context.clone(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn terminate(&self, window_context: WindowContext) -> Result<()> {
|
pub async fn terminate(&self, window_context: &WindowContext) -> Result<()> {
|
||||||
info!("Terminating plugin {}", self.dir);
|
info!("Terminating plugin {}", self.dir);
|
||||||
let event =
|
let event =
|
||||||
self.build_event_to_send(window_context, &InternalEventPayload::TerminateRequest, None);
|
self.build_event_to_send(window_context, &InternalEventPayload::TerminateRequest, None);
|
||||||
self.send(&event).await
|
self.send(&event).await
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(crate) async fn send(&self, event: &InternalEvent) -> Result<()> {
|
pub async fn send(&self, event: &InternalEvent) -> Result<()> {
|
||||||
self.to_plugin_tx.lock().await.send(event.to_owned()).await?;
|
self.to_plugin_tx.lock().await.send(event.to_owned()).await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,8 +85,9 @@ impl PluginRuntimeServerWebsocket {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Parse everything but the payload so we can catch errors on that, specifically
|
// Parse everything but the payload so we can catch errors on that, specifically
|
||||||
let payload = serde_json::from_value::<InternalEventPayload>(event.payload)
|
let payload = serde_json::from_value::<InternalEventPayload>(event.payload.clone())
|
||||||
.unwrap_or_else(|e| {
|
.unwrap_or_else(|e| {
|
||||||
|
warn!("Plugin error from {}: {:?} {}", event.plugin_name, e, event.payload);
|
||||||
InternalEventPayload::ErrorResponse(ErrorResponse {
|
InternalEventPayload::ErrorResponse(ErrorResponse {
|
||||||
error: format!("Plugin error from {}: {e:?}", event.plugin_name),
|
error: format!("Plugin error from {}: {e:?}", event.plugin_name),
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||||
import type { SyncModel } from "./models.js";
|
import type { SyncModel } from "./gen_models.js";
|
||||||
import type { SyncState } from "./models.js";
|
import type { SyncState } from "./gen_models.js";
|
||||||
|
|
||||||
export type FsCandidate = { "type": "FsCandidate", model: SyncModel, relPath: string, checksum: string, };
|
export type FsCandidate = { "type": "FsCandidate", model: SyncModel, relPath: string, checksum: string, };
|
||||||
|
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
import { Channel, invoke } from '@tauri-apps/api/core';
|
import { Channel, invoke } from '@tauri-apps/api/core';
|
||||||
import { emit } from '@tauri-apps/api/event';
|
import { emit } from '@tauri-apps/api/event';
|
||||||
import { SyncOp } from './bindings/sync';
|
import { SyncOp } from './bindings/gen_sync';
|
||||||
import { WatchEvent, WatchResult } from './bindings/watch';
|
import { WatchEvent, WatchResult } from './bindings/gen_watch';
|
||||||
|
|
||||||
export async function calculateSync(workspaceId: string, syncDir: string) {
|
export async function calculateSync(workspaceId: string, syncDir: string) {
|
||||||
return invoke<SyncOp[]>('plugin:yaak-sync|calculate', {
|
return invoke<SyncOp[]>('plugin:yaak-sync|calculate', {
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ pub async fn apply<R: Runtime>(
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "watch.ts")]
|
#[ts(export, export_to = "gen_watch.ts")]
|
||||||
pub(crate) struct WatchResult {
|
pub(crate) struct WatchResult {
|
||||||
unlisten_event: String,
|
unlisten_event: String,
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use yaak_models::models::{AnyModel, Environment, Folder, GrpcRequest, HttpReques
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "snake_case", tag = "type")]
|
#[serde(rename_all = "snake_case", tag = "type")]
|
||||||
#[ts(export, export_to = "models.ts")]
|
#[ts(export, export_to = "gen_models.ts")]
|
||||||
pub enum SyncModel {
|
pub enum SyncModel {
|
||||||
Workspace(Workspace),
|
Workspace(Workspace),
|
||||||
Environment(Environment),
|
Environment(Environment),
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ use yaak_models::queries::{
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase", tag = "type")]
|
#[serde(rename_all = "camelCase", tag = "type")]
|
||||||
#[ts(export, export_to = "sync.ts")]
|
#[ts(export, export_to = "gen_sync.ts")]
|
||||||
pub(crate) enum SyncOp {
|
pub(crate) enum SyncOp {
|
||||||
FsCreate {
|
FsCreate {
|
||||||
model: SyncModel,
|
model: SyncModel,
|
||||||
@@ -98,7 +98,7 @@ impl DbCandidate {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase", tag = "type")]
|
#[serde(rename_all = "camelCase", tag = "type")]
|
||||||
#[ts(export, export_to = "sync.ts")]
|
#[ts(export, export_to = "gen_sync.ts")]
|
||||||
pub(crate) struct FsCandidate {
|
pub(crate) struct FsCandidate {
|
||||||
pub(crate) model: SyncModel,
|
pub(crate) model: SyncModel,
|
||||||
pub(crate) rel_path: PathBuf,
|
pub(crate) rel_path: PathBuf,
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ use ts_rs::TS;
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
|
||||||
#[serde(rename_all = "camelCase")]
|
#[serde(rename_all = "camelCase")]
|
||||||
#[ts(export, export_to = "watch.ts")]
|
#[ts(export, export_to = "gen_watch.ts")]
|
||||||
pub(crate) struct WatchEvent {
|
pub(crate) struct WatchEvent {
|
||||||
paths: Vec<PathBuf>,
|
paths: Vec<PathBuf>,
|
||||||
kind: String,
|
kind: String,
|
||||||
|
|||||||
@@ -153,9 +153,10 @@ export function CommandPaletteDialog({ onClose }: { onClose: () => void }) {
|
|||||||
label: 'Send Request',
|
label: 'Send Request',
|
||||||
onSelect: () => sendRequest(activeRequest.id),
|
onSelect: () => sendRequest(activeRequest.id),
|
||||||
});
|
});
|
||||||
for (const a of httpRequestActions) {
|
for (let i = 0; i < httpRequestActions.length; i++) {
|
||||||
|
const a = httpRequestActions[i]!;
|
||||||
commands.push({
|
commands.push({
|
||||||
key: a.key,
|
key: `http_request_action.${i}`,
|
||||||
label: a.label,
|
label: a.label,
|
||||||
onSelect: () => a.call(activeRequest),
|
onSelect: () => a.call(activeRequest),
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -73,7 +73,6 @@ export const CookieDropdown = memo(function CookieDropdown() {
|
|||||||
...(((cookieJars ?? []).length > 1 // Never delete the last one
|
...(((cookieJars ?? []).length > 1 // Never delete the last one
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: 'delete',
|
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import type {
|
|||||||
FormInputHttpRequest,
|
FormInputHttpRequest,
|
||||||
FormInputSelect,
|
FormInputSelect,
|
||||||
FormInputText,
|
FormInputText,
|
||||||
|
JsonPrimitive,
|
||||||
} from '@yaakapp-internal/plugins';
|
} from '@yaakapp-internal/plugins';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
@@ -15,51 +16,93 @@ import { useFolders } from '../hooks/useFolders';
|
|||||||
import { useHttpRequests } from '../hooks/useHttpRequests';
|
import { useHttpRequests } from '../hooks/useHttpRequests';
|
||||||
import { capitalize } from '../lib/capitalize';
|
import { capitalize } from '../lib/capitalize';
|
||||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||||
|
import { Banner } from './core/Banner';
|
||||||
import { Checkbox } from './core/Checkbox';
|
import { Checkbox } from './core/Checkbox';
|
||||||
import { Editor } from './core/Editor/Editor';
|
import { Editor } from './core/Editor/Editor';
|
||||||
import { Input } from './core/Input';
|
import { Input } from './core/Input';
|
||||||
import { Label } from './core/Label';
|
import { Label } from './core/Label';
|
||||||
import { Select } from './core/Select';
|
import { Select } from './core/Select';
|
||||||
import { VStack } from './core/Stacks';
|
import { VStack } from './core/Stacks';
|
||||||
|
import { Markdown } from './Markdown';
|
||||||
import { SelectFile } from './SelectFile';
|
import { SelectFile } from './SelectFile';
|
||||||
|
|
||||||
// eslint-disable-next-line react-refresh/only-export-components
|
// eslint-disable-next-line react-refresh/only-export-components
|
||||||
export const DYNAMIC_FORM_NULL_ARG = '__NULL__';
|
export const DYNAMIC_FORM_NULL_ARG = '__NULL__';
|
||||||
|
const INPUT_SIZE = 'sm';
|
||||||
|
|
||||||
export function DynamicForm<T extends Record<string, string | boolean>>({
|
interface Props<T> {
|
||||||
config,
|
inputs: FormInput[] | undefined | null;
|
||||||
data,
|
|
||||||
onChange,
|
|
||||||
useTemplating,
|
|
||||||
autocompleteVariables,
|
|
||||||
stateKey,
|
|
||||||
}: {
|
|
||||||
config: FormInput[];
|
|
||||||
onChange: (value: T) => void;
|
onChange: (value: T) => void;
|
||||||
data: T;
|
data: T;
|
||||||
useTemplating?: boolean;
|
useTemplating?: boolean;
|
||||||
autocompleteVariables?: boolean;
|
autocompleteVariables?: boolean;
|
||||||
stateKey: string;
|
stateKey: string;
|
||||||
}) {
|
disabled?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function DynamicForm<T extends Record<string, JsonPrimitive>>({
|
||||||
|
inputs,
|
||||||
|
data,
|
||||||
|
onChange,
|
||||||
|
useTemplating,
|
||||||
|
autocompleteVariables,
|
||||||
|
stateKey,
|
||||||
|
disabled,
|
||||||
|
}: Props<T>) {
|
||||||
const setDataAttr = useCallback(
|
const setDataAttr = useCallback(
|
||||||
(name: string, value: string | boolean | null) => {
|
(name: string, value: JsonPrimitive) => {
|
||||||
onChange({ ...data, [name]: value == DYNAMIC_FORM_NULL_ARG ? undefined : value });
|
onChange({ ...data, [name]: value == DYNAMIC_FORM_NULL_ARG ? undefined : value });
|
||||||
},
|
},
|
||||||
[data, onChange],
|
[data, onChange],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<FormInputs
|
||||||
|
disabled={disabled}
|
||||||
|
inputs={inputs}
|
||||||
|
setDataAttr={setDataAttr}
|
||||||
|
stateKey={stateKey}
|
||||||
|
useTemplating={useTemplating}
|
||||||
|
autocompleteVariables={autocompleteVariables}
|
||||||
|
data={data}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function FormInputs<T extends Record<string, JsonPrimitive>>({
|
||||||
|
inputs,
|
||||||
|
autocompleteVariables,
|
||||||
|
stateKey,
|
||||||
|
useTemplating,
|
||||||
|
setDataAttr,
|
||||||
|
data,
|
||||||
|
disabled,
|
||||||
|
}: Pick<Props<T>, 'inputs' | 'useTemplating' | 'autocompleteVariables' | 'stateKey' | 'data'> & {
|
||||||
|
setDataAttr: (name: string, value: JsonPrimitive) => void;
|
||||||
|
disabled?: boolean;
|
||||||
|
}) {
|
||||||
return (
|
return (
|
||||||
<VStack space={3} className="h-full overflow-auto">
|
<VStack space={3} className="h-full overflow-auto">
|
||||||
{config.map((a, i) => {
|
{inputs?.map((input, i) => {
|
||||||
switch (a.type) {
|
if ('hidden' in input && input.hidden) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ('disabled' in input && disabled != null) {
|
||||||
|
input.disabled = disabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (input.type) {
|
||||||
case 'select':
|
case 'select':
|
||||||
return (
|
return (
|
||||||
<SelectArg
|
<SelectArg
|
||||||
key={i + stateKey}
|
key={i + stateKey}
|
||||||
arg={a}
|
arg={input}
|
||||||
onChange={(v) => setDataAttr(a.name, v)}
|
onChange={(v) => setDataAttr(input.name, v)}
|
||||||
value={
|
value={
|
||||||
data[a.name] ? String(data[a.name]) : (a.defaultValue ?? DYNAMIC_FORM_NULL_ARG)
|
data[input.name]
|
||||||
|
? String(data[input.name])
|
||||||
|
: (input.defaultValue ?? DYNAMIC_FORM_NULL_ARG)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
@@ -68,11 +111,13 @@ export function DynamicForm<T extends Record<string, string | boolean>>({
|
|||||||
<TextArg
|
<TextArg
|
||||||
key={i}
|
key={i}
|
||||||
stateKey={stateKey}
|
stateKey={stateKey}
|
||||||
arg={a}
|
arg={input}
|
||||||
useTemplating={useTemplating || false}
|
useTemplating={useTemplating || false}
|
||||||
autocompleteVariables={autocompleteVariables || false}
|
autocompleteVariables={autocompleteVariables || false}
|
||||||
onChange={(v) => setDataAttr(a.name, v)}
|
onChange={(v) => setDataAttr(input.name, v)}
|
||||||
value={data[a.name] ? String(data[a.name]) : (a.defaultValue ?? '')}
|
value={
|
||||||
|
data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '')
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'editor':
|
case 'editor':
|
||||||
@@ -80,40 +125,79 @@ export function DynamicForm<T extends Record<string, string | boolean>>({
|
|||||||
<EditorArg
|
<EditorArg
|
||||||
key={i}
|
key={i}
|
||||||
stateKey={stateKey}
|
stateKey={stateKey}
|
||||||
arg={a}
|
arg={input}
|
||||||
useTemplating={useTemplating || false}
|
useTemplating={useTemplating || false}
|
||||||
autocompleteVariables={autocompleteVariables || false}
|
autocompleteVariables={autocompleteVariables || false}
|
||||||
onChange={(v) => setDataAttr(a.name, v)}
|
onChange={(v) => setDataAttr(input.name, v)}
|
||||||
value={data[a.name] ? String(data[a.name]) : (a.defaultValue ?? '')}
|
value={
|
||||||
|
data[input.name] != null ? String(data[input.name]) : (input.defaultValue ?? '')
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'checkbox':
|
case 'checkbox':
|
||||||
return (
|
return (
|
||||||
<CheckboxArg
|
<CheckboxArg
|
||||||
key={i + stateKey}
|
key={i + stateKey}
|
||||||
arg={a}
|
arg={input}
|
||||||
onChange={(v) => setDataAttr(a.name, v)}
|
onChange={(v) => setDataAttr(input.name, v)}
|
||||||
value={data[a.name] !== undefined ? data[a.name] === true : false}
|
value={data[input.name] != null ? data[input.name] === true : false}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'http_request':
|
case 'http_request':
|
||||||
return (
|
return (
|
||||||
<HttpRequestArg
|
<HttpRequestArg
|
||||||
key={i + stateKey}
|
key={i + stateKey}
|
||||||
arg={a}
|
arg={input}
|
||||||
onChange={(v) => setDataAttr(a.name, v)}
|
onChange={(v) => setDataAttr(input.name, v)}
|
||||||
value={data[a.name] ? String(data[a.name]) : DYNAMIC_FORM_NULL_ARG}
|
value={data[input.name] != null ? String(data[input.name]) : DYNAMIC_FORM_NULL_ARG}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
case 'file':
|
case 'file':
|
||||||
return (
|
return (
|
||||||
<FileArg
|
<FileArg
|
||||||
key={i + stateKey}
|
key={i + stateKey}
|
||||||
arg={a}
|
arg={input}
|
||||||
onChange={(v) => setDataAttr(a.name, v)}
|
onChange={(v) => setDataAttr(input.name, v)}
|
||||||
filePath={data[a.name] ? String(data[a.name]) : DYNAMIC_FORM_NULL_ARG}
|
filePath={
|
||||||
|
data[input.name] != null ? String(data[input.name]) : DYNAMIC_FORM_NULL_ARG
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
case 'accordion':
|
||||||
|
return (
|
||||||
|
<Banner key={i} className={classNames('!p-0', disabled && 'opacity-disabled')}>
|
||||||
|
<details>
|
||||||
|
<summary className="px-3 py-1.5 text-text-subtle">{input.label}</summary>
|
||||||
|
<div className="mb-3 px-3">
|
||||||
|
<FormInputs
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
inputs={input.inputs}
|
||||||
|
setDataAttr={setDataAttr}
|
||||||
|
stateKey={stateKey}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</details>
|
||||||
|
</Banner>
|
||||||
|
);
|
||||||
|
case 'banner':
|
||||||
|
return (
|
||||||
|
<Banner
|
||||||
|
key={i}
|
||||||
|
color={input.color}
|
||||||
|
className={classNames(disabled && 'opacity-disabled')}
|
||||||
|
>
|
||||||
|
<FormInputs
|
||||||
|
data={data}
|
||||||
|
disabled={disabled}
|
||||||
|
inputs={input.inputs}
|
||||||
|
setDataAttr={setDataAttr}
|
||||||
|
stateKey={stateKey}
|
||||||
|
/>
|
||||||
|
</Banner>
|
||||||
|
);
|
||||||
|
case 'markdown':
|
||||||
|
return <Markdown>{input.content}</Markdown>;
|
||||||
}
|
}
|
||||||
})}
|
})}
|
||||||
</VStack>
|
</VStack>
|
||||||
@@ -145,13 +229,17 @@ function TextArg({
|
|||||||
return (
|
return (
|
||||||
<Input
|
<Input
|
||||||
name={arg.name}
|
name={arg.name}
|
||||||
|
multiLine={arg.multiLine}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
|
defaultValue={value === DYNAMIC_FORM_NULL_ARG ? arg.defaultValue : value}
|
||||||
required={!arg.optional}
|
required={!arg.optional}
|
||||||
|
disabled={arg.disabled}
|
||||||
type={arg.password ? 'password' : 'text'}
|
type={arg.password ? 'password' : 'text'}
|
||||||
label={arg.label ?? arg.name}
|
label={arg.label ?? arg.name}
|
||||||
|
size={INPUT_SIZE}
|
||||||
hideLabel={arg.label == null}
|
hideLabel={arg.label == null}
|
||||||
placeholder={arg.placeholder ?? arg.defaultValue ?? ''}
|
placeholder={arg.placeholder ?? arg.defaultValue ?? ''}
|
||||||
|
autocomplete={arg.completionOptions ? { options: arg.completionOptions } : undefined}
|
||||||
useTemplating={useTemplating}
|
useTemplating={useTemplating}
|
||||||
autocompleteVariables={autocompleteVariables}
|
autocompleteVariables={autocompleteVariables}
|
||||||
stateKey={stateKey}
|
stateKey={stateKey}
|
||||||
@@ -184,23 +272,29 @@ function EditorArg({
|
|||||||
|
|
||||||
const id = `input-${arg.name}`;
|
const id = `input-${arg.name}`;
|
||||||
|
|
||||||
|
// Read-only editor force refresh for every defaultValue change
|
||||||
|
// Should this be built into the <Editor/> component?
|
||||||
|
const forceUpdateKey = arg.readOnly ? arg.defaultValue + stateKey : stateKey;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className=" w-full grid grid-cols-1 grid-rows-[auto_minmax(0,1fr)]">
|
<div className=" w-full grid grid-cols-1 grid-rows-[auto_minmax(0,1fr)]">
|
||||||
<Label
|
<Label
|
||||||
htmlFor={id}
|
htmlFor={id}
|
||||||
optional={arg.optional}
|
required={!arg.optional}
|
||||||
visuallyHidden={arg.hideLabel}
|
visuallyHidden={arg.hideLabel}
|
||||||
otherTags={arg.language ? [capitalize(arg.language)] : undefined}
|
tags={arg.language ? [capitalize(arg.language)] : undefined}
|
||||||
>
|
>
|
||||||
{arg.label}
|
{arg.label}
|
||||||
</Label>
|
</Label>
|
||||||
<Editor
|
<Editor
|
||||||
id={id}
|
id={id}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'border border-border rounded-md overflow-hidden px-2 py-1.5',
|
'border border-border rounded-md overflow-hidden px-2 py-1',
|
||||||
'focus-within:border-border-focus',
|
'focus-within:border-border-focus',
|
||||||
'max-h-[15rem]', // So it doesn't take up too much space
|
'max-h-[15rem]', // So it doesn't take up too much space
|
||||||
)}
|
)}
|
||||||
|
autocomplete={arg.completionOptions ? { options: arg.completionOptions } : undefined}
|
||||||
|
disabled={arg.disabled}
|
||||||
language={arg.language}
|
language={arg.language}
|
||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
heightMode="auto"
|
heightMode="auto"
|
||||||
@@ -209,7 +303,7 @@ function EditorArg({
|
|||||||
useTemplating={useTemplating}
|
useTemplating={useTemplating}
|
||||||
autocompleteVariables={autocompleteVariables}
|
autocompleteVariables={autocompleteVariables}
|
||||||
stateKey={stateKey}
|
stateKey={stateKey}
|
||||||
forceUpdateKey={stateKey}
|
forceUpdateKey={forceUpdateKey}
|
||||||
hideGutter
|
hideGutter
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@@ -232,12 +326,9 @@ function SelectArg({
|
|||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
hideLabel={arg.hideLabel}
|
hideLabel={arg.hideLabel}
|
||||||
value={value}
|
value={value}
|
||||||
options={[
|
size={INPUT_SIZE}
|
||||||
...arg.options.map((a) => ({
|
disabled={arg.disabled}
|
||||||
label: a.name,
|
options={arg.options}
|
||||||
value: a.value,
|
|
||||||
})),
|
|
||||||
]}
|
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@@ -253,6 +344,7 @@ function FileArg({
|
|||||||
}) {
|
}) {
|
||||||
return (
|
return (
|
||||||
<SelectFile
|
<SelectFile
|
||||||
|
disabled={arg.disabled}
|
||||||
onChange={({ filePath }) => onChange(filePath)}
|
onChange={({ filePath }) => onChange(filePath)}
|
||||||
filePath={filePath === '__NULL__' ? null : filePath}
|
filePath={filePath === '__NULL__' ? null : filePath}
|
||||||
directory={!!arg.directory}
|
directory={!!arg.directory}
|
||||||
@@ -278,6 +370,7 @@ function HttpRequestArg({
|
|||||||
name={arg.name}
|
name={arg.name}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={value}
|
value={value}
|
||||||
|
disabled={arg.disabled}
|
||||||
options={[
|
options={[
|
||||||
...httpRequests.map((r) => {
|
...httpRequests.map((r) => {
|
||||||
return {
|
return {
|
||||||
@@ -323,6 +416,7 @@ function CheckboxArg({
|
|||||||
<Checkbox
|
<Checkbox
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
checked={value}
|
checked={value}
|
||||||
|
disabled={arg.disabled}
|
||||||
title={arg.label ?? arg.name}
|
title={arg.label ?? arg.name}
|
||||||
hideLabel={arg.label == null}
|
hideLabel={arg.label == null}
|
||||||
/>
|
/>
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { Environment } from '@yaakapp-internal/models';
|
import type { Environment } from '@yaakapp-internal/models';
|
||||||
|
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import type { ReactNode } from 'react';
|
||||||
import React, { useCallback, useMemo, useState } from 'react';
|
import React, { useCallback, useMemo, useState } from 'react';
|
||||||
@@ -11,10 +12,7 @@ import { showPrompt } from '../lib/prompt';
|
|||||||
import { Banner } from './core/Banner';
|
import { Banner } from './core/Banner';
|
||||||
import { Button } from './core/Button';
|
import { Button } from './core/Button';
|
||||||
import { ContextMenu } from './core/Dropdown';
|
import { ContextMenu } from './core/Dropdown';
|
||||||
import type {
|
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||||
GenericCompletionConfig,
|
|
||||||
GenericCompletionOption,
|
|
||||||
} from './core/Editor/genericCompletion';
|
|
||||||
import { Heading } from './core/Heading';
|
import { Heading } from './core/Heading';
|
||||||
import { Icon } from './core/Icon';
|
import { Icon } from './core/Icon';
|
||||||
import { IconButton } from './core/IconButton';
|
import { IconButton } from './core/IconButton';
|
||||||
@@ -255,7 +253,6 @@ function SidebarButton({
|
|||||||
onClose={() => setShowContextMenu(null)}
|
onClose={() => setShowContextMenu(null)}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: 'rename',
|
|
||||||
label: 'Rename',
|
label: 'Rename',
|
||||||
leftSlot: <Icon icon="pencil" size="sm" />,
|
leftSlot: <Icon icon="pencil" size="sm" />,
|
||||||
onSelect: async () => {
|
onSelect: async () => {
|
||||||
@@ -277,7 +274,6 @@ function SidebarButton({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete-environment',
|
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
leftSlot: <Icon icon="trash" size="sm" />,
|
leftSlot: <Icon icon="trash" size="sm" />,
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import { emit } from '@tauri-apps/api/event';
|
import { emit } from '@tauri-apps/api/event';
|
||||||
import type { PromptTextRequest, PromptTextResponse } from '@yaakapp-internal/plugins';
|
import type { InternalEvent } from '@yaakapp-internal/plugins';
|
||||||
import type { ShowToastRequest } from '@yaakapp/api';
|
import type { ShowToastRequest } from '@yaakapp/api';
|
||||||
import { useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
import { useSubscribeActiveWorkspaceId } from '../hooks/useActiveWorkspace';
|
||||||
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
import { useActiveWorkspaceChangedToast } from '../hooks/useActiveWorkspaceChangedToast';
|
||||||
@@ -12,6 +12,7 @@ import { useSyncModelStores } from '../hooks/useSyncModelStores';
|
|||||||
import { useSyncWorkspaceChildModels } from '../hooks/useSyncWorkspaceChildModels';
|
import { useSyncWorkspaceChildModels } from '../hooks/useSyncWorkspaceChildModels';
|
||||||
import { useSyncZoomSetting } from '../hooks/useSyncZoomSetting';
|
import { useSyncZoomSetting } from '../hooks/useSyncZoomSetting';
|
||||||
import { useSubscribeTemplateFunctions } from '../hooks/useTemplateFunctions';
|
import { useSubscribeTemplateFunctions } from '../hooks/useTemplateFunctions';
|
||||||
|
import { generateId } from '../lib/generateId';
|
||||||
import { showPrompt } from '../lib/prompt';
|
import { showPrompt } from '../lib/prompt';
|
||||||
import { showToast } from '../lib/toast';
|
import { showToast } from '../lib/toast';
|
||||||
|
|
||||||
@@ -36,15 +37,24 @@ export function GlobalHooks() {
|
|||||||
showToast({ ...event.payload });
|
showToast({ ...event.payload });
|
||||||
});
|
});
|
||||||
|
|
||||||
// Listen for prompts
|
// Listen for plugin events
|
||||||
useListenToTauriEvent<{ replyId: string; args: PromptTextRequest }>(
|
useListenToTauriEvent<InternalEvent>('plugin_event', async ({ payload: event }) => {
|
||||||
'show_prompt',
|
if (event.payload.type === 'prompt_text_request') {
|
||||||
async (event) => {
|
const value = await showPrompt(event.payload);
|
||||||
const value = await showPrompt(event.payload.args);
|
const result: InternalEvent = {
|
||||||
const result: PromptTextResponse = { value };
|
id: generateId(),
|
||||||
await emit(event.payload.replyId, result);
|
replyId: event.id,
|
||||||
},
|
pluginName: event.pluginName,
|
||||||
);
|
pluginRefId: event.pluginRefId,
|
||||||
|
windowContext: event.windowContext,
|
||||||
|
payload: {
|
||||||
|
type: 'prompt_text_response',
|
||||||
|
value,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
await emit(event.id, result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -69,13 +69,11 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: 'refresh',
|
|
||||||
label: 'Refetch',
|
label: 'Refetch',
|
||||||
leftSlot: <Icon icon="refresh" />,
|
leftSlot: <Icon icon="refresh" />,
|
||||||
onSelect: refetch,
|
onSelect: refetch,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'clear',
|
|
||||||
label: 'Clear',
|
label: 'Clear',
|
||||||
onSelect: clear,
|
onSelect: clear,
|
||||||
hidden: !schema,
|
hidden: !schema,
|
||||||
@@ -84,7 +82,6 @@ export function GraphQLEditor({ request, onChange, baseRequest, ...extraEditorPr
|
|||||||
},
|
},
|
||||||
{ type: 'separator', label: 'Setting' },
|
{ type: 'separator', label: 'Setting' },
|
||||||
{
|
{
|
||||||
key: 'auto_fetch',
|
|
||||||
label: 'Automatic Introspection',
|
label: 'Automatic Introspection',
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
setAutoIntrospectDisabled({
|
setAutoIntrospectDisabled({
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import type { CSSProperties } from 'react';
|
|||||||
import React, { useCallback, useMemo, useRef } from 'react';
|
import React, { useCallback, useMemo, useRef } from 'react';
|
||||||
import { useContainerSize } from '../hooks/useContainerQuery';
|
import { useContainerSize } from '../hooks/useContainerQuery';
|
||||||
import type { ReflectResponseService } from '../hooks/useGrpc';
|
import type { ReflectResponseService } from '../hooks/useGrpc';
|
||||||
import { useHttpAuthentication } from '../hooks/useHttpAuthentication';
|
import { useHttpAuthenticationSummaries } from '../hooks/useHttpAuthentication';
|
||||||
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
import { useRequestUpdateKey } from '../hooks/useRequestUpdateKey';
|
||||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||||
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
import { fallbackRequestName } from '../lib/fallbackRequestName';
|
||||||
@@ -69,7 +69,7 @@ export function GrpcConnectionSetupPane({
|
|||||||
onSend,
|
onSend,
|
||||||
}: Props) {
|
}: Props) {
|
||||||
const updateRequest = useUpdateAnyGrpcRequest();
|
const updateRequest = useUpdateAnyGrpcRequest();
|
||||||
const authentication = useHttpAuthentication();
|
const authentication = useHttpAuthenticationSummaries();
|
||||||
const [activeTabs, setActiveTabs] = useAtom(tabsAtom);
|
const [activeTabs, setActiveTabs] = useAtom(tabsAtom);
|
||||||
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
||||||
|
|
||||||
@@ -237,7 +237,6 @@ export function GrpcConnectionSetupPane({
|
|||||||
{
|
{
|
||||||
label: 'Refresh',
|
label: 'Refresh',
|
||||||
type: 'default',
|
type: 'default',
|
||||||
key: 'custom',
|
|
||||||
leftSlot: <Icon className="text-text-subtlest" size="sm" icon="refresh" />,
|
leftSlot: <Icon className="text-text-subtlest" size="sm" icon="refresh" />,
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||||
|
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||||
import { charsets } from '../lib/data/charsets';
|
import { charsets } from '../lib/data/charsets';
|
||||||
import { connections } from '../lib/data/connections';
|
import { connections } from '../lib/data/connections';
|
||||||
import { encodings } from '../lib/data/encodings';
|
import { encodings } from '../lib/data/encodings';
|
||||||
@@ -44,7 +45,7 @@ const headerOptionsMap: Record<string, string[]> = {
|
|||||||
|
|
||||||
const valueAutocomplete = (headerName: string): GenericCompletionConfig | undefined => {
|
const valueAutocomplete = (headerName: string): GenericCompletionConfig | undefined => {
|
||||||
const name = headerName.toLowerCase().trim();
|
const name = headerName.toLowerCase().trim();
|
||||||
const options: GenericCompletionConfig['options'] =
|
const options: GenericCompletionOption[] =
|
||||||
headerOptionsMap[name]?.map((o) => ({
|
headerOptionsMap[name]?.map((o) => ({
|
||||||
label: o,
|
label: o,
|
||||||
type: 'constant',
|
type: 'constant',
|
||||||
|
|||||||
@@ -1,8 +1,14 @@
|
|||||||
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||||
import React, { useCallback } from 'react';
|
import React, { useCallback } from 'react';
|
||||||
import { useHttpAuthentication } from '../hooks/useHttpAuthentication';
|
import { useHttpAuthenticationConfig } from '../hooks/useHttpAuthenticationConfig';
|
||||||
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
import { useUpdateAnyGrpcRequest } from '../hooks/useUpdateAnyGrpcRequest';
|
||||||
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
import { useUpdateAnyHttpRequest } from '../hooks/useUpdateAnyHttpRequest';
|
||||||
|
import { Checkbox } from './core/Checkbox';
|
||||||
|
import type { DropdownItem } from './core/Dropdown';
|
||||||
|
import { Dropdown } from './core/Dropdown';
|
||||||
|
import { Icon } from './core/Icon';
|
||||||
|
import { IconButton } from './core/IconButton';
|
||||||
|
import { HStack } from './core/Stacks';
|
||||||
import { DynamicForm } from './DynamicForm';
|
import { DynamicForm } from './DynamicForm';
|
||||||
import { EmptyStateText } from './EmptyStateText';
|
import { EmptyStateText } from './EmptyStateText';
|
||||||
|
|
||||||
@@ -13,11 +19,15 @@ interface Props {
|
|||||||
export function HttpAuthenticationEditor({ request }: Props) {
|
export function HttpAuthenticationEditor({ request }: Props) {
|
||||||
const updateHttpRequest = useUpdateAnyHttpRequest();
|
const updateHttpRequest = useUpdateAnyHttpRequest();
|
||||||
const updateGrpcRequest = useUpdateAnyGrpcRequest();
|
const updateGrpcRequest = useUpdateAnyGrpcRequest();
|
||||||
const auths = useHttpAuthentication();
|
const auth = useHttpAuthenticationConfig(
|
||||||
const auth = auths.find((a) => a.name === request.authenticationType);
|
request.authenticationType,
|
||||||
|
request.authentication,
|
||||||
|
request.id,
|
||||||
|
);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(authentication: Record<string, boolean>) => {
|
(authentication: Record<string, boolean>) => {
|
||||||
|
console.log('UPDATE', authentication);
|
||||||
if (request.model === 'http_request') {
|
if (request.model === 'http_request') {
|
||||||
updateHttpRequest.mutate({
|
updateHttpRequest.mutate({
|
||||||
id: request.id,
|
id: request.id,
|
||||||
@@ -33,18 +43,42 @@ export function HttpAuthenticationEditor({ request }: Props) {
|
|||||||
[request.id, request.model, updateGrpcRequest, updateHttpRequest],
|
[request.id, request.model, updateGrpcRequest, updateHttpRequest],
|
||||||
);
|
);
|
||||||
|
|
||||||
if (auth == null) {
|
if (auth.data == null) {
|
||||||
return <EmptyStateText>No Authentication {request.authenticationType}</EmptyStateText>;
|
return <EmptyStateText>No Authentication {request.authenticationType}</EmptyStateText>;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DynamicForm
|
<div className="h-full grid grid-rows-[auto_minmax(0,1fr)]">
|
||||||
autocompleteVariables
|
<HStack space={2} className="mb-1" alignItems="center">
|
||||||
useTemplating
|
<Checkbox
|
||||||
stateKey={`auth.${request.id}.${request.authenticationType}`}
|
className="w-full"
|
||||||
config={auth.config}
|
checked={!request.authentication.disabled}
|
||||||
data={request.authentication}
|
onChange={(disabled) => handleChange({ ...request.authentication, disabled: !disabled })}
|
||||||
onChange={handleChange}
|
title="Enabled"
|
||||||
/>
|
/>
|
||||||
|
{auth.data.actions && (
|
||||||
|
<Dropdown
|
||||||
|
items={auth.data.actions.map(
|
||||||
|
(a): DropdownItem => ({
|
||||||
|
label: a.label,
|
||||||
|
leftSlot: a.icon ? <Icon icon={a.icon} /> : null,
|
||||||
|
onSelect: () => a.call(request),
|
||||||
|
}),
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<IconButton title="Authentication Actions" icon="settings" size="xs" />
|
||||||
|
</Dropdown>
|
||||||
|
)}
|
||||||
|
</HStack>
|
||||||
|
<DynamicForm
|
||||||
|
disabled={request.authentication.disabled}
|
||||||
|
autocompleteVariables
|
||||||
|
useTemplating
|
||||||
|
stateKey={`auth.${request.id}.${request.authenticationType}`}
|
||||||
|
inputs={auth.data.args}
|
||||||
|
data={request.authentication}
|
||||||
|
onChange={handleChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
import remarkGfm from 'remark-gfm';
|
||||||
|
import ReactMarkdown, { type Components } from 'react-markdown';
|
||||||
|
import { Prose } from './Prose';
|
||||||
|
|
||||||
|
export function Markdown({ children, className }: { children: string; className?: string }) {
|
||||||
|
return (
|
||||||
|
<Prose className={className}>
|
||||||
|
<ReactMarkdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
||||||
|
{children}
|
||||||
|
</ReactMarkdown>
|
||||||
|
</Prose>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const markdownComponents: Partial<Components> = {
|
||||||
|
// Ensure links open in external browser by adding target="_blank"
|
||||||
|
a: ({ href, children, ...rest }) => {
|
||||||
|
if (href && !href.match(/https?:\/\//)) {
|
||||||
|
href = `http://${href}`;
|
||||||
|
}
|
||||||
|
return (
|
||||||
|
<a target="_blank" rel="noreferrer noopener" href={href} {...rest}>
|
||||||
|
{children}
|
||||||
|
</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
};
|
||||||
@@ -1,12 +1,9 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import type { Components } from 'react-markdown';
|
|
||||||
import Markdown from 'react-markdown';
|
|
||||||
import remarkGfm from 'remark-gfm';
|
|
||||||
import type { EditorProps } from './core/Editor/Editor';
|
import type { EditorProps } from './core/Editor/Editor';
|
||||||
import { Editor } from './core/Editor/Editor';
|
import { Editor } from './core/Editor/Editor';
|
||||||
import { Prose } from './Prose';
|
|
||||||
import { SegmentedControl } from './core/SegmentedControl';
|
import { SegmentedControl } from './core/SegmentedControl';
|
||||||
|
import { Markdown } from './Markdown';
|
||||||
|
|
||||||
type ViewMode = 'edit' | 'preview';
|
type ViewMode = 'edit' | 'preview';
|
||||||
|
|
||||||
@@ -47,11 +44,9 @@ export function MarkdownEditor({
|
|||||||
defaultValue.length === 0 ? (
|
defaultValue.length === 0 ? (
|
||||||
<p className="text-text-subtlest">No description</p>
|
<p className="text-text-subtlest">No description</p>
|
||||||
) : (
|
) : (
|
||||||
<Prose className="max-w-xl overflow-y-auto max-h-full [&_*]:cursor-auto [&_*]:select-auto">
|
<Markdown className="max-w-xl overflow-y-auto max-h-full [&_*]:cursor-auto [&_*]:select-auto">
|
||||||
<Markdown remarkPlugins={[remarkGfm]} components={markdownComponents}>
|
{defaultValue}
|
||||||
{defaultValue}
|
</Markdown>
|
||||||
</Markdown>
|
|
||||||
</Prose>
|
|
||||||
);
|
);
|
||||||
|
|
||||||
const contents = viewMode === 'preview' ? preview : editor;
|
const contents = viewMode === 'preview' ? preview : editor;
|
||||||
@@ -88,17 +83,3 @@ export function MarkdownEditor({
|
|||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const markdownComponents: Partial<Components> = {
|
|
||||||
// Ensure links open in external browser by adding target="_blank"
|
|
||||||
a: ({ href, children, ...rest }) => {
|
|
||||||
if (href && !href.match(/https?:\/\//)) {
|
|
||||||
href = `http://${href}`;
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<a target="_blank" rel="noreferrer noopener" href={href} {...rest}>
|
|
||||||
{children}
|
|
||||||
</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|||||||
@@ -5,6 +5,10 @@
|
|||||||
@apply mt-0;
|
@apply mt-0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
& > :last-child {
|
||||||
|
@apply mb-0;
|
||||||
|
}
|
||||||
|
|
||||||
img,
|
img,
|
||||||
video,
|
video,
|
||||||
p,
|
p,
|
||||||
@@ -107,6 +111,7 @@
|
|||||||
ul code {
|
ul code {
|
||||||
@apply text-xs bg-surface-active text-info font-normal whitespace-nowrap;
|
@apply text-xs bg-surface-active text-info font-normal whitespace-nowrap;
|
||||||
@apply px-1.5 py-0.5 rounded not-italic;
|
@apply px-1.5 py-0.5 rounded not-italic;
|
||||||
|
@apply select-text;
|
||||||
}
|
}
|
||||||
|
|
||||||
pre {
|
pre {
|
||||||
|
|||||||
@@ -27,13 +27,11 @@ export function RecentConnectionsDropdown({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: 'clear-single',
|
|
||||||
label: 'Clear Connection',
|
label: 'Clear Connection',
|
||||||
onSelect: deleteConnection.mutate,
|
onSelect: deleteConnection.mutate,
|
||||||
disabled: connections.length === 0,
|
disabled: connections.length === 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'clear-all',
|
|
||||||
label: `Clear ${pluralizeCount('Connection', connections.length)}`,
|
label: `Clear ${pluralizeCount('Connection', connections.length)}`,
|
||||||
onSelect: deleteAllConnections.mutate,
|
onSelect: deleteAllConnections.mutate,
|
||||||
hidden: connections.length <= 1,
|
hidden: connections.length <= 1,
|
||||||
@@ -41,7 +39,6 @@ export function RecentConnectionsDropdown({
|
|||||||
},
|
},
|
||||||
{ type: 'separator', label: 'History' },
|
{ type: 'separator', label: 'History' },
|
||||||
...connections.slice(0, 20).map((c) => ({
|
...connections.slice(0, 20).map((c) => ({
|
||||||
key: c.id,
|
|
||||||
label: (
|
label: (
|
||||||
<HStack space={2}>
|
<HStack space={2}>
|
||||||
{formatDistanceToNowStrict(c.createdAt + 'Z')} ago •{' '}
|
{formatDistanceToNowStrict(c.createdAt + 'Z')} ago •{' '}
|
||||||
|
|||||||
@@ -58,7 +58,6 @@ export function RecentRequestsDropdown({ className }: Props) {
|
|||||||
if (request === undefined) continue;
|
if (request === undefined) continue;
|
||||||
|
|
||||||
recentRequestItems.push({
|
recentRequestItems.push({
|
||||||
key: request.id,
|
|
||||||
label: fallbackRequestName(request),
|
label: fallbackRequestName(request),
|
||||||
// leftSlot: <CountBadge className="!ml-0 px-0 w-5" count={recentRequestItems.length} />,
|
// leftSlot: <CountBadge className="!ml-0 px-0 w-5" count={recentRequestItems.length} />,
|
||||||
leftSlot: <HttpMethodTag className="text-right" shortNames request={request} />,
|
leftSlot: <HttpMethodTag className="text-right" shortNames request={request} />,
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ export const RecentResponsesDropdown = function ResponsePane({
|
|||||||
<Dropdown
|
<Dropdown
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: 'save',
|
|
||||||
label: 'Save to File',
|
label: 'Save to File',
|
||||||
onSelect: saveResponse.mutate,
|
onSelect: saveResponse.mutate,
|
||||||
leftSlot: <Icon icon="save" />,
|
leftSlot: <Icon icon="save" />,
|
||||||
@@ -40,7 +39,6 @@ export const RecentResponsesDropdown = function ResponsePane({
|
|||||||
disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100,
|
disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'copy',
|
|
||||||
label: 'Copy Body',
|
label: 'Copy Body',
|
||||||
onSelect: copyResponse.mutate,
|
onSelect: copyResponse.mutate,
|
||||||
leftSlot: <Icon icon="copy" />,
|
leftSlot: <Icon icon="copy" />,
|
||||||
@@ -48,13 +46,11 @@ export const RecentResponsesDropdown = function ResponsePane({
|
|||||||
disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100,
|
disabled: activeResponse.state !== 'closed' && activeResponse.status >= 100,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'clear-single',
|
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
onSelect: deleteResponse.mutate,
|
onSelect: deleteResponse.mutate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'unpin',
|
|
||||||
label: 'Unpin Response',
|
label: 'Unpin Response',
|
||||||
onSelect: () => onPinnedResponseId(activeResponse.id),
|
onSelect: () => onPinnedResponseId(activeResponse.id),
|
||||||
leftSlot: <Icon icon="unpin" />,
|
leftSlot: <Icon icon="unpin" />,
|
||||||
@@ -63,7 +59,6 @@ export const RecentResponsesDropdown = function ResponsePane({
|
|||||||
},
|
},
|
||||||
{ type: 'separator', label: 'History' },
|
{ type: 'separator', label: 'History' },
|
||||||
{
|
{
|
||||||
key: 'clear-all',
|
|
||||||
label: `Delete ${responses.length} ${pluralize('Response', responses.length)}`,
|
label: `Delete ${responses.length} ${pluralize('Response', responses.length)}`,
|
||||||
onSelect: deleteAllResponses.mutate,
|
onSelect: deleteAllResponses.mutate,
|
||||||
hidden: responses.length === 0,
|
hidden: responses.length === 0,
|
||||||
@@ -71,7 +66,6 @@ export const RecentResponsesDropdown = function ResponsePane({
|
|||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
...responses.slice(0, 20).map((r: HttpResponse) => ({
|
...responses.slice(0, 20).map((r: HttpResponse) => ({
|
||||||
key: r.id,
|
|
||||||
label: (
|
label: (
|
||||||
<HStack space={2}>
|
<HStack space={2}>
|
||||||
<StatusTag className="text-sm" response={r} />
|
<StatusTag className="text-sm" response={r} />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import type { HttpRequest } from '@yaakapp-internal/models';
|
import type { HttpRequest } from '@yaakapp-internal/models';
|
||||||
|
import type {GenericCompletionOption} from "@yaakapp-internal/plugins";
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { atom, useAtom, useAtomValue } from 'jotai';
|
import { atom, useAtom, useAtomValue } from 'jotai';
|
||||||
import { atomWithStorage } from 'jotai/utils';
|
import { atomWithStorage } from 'jotai/utils';
|
||||||
@@ -8,7 +9,7 @@ import { activeRequestIdAtom } from '../hooks/useActiveRequestId';
|
|||||||
import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse';
|
import { useCancelHttpResponse } from '../hooks/useCancelHttpResponse';
|
||||||
import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders';
|
import { useContentTypeFromHeaders } from '../hooks/useContentTypeFromHeaders';
|
||||||
import { grpcRequestsAtom } from '../hooks/useGrpcRequests';
|
import { grpcRequestsAtom } from '../hooks/useGrpcRequests';
|
||||||
import { useHttpAuthentication } from '../hooks/useHttpAuthentication';
|
import { useHttpAuthenticationSummaries } from '../hooks/useHttpAuthentication';
|
||||||
import { httpRequestsAtom } from '../hooks/useHttpRequests';
|
import { httpRequestsAtom } from '../hooks/useHttpRequests';
|
||||||
import { useImportCurl } from '../hooks/useImportCurl';
|
import { useImportCurl } from '../hooks/useImportCurl';
|
||||||
import { useImportQuerystring } from '../hooks/useImportQuerystring';
|
import { useImportQuerystring } from '../hooks/useImportQuerystring';
|
||||||
@@ -36,10 +37,7 @@ import { showToast } from '../lib/toast';
|
|||||||
import { BinaryFileEditor } from './BinaryFileEditor';
|
import { BinaryFileEditor } from './BinaryFileEditor';
|
||||||
import { CountBadge } from './core/CountBadge';
|
import { CountBadge } from './core/CountBadge';
|
||||||
import { Editor } from './core/Editor/Editor';
|
import { Editor } from './core/Editor/Editor';
|
||||||
import type {
|
import type { GenericCompletionConfig } from './core/Editor/genericCompletion';
|
||||||
GenericCompletionConfig,
|
|
||||||
GenericCompletionOption,
|
|
||||||
} from './core/Editor/genericCompletion';
|
|
||||||
import { InlineCode } from './core/InlineCode';
|
import { InlineCode } from './core/InlineCode';
|
||||||
import type { Pair } from './core/PairEditor';
|
import type { Pair } from './core/PairEditor';
|
||||||
import { PlainInput } from './core/PlainInput';
|
import { PlainInput } from './core/PlainInput';
|
||||||
@@ -93,7 +91,7 @@ export const RequestPane = memo(function RequestPane({
|
|||||||
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
const { updateKey: forceUpdateKey } = useRequestUpdateKey(activeRequest.id ?? null);
|
||||||
const [{ urlKey }] = useRequestEditor();
|
const [{ urlKey }] = useRequestEditor();
|
||||||
const contentType = useContentTypeFromHeaders(activeRequest.headers);
|
const contentType = useContentTypeFromHeaders(activeRequest.headers);
|
||||||
const authentication = useHttpAuthentication();
|
const authentication = useHttpAuthenticationSummaries();
|
||||||
|
|
||||||
const handleContentTypeChange = useCallback(
|
const handleContentTypeChange = useCallback(
|
||||||
async (contentType: string | null) => {
|
async (contentType: string | null) => {
|
||||||
|
|||||||
@@ -28,14 +28,12 @@ export function SettingsDropdown() {
|
|||||||
ref={dropdownRef}
|
ref={dropdownRef}
|
||||||
items={[
|
items={[
|
||||||
{
|
{
|
||||||
key: 'settings',
|
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
hotKeyAction: 'settings.show',
|
hotKeyAction: 'settings.show',
|
||||||
leftSlot: <Icon icon="settings" />,
|
leftSlot: <Icon icon="settings" />,
|
||||||
onSelect: openSettings.mutate,
|
onSelect: openSettings.mutate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'hotkeys',
|
|
||||||
label: 'Keyboard shortcuts',
|
label: 'Keyboard shortcuts',
|
||||||
hotKeyAction: 'hotkeys.showHelp',
|
hotKeyAction: 'hotkeys.showHelp',
|
||||||
leftSlot: <Icon icon="keyboard" />,
|
leftSlot: <Icon icon="keyboard" />,
|
||||||
@@ -49,33 +47,28 @@ export function SettingsDropdown() {
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'import-data',
|
|
||||||
label: 'Import Data',
|
label: 'Import Data',
|
||||||
leftSlot: <Icon icon="folder_input" />,
|
leftSlot: <Icon icon="folder_input" />,
|
||||||
onSelect: () => importData.mutate(),
|
onSelect: () => importData.mutate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'export-data',
|
|
||||||
label: 'Export Data',
|
label: 'Export Data',
|
||||||
leftSlot: <Icon icon="folder_output" />,
|
leftSlot: <Icon icon="folder_output" />,
|
||||||
onSelect: () => exportData.mutate(),
|
onSelect: () => exportData.mutate(),
|
||||||
},
|
},
|
||||||
{ type: 'separator', label: `Yaak v${appInfo.version}` },
|
{ type: 'separator', label: `Yaak v${appInfo.version}` },
|
||||||
{
|
{
|
||||||
key: 'update-check',
|
|
||||||
label: 'Check for Updates',
|
label: 'Check for Updates',
|
||||||
leftSlot: <Icon icon="update" />,
|
leftSlot: <Icon icon="update" />,
|
||||||
onSelect: () => checkForUpdates.mutate(),
|
onSelect: () => checkForUpdates.mutate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'feedback',
|
|
||||||
label: 'Feedback',
|
label: 'Feedback',
|
||||||
leftSlot: <Icon icon="chat" />,
|
leftSlot: <Icon icon="chat" />,
|
||||||
rightSlot: <Icon icon="external_link" />,
|
rightSlot: <Icon icon="external_link" />,
|
||||||
onSelect: () => openUrl('https://yaak.app/roadmap'),
|
onSelect: () => openUrl('https://yaak.app/roadmap'),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'changelog',
|
|
||||||
label: 'Changelog',
|
label: 'Changelog',
|
||||||
leftSlot: <Icon icon="cake" />,
|
leftSlot: <Icon icon="cake" />,
|
||||||
rightSlot: <Icon icon="external_link" />,
|
rightSlot: <Icon icon="external_link" />,
|
||||||
|
|||||||
@@ -46,13 +46,11 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
if (child.model === 'folder') {
|
if (child.model === 'folder') {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: 'send-all',
|
|
||||||
label: 'Send All',
|
label: 'Send All',
|
||||||
leftSlot: <Icon icon="send_horizontal" />,
|
leftSlot: <Icon icon="send_horizontal" />,
|
||||||
onSelect: () => sendManyRequests.mutate(child.children.map((c) => c.id)),
|
onSelect: () => sendManyRequests.mutate(child.children.map((c) => c.id)),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'folder-settings',
|
|
||||||
label: 'Settings',
|
label: 'Settings',
|
||||||
leftSlot: <Icon icon="settings" />,
|
leftSlot: <Icon icon="settings" />,
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
@@ -64,13 +62,11 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'duplicateFolder',
|
|
||||||
label: 'Duplicate',
|
label: 'Duplicate',
|
||||||
leftSlot: <Icon icon="copy" />,
|
leftSlot: <Icon icon="copy" />,
|
||||||
onSelect: () => duplicateFolder.mutate(),
|
onSelect: () => duplicateFolder.mutate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete-folder',
|
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
leftSlot: <Icon icon="trash" />,
|
leftSlot: <Icon icon="trash" />,
|
||||||
@@ -84,7 +80,6 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
child.model === 'http_request'
|
child.model === 'http_request'
|
||||||
? [
|
? [
|
||||||
{
|
{
|
||||||
key: 'send-request',
|
|
||||||
label: 'Send',
|
label: 'Send',
|
||||||
hotKeyAction: 'http_request.send',
|
hotKeyAction: 'http_request.send',
|
||||||
hotKeyLabelOnly: true, // Already bound in URL bar
|
hotKeyLabelOnly: true, // Already bound in URL bar
|
||||||
@@ -92,7 +87,6 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
onSelect: () => sendRequest.mutate(child.id),
|
onSelect: () => sendRequest.mutate(child.id),
|
||||||
},
|
},
|
||||||
...httpRequestActions.map((a) => ({
|
...httpRequestActions.map((a) => ({
|
||||||
key: a.key,
|
|
||||||
label: a.label,
|
label: a.label,
|
||||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
leftSlot: <Icon icon={(a.icon as any) ?? 'empty'} />,
|
leftSlot: <Icon icon={(a.icon as any) ?? 'empty'} />,
|
||||||
@@ -107,13 +101,11 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
return [
|
return [
|
||||||
...requestItems,
|
...requestItems,
|
||||||
{
|
{
|
||||||
key: 'rename-request',
|
|
||||||
label: 'Rename',
|
label: 'Rename',
|
||||||
leftSlot: <Icon icon="pencil" />,
|
leftSlot: <Icon icon="pencil" />,
|
||||||
onSelect: renameRequest.mutate,
|
onSelect: renameRequest.mutate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'duplicate-request',
|
|
||||||
label: 'Duplicate',
|
label: 'Duplicate',
|
||||||
hotKeyAction: 'http_request.duplicate',
|
hotKeyAction: 'http_request.duplicate',
|
||||||
hotKeyLabelOnly: true, // Would trigger for every request (bad)
|
hotKeyLabelOnly: true, // Would trigger for every request (bad)
|
||||||
@@ -124,14 +116,12 @@ export function SidebarItemContextMenu({ child, show, close }: Props) {
|
|||||||
: duplicateGrpcRequest.mutate(),
|
: duplicateGrpcRequest.mutate(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'move-workspace',
|
|
||||||
label: 'Move',
|
label: 'Move',
|
||||||
leftSlot: <Icon icon="arrow_right_circle" />,
|
leftSlot: <Icon icon="arrow_right_circle" />,
|
||||||
hidden: workspaces.length <= 1,
|
hidden: workspaces.length <= 1,
|
||||||
onSelect: moveToWorkspace.mutate,
|
onSelect: moveToWorkspace.mutate,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete-request',
|
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
hotKeyAction: 'http_request.delete',
|
hotKeyAction: 'http_request.delete',
|
||||||
|
|||||||
@@ -25,6 +25,10 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
|||||||
? initialTokens.tokens[0]?.val.args
|
? initialTokens.tokens[0]?.val.args
|
||||||
: [];
|
: [];
|
||||||
for (const arg of templateFunction.args) {
|
for (const arg of templateFunction.args) {
|
||||||
|
if (!('name' in arg)) {
|
||||||
|
// Skip visual-only args
|
||||||
|
continue;
|
||||||
|
}
|
||||||
const initialArg = initialArgs.find((a) => a.name === arg.name);
|
const initialArg = initialArgs.find((a) => a.name === arg.name);
|
||||||
const initialArgValue =
|
const initialArgValue =
|
||||||
initialArg?.value.type === 'str'
|
initialArg?.value.type === 'str'
|
||||||
@@ -79,7 +83,7 @@ export function TemplateFunctionDialog({ templateFunction, hide, initialTokens,
|
|||||||
<VStack className="pb-3" space={4}>
|
<VStack className="pb-3" space={4}>
|
||||||
<h1 className="font-mono !text-base">{templateFunction.name}(…)</h1>
|
<h1 className="font-mono !text-base">{templateFunction.name}(…)</h1>
|
||||||
<DynamicForm
|
<DynamicForm
|
||||||
config={templateFunction.args}
|
inputs={templateFunction.args}
|
||||||
data={argValues}
|
data={argValues}
|
||||||
onChange={setArgValues}
|
onChange={setArgValues}
|
||||||
stateKey={`template_function.${templateFunction.name}`}
|
stateKey={`template_function.${templateFunction.name}`}
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
|
|
||||||
const extraItems: DropdownItem[] = [
|
const extraItems: DropdownItem[] = [
|
||||||
{
|
{
|
||||||
key: 'workspace-settings',
|
|
||||||
label: 'Workspace Settings',
|
label: 'Workspace Settings',
|
||||||
leftSlot: <Icon icon="settings" />,
|
leftSlot: <Icon icon="settings" />,
|
||||||
hotKeyAction: 'workspace_settings.show',
|
hotKeyAction: 'workspace_settings.show',
|
||||||
@@ -62,7 +61,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'reveal-workspace-sync-dir',
|
|
||||||
label: revealInFinderText,
|
label: revealInFinderText,
|
||||||
hidden: workspaceMeta == null || workspaceMeta.settingSyncDir == null,
|
hidden: workspaceMeta == null || workspaceMeta.settingSyncDir == null,
|
||||||
leftSlot: <Icon icon="folder_open" />,
|
leftSlot: <Icon icon="folder_open" />,
|
||||||
@@ -72,7 +70,6 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete-responses',
|
|
||||||
label: 'Clear Send History',
|
label: 'Clear Send History',
|
||||||
color: 'warning',
|
color: 'warning',
|
||||||
leftSlot: <Icon icon="history" />,
|
leftSlot: <Icon icon="history" />,
|
||||||
@@ -80,13 +77,11 @@ export const WorkspaceActionsDropdown = memo(function WorkspaceActionsDropdown({
|
|||||||
},
|
},
|
||||||
{ type: 'separator' },
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
key: 'create-workspace',
|
|
||||||
label: 'New Workspace',
|
label: 'New Workspace',
|
||||||
leftSlot: <Icon icon="plus" />,
|
leftSlot: <Icon icon="plus" />,
|
||||||
onSelect: createWorkspace,
|
onSelect: createWorkspace,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'open-workspace',
|
|
||||||
label: 'Open Workspace',
|
label: 'Open Workspace',
|
||||||
leftSlot: <Icon icon="folder" />,
|
leftSlot: <Icon icon="folder" />,
|
||||||
onSelect: openWorkspaceFromSyncDir.mutate,
|
onSelect: openWorkspaceFromSyncDir.mutate,
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ interface Props {
|
|||||||
color?: 'primary' | 'secondary' | 'success' | 'notice' | 'warning' | 'danger' | 'info';
|
color?: 'primary' | 'secondary' | 'success' | 'notice' | 'warning' | 'danger' | 'info';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Banner({ children, className, color = 'secondary' }: Props) {
|
export function Banner({ children, className, color }: Props) {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<div
|
<div
|
||||||
@@ -16,7 +16,7 @@ export function Banner({ children, className, color = 'secondary' }: Props) {
|
|||||||
`x-theme-banner--${color}`,
|
`x-theme-banner--${color}`,
|
||||||
'whitespace-pre-wrap',
|
'whitespace-pre-wrap',
|
||||||
'border border-dashed border-border bg-surface',
|
'border border-dashed border-border bg-surface',
|
||||||
'px-3 py-2 rounded select-auto cursor-text',
|
'px-3 py-2 rounded select-auto',
|
||||||
'overflow-x-auto text-text',
|
'overflow-x-auto text-text',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { ReactNode } from 'react';
|
import { type ReactNode } from 'react';
|
||||||
import { trackEvent } from '../../lib/analytics';
|
import { trackEvent } from '../../lib/analytics';
|
||||||
import { Icon } from './Icon';
|
import { Icon } from './Icon';
|
||||||
import { HStack } from './Stacks';
|
import { HStack } from './Stacks';
|
||||||
@@ -26,17 +26,15 @@ export function Checkbox({
|
|||||||
event,
|
event,
|
||||||
}: CheckboxProps) {
|
}: CheckboxProps) {
|
||||||
return (
|
return (
|
||||||
<HStack
|
<HStack as="label" space={2} className={classNames(className, 'text-text mr-auto')}>
|
||||||
as="label"
|
|
||||||
space={2}
|
|
||||||
className={classNames(className, 'text-text mr-auto', disabled && 'opacity-disabled')}
|
|
||||||
>
|
|
||||||
<div className={classNames(inputWrapperClassName, 'x-theme-input', 'relative flex')}>
|
<div className={classNames(inputWrapperClassName, 'x-theme-input', 'relative flex')}>
|
||||||
<input
|
<input
|
||||||
aria-hidden
|
aria-hidden
|
||||||
className={classNames(
|
className={classNames(
|
||||||
'appearance-none w-4 h-4 flex-shrink-0 border border-border',
|
'appearance-none w-4 h-4 flex-shrink-0 border border-border',
|
||||||
'rounded hocus:border-border-focus hocus:bg-focus/[5%] outline-none ring-0',
|
'rounded outline-none ring-0',
|
||||||
|
!disabled && 'hocus:border-border-focus hocus:bg-focus/[5%] ',
|
||||||
|
disabled && 'border-dotted',
|
||||||
)}
|
)}
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
disabled={disabled}
|
disabled={disabled}
|
||||||
@@ -54,7 +52,7 @@ export function Checkbox({
|
|||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{!hideLabel && title}
|
<span className={classNames(disabled && 'opacity-disabled')}>{!hideLabel && title}</span>
|
||||||
</HStack>
|
</HStack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,6 @@ export type DropdownItemSeparator = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export type DropdownItemDefault = {
|
export type DropdownItemDefault = {
|
||||||
key: string;
|
|
||||||
type?: 'default';
|
type?: 'default';
|
||||||
label: ReactNode;
|
label: ReactNode;
|
||||||
keepOpen?: boolean;
|
keepOpen?: boolean;
|
||||||
@@ -465,11 +464,12 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle' | 'items'
|
|||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{items.map(
|
{items.map(
|
||||||
(item) =>
|
(item, i) =>
|
||||||
item.type !== 'separator' &&
|
item.type !== 'separator' &&
|
||||||
!item.hotKeyLabelOnly && (
|
!item.hotKeyLabelOnly &&
|
||||||
|
item.hotKeyAction && (
|
||||||
<MenuItemHotKey
|
<MenuItemHotKey
|
||||||
key={item.key}
|
key={`${item.hotKeyAction}::${i}`}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
item={item}
|
item={item}
|
||||||
action={item.hotKeyAction}
|
action={item.hotKeyAction}
|
||||||
@@ -542,7 +542,7 @@ const Menu = forwardRef<Omit<DropdownRef, 'open' | 'isOpen' | 'toggle' | 'items'
|
|||||||
focused={i === selectedIndex}
|
focused={i === selectedIndex}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onSelect={handleSelect}
|
onSelect={handleSelect}
|
||||||
key={item.key}
|
key={`item_${i}`}
|
||||||
item={item}
|
item={item}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -5,6 +5,7 @@
|
|||||||
@apply w-full block text-base;
|
@apply w-full block text-base;
|
||||||
|
|
||||||
/* Regular cursor */
|
/* Regular cursor */
|
||||||
|
|
||||||
.cm-cursor {
|
.cm-cursor {
|
||||||
@apply border-text !important;
|
@apply border-text !important;
|
||||||
/* Widen the cursor a bit */
|
/* Widen the cursor a bit */
|
||||||
@@ -12,6 +13,7 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
/* Vim-mode cursor */
|
/* Vim-mode cursor */
|
||||||
|
|
||||||
.cm-fat-cursor {
|
.cm-fat-cursor {
|
||||||
@apply outline-0 bg-text !important;
|
@apply outline-0 bg-text !important;
|
||||||
@apply text-surface !important;
|
@apply text-surface !important;
|
||||||
@@ -181,7 +183,7 @@
|
|||||||
@apply hidden !important;
|
@apply hidden !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
&.cm-singleline .cm-line {
|
&.cm-singleline * {
|
||||||
@apply cursor-default;
|
@apply cursor-default;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { defaultKeymap, historyField } from '@codemirror/commands';
|
import { defaultKeymap, historyField, indentWithTab } from '@codemirror/commands';
|
||||||
import { foldState, forceParsing } from '@codemirror/language';
|
import { foldState, forceParsing } from '@codemirror/language';
|
||||||
import type { EditorStateConfig, Extension } from '@codemirror/state';
|
import type { EditorStateConfig, Extension } from '@codemirror/state';
|
||||||
import { Compartment, EditorState } from '@codemirror/state';
|
import { Compartment, EditorState } from '@codemirror/state';
|
||||||
@@ -34,14 +34,22 @@ import { TemplateVariableDialog } from '../../TemplateVariableDialog';
|
|||||||
import { IconButton } from '../IconButton';
|
import { IconButton } from '../IconButton';
|
||||||
import { HStack } from '../Stacks';
|
import { HStack } from '../Stacks';
|
||||||
import './Editor.css';
|
import './Editor.css';
|
||||||
import { baseExtensions, getLanguageExtension, multiLineExtensions } from './extensions';
|
import {
|
||||||
|
baseExtensions,
|
||||||
|
getLanguageExtension,
|
||||||
|
multiLineExtensions,
|
||||||
|
readonlyExtensions,
|
||||||
|
} from './extensions';
|
||||||
import type { GenericCompletionConfig } from './genericCompletion';
|
import type { GenericCompletionConfig } from './genericCompletion';
|
||||||
import { singleLineExtensions } from './singleLine';
|
import { singleLineExtensions } from './singleLine';
|
||||||
|
|
||||||
|
// VSCode's Tab actions mess with the single-line editor tab actions, so remove it.
|
||||||
|
const vsCodeWithoutTab = vscodeKeymap.filter((k) => k.key !== 'Tab');
|
||||||
|
|
||||||
const keymapExtensions: Record<EditorKeymap, Extension> = {
|
const keymapExtensions: Record<EditorKeymap, Extension> = {
|
||||||
vim: vim(),
|
vim: vim(),
|
||||||
emacs: emacs(),
|
emacs: emacs(),
|
||||||
vscode: keymap.of(vscodeKeymap),
|
vscode: keymap.of(vsCodeWithoutTab),
|
||||||
default: [],
|
default: [],
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -68,6 +76,7 @@ export interface EditorProps {
|
|||||||
onKeyDown?: (e: KeyboardEvent) => void;
|
onKeyDown?: (e: KeyboardEvent) => void;
|
||||||
singleLine?: boolean;
|
singleLine?: boolean;
|
||||||
wrapLines?: boolean;
|
wrapLines?: boolean;
|
||||||
|
disableTabIndent?: boolean;
|
||||||
format?: (v: string) => Promise<string>;
|
format?: (v: string) => Promise<string>;
|
||||||
autocomplete?: GenericCompletionConfig;
|
autocomplete?: GenericCompletionConfig;
|
||||||
autocompleteVariables?: boolean;
|
autocompleteVariables?: boolean;
|
||||||
@@ -85,9 +94,9 @@ const emptyExtension: Extension = [];
|
|||||||
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
export const Editor = forwardRef<EditorView | undefined, EditorProps>(function Editor(
|
||||||
{
|
{
|
||||||
readOnly,
|
readOnly,
|
||||||
type = 'text',
|
type,
|
||||||
heightMode,
|
heightMode,
|
||||||
language = 'text',
|
language,
|
||||||
autoFocus,
|
autoFocus,
|
||||||
autoSelect,
|
autoSelect,
|
||||||
placeholder,
|
placeholder,
|
||||||
@@ -101,6 +110,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
onBlur,
|
onBlur,
|
||||||
onKeyDown,
|
onKeyDown,
|
||||||
className,
|
className,
|
||||||
|
disabled,
|
||||||
singleLine,
|
singleLine,
|
||||||
format,
|
format,
|
||||||
autocomplete,
|
autocomplete,
|
||||||
@@ -108,6 +118,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
autocompleteVariables,
|
autocompleteVariables,
|
||||||
actions,
|
actions,
|
||||||
wrapLines,
|
wrapLines,
|
||||||
|
disableTabIndent,
|
||||||
hideGutter,
|
hideGutter,
|
||||||
stateKey,
|
stateKey,
|
||||||
}: EditorProps,
|
}: EditorProps,
|
||||||
@@ -122,6 +133,20 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
wrapLines = settings.editorSoftWrap;
|
wrapLines = settings.editorSoftWrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (disabled) {
|
||||||
|
readOnly = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
singleLine ||
|
||||||
|
language == null ||
|
||||||
|
language === 'text' ||
|
||||||
|
language === 'url' ||
|
||||||
|
language === 'pairs'
|
||||||
|
) {
|
||||||
|
disableTabIndent = true;
|
||||||
|
}
|
||||||
|
|
||||||
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
|
const cm = useRef<{ view: EditorView; languageCompartment: Compartment } | null>(null);
|
||||||
useImperativeHandle(ref, () => cm.current?.view, []);
|
useImperativeHandle(ref, () => cm.current?.view, []);
|
||||||
|
|
||||||
@@ -166,7 +191,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
useEffect(
|
useEffect(
|
||||||
function configurePlaceholder() {
|
function configurePlaceholder() {
|
||||||
if (cm.current === null) return;
|
if (cm.current === null) return;
|
||||||
const ext = placeholderExt(placeholderElFromText(placeholder ?? '', type));
|
const ext = placeholderExt(placeholderElFromText(placeholder, type));
|
||||||
const effects = placeholderCompartment.current.reconfigure(ext);
|
const effects = placeholderCompartment.current.reconfigure(ext);
|
||||||
cm.current?.view.dispatch({ effects });
|
cm.current?.view.dispatch({ effects });
|
||||||
},
|
},
|
||||||
@@ -209,6 +234,23 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
[wrapLines],
|
[wrapLines],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
// Update tab indent
|
||||||
|
const tabIndentCompartment = useRef(new Compartment());
|
||||||
|
useEffect(
|
||||||
|
function configureTabIndent() {
|
||||||
|
if (cm.current === null) return;
|
||||||
|
const current = tabIndentCompartment.current.get(cm.current.view.state) ?? emptyExtension;
|
||||||
|
// PERF: This is expensive with hundreds of editors on screen, so only do it when necessary
|
||||||
|
if (disableTabIndent && current !== emptyExtension) return; // Nothing to do
|
||||||
|
if (!disableTabIndent && current === emptyExtension) return; // Nothing to do
|
||||||
|
|
||||||
|
const ext = !disableTabIndent ? keymap.of([indentWithTab]) : emptyExtension;
|
||||||
|
const effects = tabIndentCompartment.current.reconfigure(ext);
|
||||||
|
cm.current?.view.dispatch({ effects });
|
||||||
|
},
|
||||||
|
[disableTabIndent],
|
||||||
|
);
|
||||||
|
|
||||||
const onClickFunction = useCallback(
|
const onClickFunction = useCallback(
|
||||||
async (fn: TemplateFunction, tagValue: string, startPos: number) => {
|
async (fn: TemplateFunction, tagValue: string, startPos: number) => {
|
||||||
const initialTokens = await parseTemplate(tagValue);
|
const initialTokens = await parseTemplate(tagValue);
|
||||||
@@ -342,9 +384,12 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
const extensions = [
|
const extensions = [
|
||||||
languageCompartment.of(langExt),
|
languageCompartment.of(langExt),
|
||||||
placeholderCompartment.current.of(
|
placeholderCompartment.current.of(
|
||||||
placeholderExt(placeholderElFromText(placeholder ?? '', type)),
|
placeholderExt(placeholderElFromText(placeholder, type)),
|
||||||
),
|
),
|
||||||
wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : emptyExtension),
|
wrapLinesCompartment.current.of(wrapLines ? EditorView.lineWrapping : emptyExtension),
|
||||||
|
tabIndentCompartment.current.of(
|
||||||
|
!disableTabIndent ? keymap.of([indentWithTab]) : emptyExtension,
|
||||||
|
),
|
||||||
keymapCompartment.current.of(
|
keymapCompartment.current.of(
|
||||||
keymapExtensions[settings.editorKeymap] ?? keymapExtensions['default'],
|
keymapExtensions[settings.editorKeymap] ?? keymapExtensions['default'],
|
||||||
),
|
),
|
||||||
@@ -475,6 +520,7 @@ export const Editor = forwardRef<EditorView | undefined, EditorProps>(function E
|
|||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
'cm-wrapper text-base',
|
'cm-wrapper text-base',
|
||||||
|
disabled && 'opacity-disabled',
|
||||||
type === 'password' && 'cm-obscure-text',
|
type === 'password' && 'cm-obscure-text',
|
||||||
heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height',
|
heightMode === 'auto' ? 'cm-auto-height' : 'cm-full-height',
|
||||||
singleLine ? 'cm-singleline' : 'cm-multiline',
|
singleLine ? 'cm-singleline' : 'cm-multiline',
|
||||||
@@ -557,10 +603,8 @@ function getExtensions({
|
|||||||
tooltips({ parent }),
|
tooltips({ parent }),
|
||||||
keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap),
|
keymap.of(singleLine ? defaultKeymap.filter((k) => k.key !== 'Enter') : defaultKeymap),
|
||||||
...(singleLine ? [singleLineExtensions()] : []),
|
...(singleLine ? [singleLineExtensions()] : []),
|
||||||
...(!singleLine ? [multiLineExtensions({ hideGutter })] : []),
|
...(!singleLine ? multiLineExtensions({ hideGutter }) : []),
|
||||||
...(readOnly
|
...(readOnly ? readonlyExtensions : []),
|
||||||
? [EditorState.readOnly.of(true), EditorView.contentAttributes.of({ tabindex: '-1' })]
|
|
||||||
: []),
|
|
||||||
|
|
||||||
// ------------------------ //
|
// ------------------------ //
|
||||||
// Things that must be last //
|
// Things that must be last //
|
||||||
@@ -580,13 +624,15 @@ function getExtensions({
|
|||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
const placeholderElFromText = (text: string, type: EditorProps['type']) => {
|
const placeholderElFromText = (text: string | undefined, type: EditorProps['type']) => {
|
||||||
const el = document.createElement('div');
|
const el = document.createElement('div');
|
||||||
if (type === 'password') {
|
if (type === 'password') {
|
||||||
// Will be obscured (dots) so just needs to be something to take up space
|
// Will be obscured (dots) so just needs to be something to take up space
|
||||||
el.innerHTML = 'something-cool';
|
el.innerHTML = 'something-cool';
|
||||||
el.setAttribute('aria-hidden', 'true');
|
el.setAttribute('aria-hidden', 'true');
|
||||||
} else {
|
} else {
|
||||||
|
// Default to <SPACE> because codemirror needs it for sizing. I'm not sure why, but probably something
|
||||||
|
// to do with how Yaak "hacks" it with CSS for single line input.
|
||||||
el.innerHTML = text ? text.replaceAll('\n', '<br/>') : ' ';
|
el.innerHTML = text ? text.replaceAll('\n', '<br/>') : ' ';
|
||||||
}
|
}
|
||||||
return el;
|
return el;
|
||||||
@@ -596,8 +642,8 @@ function saveCachedEditorState(stateKey: string | null, state: EditorState | nul
|
|||||||
if (!stateKey || state == null) return;
|
if (!stateKey || state == null) return;
|
||||||
const stateObj = state.toJSON(stateFields);
|
const stateObj = state.toJSON(stateFields);
|
||||||
|
|
||||||
// Save state in sessionStorage by removing doc and saving the hash of it instead
|
// Save state in sessionStorage by removing doc and saving the hash of it instead.
|
||||||
// This will be checked on restore and put back in if it matches
|
// This will be checked on restore and put back in if it matches.
|
||||||
stateObj.docHash = md5(stateObj.doc);
|
stateObj.docHash = md5(stateObj.doc);
|
||||||
delete stateObj.doc;
|
delete stateObj.doc;
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import {
|
|||||||
closeBracketsKeymap,
|
closeBracketsKeymap,
|
||||||
completionKeymap,
|
completionKeymap,
|
||||||
} from '@codemirror/autocomplete';
|
} from '@codemirror/autocomplete';
|
||||||
import { history, historyKeymap, indentWithTab } from '@codemirror/commands';
|
import { history, historyKeymap } from '@codemirror/commands';
|
||||||
import { javascript } from '@codemirror/lang-javascript';
|
import { javascript } from '@codemirror/lang-javascript';
|
||||||
import { json } from '@codemirror/lang-json';
|
import { json } from '@codemirror/lang-json';
|
||||||
import { markdown } from '@codemirror/lang-markdown';
|
import { markdown } from '@codemirror/lang-markdown';
|
||||||
@@ -142,6 +142,11 @@ export const baseExtensions = [
|
|||||||
keymap.of([...historyKeymap, ...completionKeymap]),
|
keymap.of([...historyKeymap, ...completionKeymap]),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
export const readonlyExtensions = [
|
||||||
|
EditorState.readOnly.of(true),
|
||||||
|
EditorView.contentAttributes.of({ tabindex: '-1' }),
|
||||||
|
];
|
||||||
|
|
||||||
export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => [
|
export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) => [
|
||||||
hideGutter
|
hideGutter
|
||||||
? []
|
? []
|
||||||
@@ -208,5 +213,5 @@ export const multiLineExtensions = ({ hideGutter }: { hideGutter?: boolean }) =>
|
|||||||
rectangularSelection(),
|
rectangularSelection(),
|
||||||
crosshairCursor(),
|
crosshairCursor(),
|
||||||
highlightActiveLineGutter(),
|
highlightActiveLineGutter(),
|
||||||
keymap.of([indentWithTab, ...closeBracketsKeymap, ...searchKeymap, ...foldKeymap, ...lintKeymap]),
|
keymap.of([...closeBracketsKeymap, ...searchKeymap, ...foldKeymap, ...lintKeymap]),
|
||||||
];
|
];
|
||||||
|
|||||||
@@ -1,16 +1,5 @@
|
|||||||
import type { CompletionContext } from '@codemirror/autocomplete';
|
import type { CompletionContext } from '@codemirror/autocomplete';
|
||||||
|
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||||
export interface GenericCompletionOption {
|
|
||||||
label: string;
|
|
||||||
type: 'constant' | 'variable';
|
|
||||||
detail?: string;
|
|
||||||
info?: string;
|
|
||||||
/** When given, should be a number from -99 to 99 that adjusts
|
|
||||||
* how this completion is ranked compared to other completions
|
|
||||||
* that match the input as well as this one. A negative number
|
|
||||||
* moves it down the list, a positive number moves it up. */
|
|
||||||
boost?: number;
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface GenericCompletionConfig {
|
export interface GenericCompletionConfig {
|
||||||
minMatch?: number;
|
minMatch?: number;
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ export type InputProps = Pick<
|
|||||||
| 'useTemplating'
|
| 'useTemplating'
|
||||||
| 'autocomplete'
|
| 'autocomplete'
|
||||||
| 'forceUpdateKey'
|
| 'forceUpdateKey'
|
||||||
|
| 'disabled'
|
||||||
| 'autoFocus'
|
| 'autoFocus'
|
||||||
| 'autoSelect'
|
| 'autoSelect'
|
||||||
| 'autocompleteVariables'
|
| 'autocompleteVariables'
|
||||||
@@ -75,6 +76,7 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
|
|||||||
readOnly,
|
readOnly,
|
||||||
stateKey,
|
stateKey,
|
||||||
multiLine,
|
multiLine,
|
||||||
|
disabled,
|
||||||
...props
|
...props
|
||||||
}: InputProps,
|
}: InputProps,
|
||||||
ref,
|
ref,
|
||||||
@@ -82,18 +84,26 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
|
|||||||
const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]);
|
const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]);
|
||||||
const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
|
const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
|
const [hasChanged, setHasChanged] = useStateWithDeps<boolean>(false, [stateKey, forceUpdateKey]);
|
||||||
const editorRef = useRef<EditorView | null>(null);
|
const editorRef = useRef<EditorView | null>(null);
|
||||||
useImperativeHandle<EditorView | null, EditorView | null>(ref, () => editorRef.current);
|
useImperativeHandle<EditorView | null, EditorView | null>(ref, () => editorRef.current);
|
||||||
|
|
||||||
const handleFocus = useCallback(() => {
|
const handleFocus = useCallback(() => {
|
||||||
if (readOnly) return;
|
if (readOnly) return;
|
||||||
setFocused(true);
|
setFocused(true);
|
||||||
|
// Select all text on focus
|
||||||
|
editorRef.current?.dispatch({
|
||||||
|
selection: { anchor: 0, head: editorRef.current.state.doc.length },
|
||||||
|
});
|
||||||
onFocus?.();
|
onFocus?.();
|
||||||
}, [onFocus, readOnly]);
|
}, [onFocus, readOnly]);
|
||||||
|
|
||||||
const handleBlur = useCallback(() => {
|
const handleBlur = useCallback(() => {
|
||||||
setFocused(false);
|
setFocused(false);
|
||||||
editorRef.current?.dispatch({ selection: { anchor: 0 } });
|
// Move selection to the end on blur
|
||||||
|
editorRef.current?.dispatch({
|
||||||
|
selection: { anchor: editorRef.current.state.doc.length },
|
||||||
|
});
|
||||||
onBlur?.();
|
onBlur?.();
|
||||||
}, [onBlur]);
|
}, [onBlur]);
|
||||||
|
|
||||||
@@ -114,13 +124,14 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
|
|||||||
(value: string) => {
|
(value: string) => {
|
||||||
setCurrentValue(value);
|
setCurrentValue(value);
|
||||||
onChange?.(value);
|
onChange?.(value);
|
||||||
|
setHasChanged(true);
|
||||||
},
|
},
|
||||||
[onChange],
|
[onChange, setHasChanged],
|
||||||
);
|
);
|
||||||
|
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
// Submit nearest form on Enter key press
|
// Submit the nearest form on Enter key press
|
||||||
const handleKeyDown = useCallback(
|
const handleKeyDown = useCallback(
|
||||||
(e: KeyboardEvent) => {
|
(e: KeyboardEvent) => {
|
||||||
if (e.key !== 'Enter') return;
|
if (e.key !== 'Enter') return;
|
||||||
@@ -145,7 +156,7 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
|
|||||||
>
|
>
|
||||||
<Label
|
<Label
|
||||||
htmlFor={id.current}
|
htmlFor={id.current}
|
||||||
optional={!required}
|
required={required}
|
||||||
visuallyHidden={hideLabel}
|
visuallyHidden={hideLabel}
|
||||||
className={classNames(labelClassName)}
|
className={classNames(labelClassName)}
|
||||||
>
|
>
|
||||||
@@ -158,8 +169,9 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
|
|||||||
'x-theme-input',
|
'x-theme-input',
|
||||||
'relative w-full rounded-md text',
|
'relative w-full rounded-md text',
|
||||||
'border',
|
'border',
|
||||||
focused ? 'border-border-focus' : 'border-border',
|
focused && !disabled ? 'border-border-focus' : 'border-border',
|
||||||
!isValid && '!border-danger',
|
disabled && 'border-dotted',
|
||||||
|
!isValid && hasChanged && '!border-danger',
|
||||||
size === 'md' && 'min-h-md',
|
size === 'md' && 'min-h-md',
|
||||||
size === 'sm' && 'min-h-sm',
|
size === 'sm' && 'min-h-sm',
|
||||||
size === 'xs' && 'min-h-xs',
|
size === 'xs' && 'min-h-xs',
|
||||||
@@ -190,7 +202,12 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
|
|||||||
onChange={handleChange}
|
onChange={handleChange}
|
||||||
onPaste={onPaste}
|
onPaste={onPaste}
|
||||||
onPasteOverwrite={onPasteOverwrite}
|
onPasteOverwrite={onPasteOverwrite}
|
||||||
className={classNames(editorClassName, multiLine && 'py-1.5')}
|
disabled={disabled}
|
||||||
|
className={classNames(
|
||||||
|
editorClassName,
|
||||||
|
multiLine && size === 'md' && 'py-1.5',
|
||||||
|
multiLine && size === 'sm' && 'py-1',
|
||||||
|
)}
|
||||||
onFocus={handleFocus}
|
onFocus={handleFocus}
|
||||||
onBlur={handleBlur}
|
onBlur={handleBlur}
|
||||||
readOnly={readOnly}
|
readOnly={readOnly}
|
||||||
@@ -201,7 +218,7 @@ export const Input = forwardRef<EditorView, InputProps>(function Input(
|
|||||||
<IconButton
|
<IconButton
|
||||||
title={obscured ? `Show ${label}` : `Obscure ${label}`}
|
title={obscured ? `Show ${label}` : `Obscure ${label}`}
|
||||||
size="xs"
|
size="xs"
|
||||||
className="mr-0.5 group/obscure !h-auto my-0.5"
|
className={classNames("mr-0.5 group/obscure !h-auto my-0.5", disabled && 'opacity-disabled')}
|
||||||
iconClassName="text-text-subtle group-hover/obscure:text"
|
iconClassName="text-text-subtle group-hover/obscure:text"
|
||||||
iconSize="sm"
|
iconSize="sm"
|
||||||
icon={obscured ? 'eye' : 'eye_closed'}
|
icon={obscured ? 'eye' : 'eye_closed'}
|
||||||
|
|||||||
@@ -4,30 +4,32 @@ import type { HTMLAttributes } from 'react';
|
|||||||
export function Label({
|
export function Label({
|
||||||
htmlFor,
|
htmlFor,
|
||||||
className,
|
className,
|
||||||
optional,
|
|
||||||
children,
|
children,
|
||||||
visuallyHidden,
|
visuallyHidden,
|
||||||
otherTags = [],
|
tags = [],
|
||||||
|
required,
|
||||||
...props
|
...props
|
||||||
}: HTMLAttributes<HTMLLabelElement> & {
|
}: HTMLAttributes<HTMLLabelElement> & {
|
||||||
htmlFor: string;
|
htmlFor: string;
|
||||||
optional?: boolean;
|
required?: boolean;
|
||||||
otherTags?: string[];
|
tags?: string[];
|
||||||
visuallyHidden?: boolean;
|
visuallyHidden?: boolean;
|
||||||
}) {
|
}) {
|
||||||
const tags = optional ? ['optional', ...otherTags] : otherTags;
|
|
||||||
return (
|
return (
|
||||||
<label
|
<label
|
||||||
|
htmlFor={htmlFor}
|
||||||
className={classNames(
|
className={classNames(
|
||||||
className,
|
className,
|
||||||
visuallyHidden && 'sr-only',
|
visuallyHidden && 'sr-only',
|
||||||
'flex-shrink-0',
|
'flex-shrink-0 text-sm',
|
||||||
'text-text-subtle whitespace-nowrap flex items-center gap-1',
|
'text-text-subtle whitespace-nowrap flex items-center gap-1',
|
||||||
)}
|
)}
|
||||||
htmlFor={htmlFor}
|
|
||||||
{...props}
|
{...props}
|
||||||
>
|
>
|
||||||
{children}
|
<span>
|
||||||
|
{children}
|
||||||
|
{required === true && <span className="text-text-subtlest">*</span>}
|
||||||
|
</span>
|
||||||
{tags.map((tag, i) => (
|
{tags.map((tag, i) => (
|
||||||
<span key={i} className="text-xs text-text-subtlest">
|
<span key={i} className="text-xs text-text-subtlest">
|
||||||
({tag})
|
({tag})
|
||||||
|
|||||||
@@ -168,7 +168,8 @@ export const PairEditor = forwardRef<PairEditorRef, PairEditorProps>(function Pa
|
|||||||
);
|
);
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(pair: PairWithId) => 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],
|
[setPairsAndSave],
|
||||||
);
|
);
|
||||||
|
|
||||||
@@ -344,7 +345,6 @@ function PairEditorRow({
|
|||||||
const deleteItems = useMemo(
|
const deleteItems = useMemo(
|
||||||
(): DropdownItem[] => [
|
(): DropdownItem[] => [
|
||||||
{
|
{
|
||||||
key: 'delete',
|
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
onSelect: handleDelete,
|
onSelect: handleDelete,
|
||||||
color: 'danger',
|
color: 'danger',
|
||||||
@@ -570,7 +570,6 @@ function FileActionsDropdown({
|
|||||||
const extraItems = useMemo<DropdownItem[]>(
|
const extraItems = useMemo<DropdownItem[]>(
|
||||||
() => [
|
() => [
|
||||||
{
|
{
|
||||||
key: 'mime',
|
|
||||||
label: 'Set Content-Type',
|
label: 'Set Content-Type',
|
||||||
leftSlot: <Icon icon="pencil" />,
|
leftSlot: <Icon icon="pencil" />,
|
||||||
hidden: !pair.isFile,
|
hidden: !pair.isFile,
|
||||||
@@ -589,7 +588,6 @@ function FileActionsDropdown({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'clear-file',
|
|
||||||
label: 'Unset File',
|
label: 'Unset File',
|
||||||
leftSlot: <Icon icon="x" />,
|
leftSlot: <Icon icon="x" />,
|
||||||
hidden: pair.isFile,
|
hidden: pair.isFile,
|
||||||
@@ -598,7 +596,6 @@ function FileActionsDropdown({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'delete',
|
|
||||||
label: 'Delete',
|
label: 'Delete',
|
||||||
onSelect: onDelete,
|
onSelect: onDelete,
|
||||||
variant: 'danger',
|
variant: 'danger',
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import type { HTMLAttributes, FocusEvent } from 'react';
|
import type { FocusEvent, HTMLAttributes } from 'react';
|
||||||
import { useCallback, useMemo, useRef, useState } from 'react';
|
import { useCallback, useRef, useState } from 'react';
|
||||||
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
import { useStateWithDeps } from '../../hooks/useStateWithDeps';
|
||||||
import { IconButton } from './IconButton';
|
import { IconButton } from './IconButton';
|
||||||
import type { InputProps } from './Input';
|
import type { InputProps } from './Input';
|
||||||
@@ -41,8 +41,8 @@ export function PlainInput({
|
|||||||
onFocusRaw,
|
onFocusRaw,
|
||||||
}: PlainInputProps) {
|
}: PlainInputProps) {
|
||||||
const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]);
|
const [obscured, setObscured] = useStateWithDeps(type === 'password', [type]);
|
||||||
const [currentValue, setCurrentValue] = useState(defaultValue ?? '');
|
|
||||||
const [focused, setFocused] = useState(false);
|
const [focused, setFocused] = useState(false);
|
||||||
|
const [hasChanged, setHasChanged] = useState<boolean>(false);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
@@ -71,19 +71,19 @@ export function PlainInput({
|
|||||||
'px-2 text-xs font-mono cursor-text',
|
'px-2 text-xs font-mono cursor-text',
|
||||||
);
|
);
|
||||||
|
|
||||||
const isValid = useMemo(() => {
|
|
||||||
if (required && !validateRequire(currentValue)) return false;
|
|
||||||
if (typeof validate === 'boolean') return validate;
|
|
||||||
if (typeof validate === 'function' && !validate(currentValue)) return false;
|
|
||||||
return true;
|
|
||||||
}, [required, currentValue, validate]);
|
|
||||||
|
|
||||||
const handleChange = useCallback(
|
const handleChange = useCallback(
|
||||||
(value: string) => {
|
(value: string) => {
|
||||||
setCurrentValue(value);
|
|
||||||
onChange?.(value);
|
onChange?.(value);
|
||||||
|
setHasChanged(true);
|
||||||
|
const isValid = (value: string) => {
|
||||||
|
if (required && !validateRequire(value)) return false;
|
||||||
|
if (typeof validate === 'boolean') return validate;
|
||||||
|
if (typeof validate === 'function' && !validate(value)) return false;
|
||||||
|
return true;
|
||||||
|
};
|
||||||
|
inputRef.current?.setCustomValidity(isValid(value) ? '' : 'Invalid value');
|
||||||
},
|
},
|
||||||
[onChange],
|
[onChange, required, validate],
|
||||||
);
|
);
|
||||||
|
|
||||||
const wrapperRef = useRef<HTMLDivElement>(null);
|
const wrapperRef = useRef<HTMLDivElement>(null);
|
||||||
@@ -98,12 +98,7 @@ export function PlainInput({
|
|||||||
labelPosition === 'top' && 'flex-row gap-0.5',
|
labelPosition === 'top' && 'flex-row gap-0.5',
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<Label
|
<Label htmlFor={id} className={labelClassName} visuallyHidden={hideLabel} required={required}>
|
||||||
htmlFor={id}
|
|
||||||
className={labelClassName}
|
|
||||||
visuallyHidden={hideLabel}
|
|
||||||
optional={!required}
|
|
||||||
>
|
|
||||||
{label}
|
{label}
|
||||||
</Label>
|
</Label>
|
||||||
<HStack
|
<HStack
|
||||||
@@ -114,7 +109,7 @@ export function PlainInput({
|
|||||||
'relative w-full rounded-md text',
|
'relative w-full rounded-md text',
|
||||||
'border',
|
'border',
|
||||||
focused ? 'border-border-focus' : 'border-border-subtle',
|
focused ? 'border-border-focus' : 'border-border-subtle',
|
||||||
!isValid && '!border-danger',
|
hasChanged && 'has-[:invalid]:border-danger', // For built-in HTML validation
|
||||||
size === 'md' && 'min-h-md',
|
size === 'md' && 'min-h-md',
|
||||||
size === 'sm' && 'min-h-sm',
|
size === 'sm' && 'min-h-sm',
|
||||||
size === 'xs' && 'min-h-xs',
|
size === 'xs' && 'min-h-xs',
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import type { PromptTextRequest } from '@yaakapp-internal/plugins';
|
|||||||
import type { FormEvent, ReactNode } from 'react';
|
import type { FormEvent, ReactNode } from 'react';
|
||||||
import { useCallback, useState } from 'react';
|
import { useCallback, useState } from 'react';
|
||||||
import { Button } from './Button';
|
import { Button } from './Button';
|
||||||
import { Input } from './Input';
|
import { PlainInput } from './PlainInput';
|
||||||
import { HStack } from './Stacks';
|
import { HStack } from './Stacks';
|
||||||
|
|
||||||
export type PromptProps = Omit<PromptTextRequest, 'id' | 'title' | 'description'> & {
|
export type PromptProps = Omit<PromptTextRequest, 'id' | 'title' | 'description'> & {
|
||||||
@@ -35,7 +35,7 @@ export function Prompt({
|
|||||||
className="grid grid-rows-[auto_auto] grid-cols-[minmax(0,1fr)] gap-4 mb-4"
|
className="grid grid-rows-[auto_auto] grid-cols-[minmax(0,1fr)] gap-4 mb-4"
|
||||||
onSubmit={handleSubmit}
|
onSubmit={handleSubmit}
|
||||||
>
|
>
|
||||||
<Input
|
<PlainInput
|
||||||
hideLabel
|
hideLabel
|
||||||
autoSelect
|
autoSelect
|
||||||
required={required}
|
required={required}
|
||||||
@@ -43,7 +43,6 @@ export function Prompt({
|
|||||||
label={label}
|
label={label}
|
||||||
defaultValue={defaultValue}
|
defaultValue={defaultValue}
|
||||||
onChange={setValue}
|
onChange={setValue}
|
||||||
stateKey={null}
|
|
||||||
/>
|
/>
|
||||||
<HStack space={2} justifyContent="end">
|
<HStack space={2} justifyContent="end">
|
||||||
<Button onClick={onCancel} variant="border" color="secondary">
|
<Button onClick={onCancel} variant="border" color="secondary">
|
||||||
|
|||||||
@@ -23,12 +23,14 @@ export interface SelectProps<T extends string> {
|
|||||||
size?: ButtonProps['size'];
|
size?: ButtonProps['size'];
|
||||||
className?: string;
|
className?: string;
|
||||||
event?: string;
|
event?: string;
|
||||||
|
disabled?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function Select<T extends string>({
|
export function Select<T extends string>({
|
||||||
labelPosition = 'top',
|
labelPosition = 'top',
|
||||||
name,
|
name,
|
||||||
labelClassName,
|
labelClassName,
|
||||||
|
disabled,
|
||||||
hideLabel,
|
hideLabel,
|
||||||
label,
|
label,
|
||||||
value,
|
value,
|
||||||
@@ -72,7 +74,8 @@ export function Select<T extends string>({
|
|||||||
'w-full rounded-md text text-sm font-mono',
|
'w-full rounded-md text text-sm font-mono',
|
||||||
'pl-2',
|
'pl-2',
|
||||||
'border',
|
'border',
|
||||||
focused ? 'border-border-focus' : 'border-border',
|
focused && !disabled ? 'border-border-focus' : 'border-border',
|
||||||
|
disabled && 'border-dotted',
|
||||||
isInvalidSelection && 'border-danger',
|
isInvalidSelection && 'border-danger',
|
||||||
size === 'xs' && 'h-xs',
|
size === 'xs' && 'h-xs',
|
||||||
size === 'sm' && 'h-sm',
|
size === 'sm' && 'h-sm',
|
||||||
@@ -86,7 +89,8 @@ export function Select<T extends string>({
|
|||||||
onChange={(e) => handleChange(e.target.value as T)}
|
onChange={(e) => handleChange(e.target.value as T)}
|
||||||
onFocus={() => setFocused(true)}
|
onFocus={() => setFocused(true)}
|
||||||
onBlur={() => setFocused(false)}
|
onBlur={() => setFocused(false)}
|
||||||
className={classNames('pr-7 w-full outline-none bg-transparent')}
|
className={classNames('pr-7 w-full outline-none bg-transparent disabled:opacity-disabled')}
|
||||||
|
disabled={disabled}
|
||||||
>
|
>
|
||||||
{isInvalidSelection && <option value={'__NONE__'}>-- Select an Option --</option>}
|
{isInvalidSelection && <option value={'__NONE__'}>-- Select an Option --</option>}
|
||||||
{options.map((o) => {
|
{options.map((o) => {
|
||||||
@@ -109,6 +113,7 @@ export function Select<T extends string>({
|
|||||||
variant="border"
|
variant="border"
|
||||||
size={size}
|
size={size}
|
||||||
leftSlot={leftSlot}
|
leftSlot={leftSlot}
|
||||||
|
disabled={disabled}
|
||||||
forDropdown
|
forDropdown
|
||||||
>
|
>
|
||||||
{options.find((o) => o.type !== 'separator' && o.value === value)?.label ?? '--'}
|
{options.find((o) => o.type !== 'separator' && o.value === value)?.label ?? '--'}
|
||||||
|
|||||||
@@ -20,9 +20,8 @@ export interface ToastProps {
|
|||||||
color?: ShowToastRequest['color'];
|
color?: ShowToastRequest['color'];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ICONS: Record<NonNullable<ToastProps['color']>, IconProps['icon'] | null> = {
|
const ICONS: Record<NonNullable<ToastProps['color'] | 'custom'>, IconProps['icon'] | null> = {
|
||||||
custom: null,
|
custom: null,
|
||||||
default: 'info',
|
|
||||||
danger: 'alert_triangle',
|
danger: 'alert_triangle',
|
||||||
info: 'info',
|
info: 'info',
|
||||||
notice: 'alert_triangle',
|
notice: 'alert_triangle',
|
||||||
@@ -42,9 +41,8 @@ export function Toast({ children, open, onClose, timeout, action, icon, color }:
|
|||||||
{},
|
{},
|
||||||
[open],
|
[open],
|
||||||
);
|
);
|
||||||
color = color ?? 'default';
|
|
||||||
|
|
||||||
const toastIcon = icon ?? (color in ICONS && ICONS[color]);
|
const toastIcon = icon ?? (color && color in ICONS && ICONS[color]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
|
|||||||
@@ -26,7 +26,6 @@ export function useCreateDropdownItems({
|
|||||||
|
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
key: 'create-http-request',
|
|
||||||
label: 'HTTP Request',
|
label: 'HTTP Request',
|
||||||
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
||||||
onSelect: () => {
|
onSelect: () => {
|
||||||
@@ -34,7 +33,6 @@ export function useCreateDropdownItems({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'create-graphql-request',
|
|
||||||
label: 'GraphQL Query',
|
label: 'GraphQL Query',
|
||||||
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
||||||
onSelect: () =>
|
onSelect: () =>
|
||||||
@@ -46,7 +44,6 @@ export function useCreateDropdownItems({
|
|||||||
}),
|
}),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'create-grpc-request',
|
|
||||||
label: 'gRPC Call',
|
label: 'gRPC Call',
|
||||||
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
||||||
onSelect: () => createGrpcRequest({ folderId }),
|
onSelect: () => createGrpcRequest({ folderId }),
|
||||||
@@ -54,11 +51,8 @@ export function useCreateDropdownItems({
|
|||||||
...((hideFolder
|
...((hideFolder
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
|
{ type: 'separator' },
|
||||||
{
|
{
|
||||||
type: 'separator',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'create-folder',
|
|
||||||
label: 'Folder',
|
label: 'Folder',
|
||||||
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
leftSlot: hideIcons ? undefined : <Icon icon="plus" />,
|
||||||
onSelect: () => createFolder.mutate({ folderId }),
|
onSelect: () => createFolder.mutate({ folderId }),
|
||||||
|
|||||||
@@ -1,39 +1,42 @@
|
|||||||
import { useQuery } from '@tanstack/react-query';
|
import { useQuery } from '@tanstack/react-query';
|
||||||
import type { GetHttpAuthenticationResponse } from '@yaakapp-internal/plugins';
|
import type { GetHttpAuthenticationSummaryResponse } from '@yaakapp-internal/plugins';
|
||||||
import { useAtomValue } from 'jotai';
|
import { useAtomValue } from 'jotai';
|
||||||
import { atom, useSetAtom } from 'jotai/index';
|
import { atom } from 'jotai/index';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { jotaiStore } from '../lib/jotai';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { showErrorToast } from '../lib/toast';
|
import { showErrorToast } from '../lib/toast';
|
||||||
|
import { usePluginsKey } from './usePlugins';
|
||||||
|
|
||||||
const httpAuthenticationAtom = atom<GetHttpAuthenticationResponse[]>([]);
|
const httpAuthenticationSummariesAtom = atom<GetHttpAuthenticationSummaryResponse[]>([]);
|
||||||
const orderedHttpAuthenticationAtom = atom((get) =>
|
const orderedHttpAuthenticationAtom = atom((get) =>
|
||||||
get(httpAuthenticationAtom).sort((a, b) => a.name.localeCompare(b.name)),
|
get(httpAuthenticationSummariesAtom)?.sort((a, b) => a.name.localeCompare(b.name)),
|
||||||
);
|
);
|
||||||
|
|
||||||
export function useHttpAuthentication() {
|
export function useHttpAuthenticationSummaries() {
|
||||||
return useAtomValue(orderedHttpAuthenticationAtom);
|
return useAtomValue(orderedHttpAuthenticationAtom);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function useSubscribeHttpAuthentication() {
|
export function useSubscribeHttpAuthentication() {
|
||||||
const [numResults, setNumResults] = useState<number>(0);
|
const [numResults, setNumResults] = useState<number>(0);
|
||||||
const setAtom = useSetAtom(httpAuthenticationAtom);
|
const pluginsKey = usePluginsKey();
|
||||||
|
|
||||||
useQuery({
|
useQuery({
|
||||||
queryKey: ['http_authentication'],
|
queryKey: ['http_authentication_summaries', pluginsKey],
|
||||||
// Fetch periodically until functions are returned
|
// Fetch periodically until functions are returned
|
||||||
// NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic
|
// NOTE: visibilitychange (refetchOnWindowFocus) does not work on Windows, so we'll rely on this logic
|
||||||
// to refetch things until that's working again
|
// 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
|
// TODO: Update plugin system to wait for plugins to initialize before sending the first event to them
|
||||||
refetchInterval: numResults > 0 ? Infinity : 1000,
|
refetchInterval: numResults > 0 ? Infinity : 1000,
|
||||||
refetchOnMount: true,
|
refetchOnMount: true,
|
||||||
|
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||||
queryFn: async () => {
|
queryFn: async () => {
|
||||||
try {
|
try {
|
||||||
const result = await invokeCmd<GetHttpAuthenticationResponse[]>(
|
const result = await invokeCmd<GetHttpAuthenticationSummaryResponse[]>(
|
||||||
'cmd_get_http_authentication',
|
'cmd_get_http_authentication_summaries',
|
||||||
);
|
);
|
||||||
setNumResults(result.length);
|
setNumResults(result.length);
|
||||||
setAtom(result);
|
jotaiStore.set(httpAuthenticationSummariesAtom, result);
|
||||||
return result;
|
return result;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
showErrorToast('http-authentication-error', err);
|
showErrorToast('http-authentication-error', err);
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
import { useQuery } from '@tanstack/react-query';
|
||||||
|
import type { GrpcRequest, HttpRequest } from '@yaakapp-internal/models';
|
||||||
|
import type { GetHttpAuthenticationConfigResponse, JsonPrimitive } from '@yaakapp-internal/plugins';
|
||||||
|
import { md5 } from 'js-md5';
|
||||||
|
import { useState } from 'react';
|
||||||
|
import { invokeCmd } from '../lib/tauri';
|
||||||
|
import { useHttpResponses } from './useHttpResponses';
|
||||||
|
|
||||||
|
export function useHttpAuthenticationConfig(
|
||||||
|
authName: string | null,
|
||||||
|
values: Record<string, JsonPrimitive>,
|
||||||
|
requestId: string,
|
||||||
|
) {
|
||||||
|
const responses = useHttpResponses();
|
||||||
|
const [forceRefreshCounter, setForceRefreshCounter] = useState<number>(0);
|
||||||
|
|
||||||
|
// Some auth handlers like OAuth 2.0 show the current token after a successful request. To
|
||||||
|
// handle that, we'll force the auth to re-fetch after each new response closes
|
||||||
|
const responseKey = md5(
|
||||||
|
responses
|
||||||
|
.filter((r) => r.state === 'closed')
|
||||||
|
.map((r) => r.id)
|
||||||
|
.join(':'),
|
||||||
|
);
|
||||||
|
|
||||||
|
return useQuery({
|
||||||
|
queryKey: ['http_authentication_config', requestId, authName, values, responseKey, forceRefreshCounter],
|
||||||
|
placeholderData: (prev) => prev, // Keep previous data on refetch
|
||||||
|
queryFn: async () => {
|
||||||
|
const config = await invokeCmd<GetHttpAuthenticationConfigResponse>(
|
||||||
|
'cmd_get_http_authentication_config',
|
||||||
|
{
|
||||||
|
authName,
|
||||||
|
values,
|
||||||
|
requestId,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
return {
|
||||||
|
...config,
|
||||||
|
actions: config.actions?.map((a, i) => ({
|
||||||
|
...a,
|
||||||
|
call: async ({ id: requestId }: HttpRequest | GrpcRequest) => {
|
||||||
|
await invokeCmd('cmd_call_http_authentication_action', {
|
||||||
|
pluginRefId: config.pluginRefId,
|
||||||
|
actionIndex: i,
|
||||||
|
authName,
|
||||||
|
values,
|
||||||
|
requestId,
|
||||||
|
});
|
||||||
|
|
||||||
|
// Ensure the config is refreshed after the action is done
|
||||||
|
setForceRefreshCounter((c) => c + 1);
|
||||||
|
},
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
},
|
||||||
|
});
|
||||||
|
}
|
||||||
@@ -5,11 +5,11 @@ import type {
|
|||||||
GetHttpRequestActionsResponse,
|
GetHttpRequestActionsResponse,
|
||||||
HttpRequestAction,
|
HttpRequestAction,
|
||||||
} from '@yaakapp-internal/plugins';
|
} from '@yaakapp-internal/plugins';
|
||||||
|
import { useMemo } from 'react';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { usePluginsKey } from './usePlugins';
|
import { usePluginsKey } from './usePlugins';
|
||||||
import { useMemo } from 'react';
|
|
||||||
|
|
||||||
export type CallableHttpRequestAction = Pick<HttpRequestAction, 'key' | 'label' | 'icon'> & {
|
export type CallableHttpRequestAction = Pick<HttpRequestAction, 'label' | 'icon'> & {
|
||||||
call: (httpRequest: HttpRequest) => Promise<void>;
|
call: (httpRequest: HttpRequest) => Promise<void>;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -24,13 +24,12 @@ export function useHttpRequestActions() {
|
|||||||
);
|
);
|
||||||
|
|
||||||
return responses.flatMap((r) =>
|
return responses.flatMap((r) =>
|
||||||
r.actions.map((a) => ({
|
r.actions.map((a, i) => ({
|
||||||
key: a.key,
|
|
||||||
label: a.label,
|
label: a.label,
|
||||||
icon: a.icon,
|
icon: a.icon,
|
||||||
call: async (httpRequest: HttpRequest) => {
|
call: async (httpRequest: HttpRequest) => {
|
||||||
const payload: CallHttpRequestActionRequest = {
|
const payload: CallHttpRequestActionRequest = {
|
||||||
key: a.key,
|
index: i,
|
||||||
pluginRefId: r.pluginRefId,
|
pluginRefId: r.pluginRefId,
|
||||||
args: { httpRequest },
|
args: { httpRequest },
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,7 +25,6 @@ export function useNotificationToast() {
|
|||||||
id: payload.id,
|
id: payload.id,
|
||||||
timeout: null,
|
timeout: null,
|
||||||
message: payload.message,
|
message: payload.message,
|
||||||
color: 'custom',
|
|
||||||
onClose: () => markRead(payload.id),
|
onClose: () => markRead(payload.id),
|
||||||
action: ({ hide }) =>
|
action: ({ hide }) =>
|
||||||
actionLabel && actionUrl ? (
|
actionLabel && actionUrl ? (
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { GetTemplateFunctionsResponse, TemplateFunction } from '@yaakapp-in
|
|||||||
import { atom, useAtomValue } from 'jotai';
|
import { atom, useAtomValue } from 'jotai';
|
||||||
import { useSetAtom } from 'jotai/index';
|
import { useSetAtom } from 'jotai/index';
|
||||||
import { useMemo, useState } from 'react';
|
import { useMemo, useState } from 'react';
|
||||||
import type {TwigCompletionOption} from "../components/core/Editor/twig/completion";
|
import type { TwigCompletionOption } from '../components/core/Editor/twig/completion';
|
||||||
import { invokeCmd } from '../lib/tauri';
|
import { invokeCmd } from '../lib/tauri';
|
||||||
import { usePluginsKey } from './usePlugins';
|
import { usePluginsKey } from './usePlugins';
|
||||||
|
|
||||||
@@ -17,8 +17,9 @@ export function useTemplateFunctionCompletionOptions(
|
|||||||
return (
|
return (
|
||||||
templateFunctions.map((fn) => {
|
templateFunctions.map((fn) => {
|
||||||
const NUM_ARGS = 2;
|
const NUM_ARGS = 2;
|
||||||
|
const argsWithName = fn.args.filter((a) => 'name' in a);
|
||||||
const shortArgs =
|
const shortArgs =
|
||||||
fn.args
|
argsWithName
|
||||||
.slice(0, NUM_ARGS)
|
.slice(0, NUM_ARGS)
|
||||||
.map((a) => a.name)
|
.map((a) => a.name)
|
||||||
.join(', ') + (fn.args.length > NUM_ARGS ? ', …' : '');
|
.join(', ') + (fn.args.length > NUM_ARGS ? ', …' : '');
|
||||||
@@ -27,7 +28,7 @@ export function useTemplateFunctionCompletionOptions(
|
|||||||
aliases: fn.aliases,
|
aliases: fn.aliases,
|
||||||
type: 'function',
|
type: 'function',
|
||||||
description: fn.description,
|
description: fn.description,
|
||||||
args: fn.args.map((a) => ({ name: a.name })),
|
args: argsWithName.map((a) => ({ name: a.name })),
|
||||||
value: null,
|
value: null,
|
||||||
label: `${fn.name}(${shortArgs})`,
|
label: `${fn.name}(${shortArgs})`,
|
||||||
onClick: (rawTag: string, startPos: number) => onClick(fn, rawTag, startPos),
|
onClick: (rawTag: string, startPos: number) => onClick(fn, rawTag, startPos),
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { GenericCompletionOption } from '../../components/core/Editor/genericCompletion';
|
import type { GenericCompletionOption } from '@yaakapp-internal/plugins';
|
||||||
|
|
||||||
export const headerNames: (GenericCompletionOption | string)[] = [
|
export const headerNames: (GenericCompletionOption | string)[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -2,17 +2,16 @@ import type { InvokeArgs } from '@tauri-apps/api/core';
|
|||||||
import { invoke } from '@tauri-apps/api/core';
|
import { invoke } from '@tauri-apps/api/core';
|
||||||
|
|
||||||
type TauriCmd =
|
type TauriCmd =
|
||||||
|
| 'cmd_call_http_authentication_action'
|
||||||
| 'cmd_call_http_request_action'
|
| 'cmd_call_http_request_action'
|
||||||
| 'cmd_check_for_updates'
|
| 'cmd_check_for_updates'
|
||||||
| 'cmd_create_cookie_jar'
|
| 'cmd_create_cookie_jar'
|
||||||
| 'cmd_create_environment'
|
| 'cmd_create_environment'
|
||||||
| 'cmd_template_tokens_to_string'
|
|
||||||
| 'cmd_create_grpc_request'
|
| 'cmd_create_grpc_request'
|
||||||
| 'cmd_create_http_request'
|
| 'cmd_create_http_request'
|
||||||
| 'cmd_curl_to_request'
|
| 'cmd_curl_to_request'
|
||||||
| 'cmd_delete_all_grpc_connections'
|
| 'cmd_delete_all_grpc_connections'
|
||||||
| 'cmd_delete_all_http_responses'
|
| 'cmd_delete_all_http_responses'
|
||||||
| 'cmd_delete_send_history'
|
|
||||||
| 'cmd_delete_cookie_jar'
|
| 'cmd_delete_cookie_jar'
|
||||||
| 'cmd_delete_environment'
|
| 'cmd_delete_environment'
|
||||||
| 'cmd_delete_folder'
|
| 'cmd_delete_folder'
|
||||||
@@ -20,6 +19,7 @@ type TauriCmd =
|
|||||||
| 'cmd_delete_grpc_request'
|
| 'cmd_delete_grpc_request'
|
||||||
| 'cmd_delete_http_request'
|
| 'cmd_delete_http_request'
|
||||||
| 'cmd_delete_http_response'
|
| 'cmd_delete_http_response'
|
||||||
|
| 'cmd_delete_send_history'
|
||||||
| 'cmd_delete_workspace'
|
| 'cmd_delete_workspace'
|
||||||
| 'cmd_dismiss_notification'
|
| 'cmd_dismiss_notification'
|
||||||
| 'cmd_duplicate_folder'
|
| 'cmd_duplicate_folder'
|
||||||
@@ -32,11 +32,12 @@ type TauriCmd =
|
|||||||
| 'cmd_get_environment'
|
| 'cmd_get_environment'
|
||||||
| 'cmd_get_folder'
|
| 'cmd_get_folder'
|
||||||
| 'cmd_get_grpc_request'
|
| 'cmd_get_grpc_request'
|
||||||
| 'cmd_get_http_authentication'
|
| 'cmd_get_http_authentication_config'
|
||||||
|
| 'cmd_get_http_authentication_summaries'
|
||||||
| 'cmd_get_http_request'
|
| 'cmd_get_http_request'
|
||||||
| 'cmd_get_sse_events'
|
|
||||||
| 'cmd_get_key_value'
|
| 'cmd_get_key_value'
|
||||||
| 'cmd_get_settings'
|
| 'cmd_get_settings'
|
||||||
|
| 'cmd_get_sse_events'
|
||||||
| 'cmd_get_workspace'
|
| 'cmd_get_workspace'
|
||||||
| 'cmd_get_workspace_meta'
|
| 'cmd_get_workspace_meta'
|
||||||
| 'cmd_grpc_go'
|
| 'cmd_grpc_go'
|
||||||
@@ -45,7 +46,6 @@ type TauriCmd =
|
|||||||
| 'cmd_import_data'
|
| 'cmd_import_data'
|
||||||
| 'cmd_install_plugin'
|
| 'cmd_install_plugin'
|
||||||
| 'cmd_list_cookie_jars'
|
| 'cmd_list_cookie_jars'
|
||||||
| 'cmd_list_key_values'
|
|
||||||
| 'cmd_list_environments'
|
| 'cmd_list_environments'
|
||||||
| 'cmd_list_folders'
|
| 'cmd_list_folders'
|
||||||
| 'cmd_list_grpc_connections'
|
| 'cmd_list_grpc_connections'
|
||||||
@@ -53,21 +53,23 @@ type TauriCmd =
|
|||||||
| 'cmd_list_grpc_requests'
|
| 'cmd_list_grpc_requests'
|
||||||
| 'cmd_list_http_requests'
|
| 'cmd_list_http_requests'
|
||||||
| 'cmd_list_http_responses'
|
| 'cmd_list_http_responses'
|
||||||
|
| 'cmd_list_key_values'
|
||||||
| 'cmd_list_plugins'
|
| 'cmd_list_plugins'
|
||||||
| 'cmd_list_workspaces'
|
| 'cmd_list_workspaces'
|
||||||
| 'cmd_metadata'
|
| 'cmd_metadata'
|
||||||
| 'cmd_new_main_window'
|
|
||||||
| 'cmd_new_child_window'
|
| 'cmd_new_child_window'
|
||||||
|
| 'cmd_new_main_window'
|
||||||
| 'cmd_parse_template'
|
| 'cmd_parse_template'
|
||||||
| 'cmd_plugin_info'
|
| 'cmd_plugin_info'
|
||||||
| 'cmd_render_template'
|
|
||||||
| 'cmd_reload_plugins'
|
| 'cmd_reload_plugins'
|
||||||
|
| 'cmd_render_template'
|
||||||
| 'cmd_save_response'
|
| 'cmd_save_response'
|
||||||
| 'cmd_send_ephemeral_request'
|
| 'cmd_send_ephemeral_request'
|
||||||
| 'cmd_send_http_request'
|
| 'cmd_send_http_request'
|
||||||
| 'cmd_set_key_value'
|
| 'cmd_set_key_value'
|
||||||
| 'cmd_set_update_mode'
|
| 'cmd_set_update_mode'
|
||||||
| 'cmd_template_functions'
|
| 'cmd_template_functions'
|
||||||
|
| 'cmd_template_tokens_to_string'
|
||||||
| 'cmd_track_event'
|
| 'cmd_track_event'
|
||||||
| 'cmd_uninstall_plugin'
|
| 'cmd_uninstall_plugin'
|
||||||
| 'cmd_update_cookie_jar'
|
| 'cmd_update_cookie_jar'
|
||||||
|
|||||||
Reference in New Issue
Block a user