mirror of
https://github.com/mountain-loop/yaak.git
synced 2026-02-17 16:17:45 +01:00
Compare commits
31 Commits
v2025.4.0
...
v2025.5.0-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
cf35658fea | ||
|
|
6330c77948 | ||
|
|
77d2edd947 | ||
|
|
4f0f60cb99 | ||
|
|
dd2b665982 | ||
|
|
19ffcd18a6 | ||
|
|
ad4d6d9720 | ||
|
|
9e98b5f905 | ||
|
|
19c6ad9d97 | ||
|
|
a0e5e60803 | ||
|
|
2a6f139d36 | ||
|
|
36bbb87a5e | ||
|
|
a6979cf37e | ||
|
|
ff26cc1344 | ||
|
|
fa62f88fa4 | ||
|
|
99975c3223 | ||
|
|
d3cda19be2 | ||
|
|
9b0a767ac8 | ||
|
|
81c3de807d | ||
|
|
9ab02130b0 | ||
|
|
25d50246c0 | ||
|
|
bb0cc16a70 | ||
|
|
8817be679b | ||
|
|
f476d87613 | ||
|
|
1438e8bacc | ||
|
|
7be2767527 | ||
|
|
a1b1eafd39 | ||
|
|
1948fb78bd | ||
|
|
cb7c44cc65 | ||
|
|
b5620fcdf3 | ||
|
|
b8e6dbc7c7 |
@@ -1,6 +0,0 @@
|
||||
node_modules/
|
||||
dist/
|
||||
.eslintrc.cjs
|
||||
.prettierrc.cjs
|
||||
src-web/postcss.config.cjs
|
||||
src-web/vite.config.ts
|
||||
@@ -1,49 +0,0 @@
|
||||
module.exports = {
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:jsx-a11y/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'eslint-config-prettier',
|
||||
],
|
||||
plugins: ['react-refresh'],
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
ignorePatterns: [
|
||||
'scripts/**/*',
|
||||
'packages/plugin-runtime/**/*',
|
||||
'packages/plugin-runtime-types/**/*',
|
||||
'src-tauri/**/*',
|
||||
'src-web/tailwind.config.cjs',
|
||||
'src-web/vite.config.ts',
|
||||
],
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
'import/resolver': {
|
||||
node: {
|
||||
paths: ['src-web'],
|
||||
extensions: ['.ts', '.tsx'],
|
||||
},
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
'react-refresh/only-export-components': 'error',
|
||||
'jsx-a11y/no-autofocus': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
disallowTypeAnnotations: true,
|
||||
fixStyle: 'separate-type-imports',
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
89
eslint.config.cjs
Normal file
89
eslint.config.cjs
Normal file
@@ -0,0 +1,89 @@
|
||||
const { defineConfig, globalIgnores } = require('eslint/config');
|
||||
|
||||
const { fixupConfigRules } = require('@eslint/compat');
|
||||
|
||||
const reactRefresh = require('eslint-plugin-react-refresh');
|
||||
const tsParser = require('@typescript-eslint/parser');
|
||||
const js = require('@eslint/js');
|
||||
|
||||
const { FlatCompat } = require('@eslint/eslintrc');
|
||||
|
||||
const compat = new FlatCompat({
|
||||
baseDirectory: __dirname,
|
||||
recommendedConfig: js.configs.recommended,
|
||||
allConfig: js.configs.all,
|
||||
});
|
||||
|
||||
module.exports = defineConfig([
|
||||
{
|
||||
extends: fixupConfigRules(
|
||||
compat.extends(
|
||||
'eslint:recommended',
|
||||
'plugin:react/recommended',
|
||||
'plugin:react-hooks/recommended',
|
||||
'plugin:import/recommended',
|
||||
'plugin:jsx-a11y/recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'eslint-config-prettier',
|
||||
),
|
||||
),
|
||||
|
||||
plugins: {
|
||||
'react-refresh': reactRefresh,
|
||||
},
|
||||
|
||||
languageOptions: {
|
||||
parser: tsParser,
|
||||
|
||||
parserOptions: {
|
||||
project: ['./tsconfig.json'],
|
||||
},
|
||||
},
|
||||
|
||||
settings: {
|
||||
react: {
|
||||
version: 'detect',
|
||||
},
|
||||
|
||||
'import/resolver': {
|
||||
node: {
|
||||
paths: ['src-web'],
|
||||
extensions: ['.ts', '.tsx'],
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
rules: {
|
||||
'react-refresh/only-export-components': 'error',
|
||||
'jsx-a11y/no-autofocus': 'off',
|
||||
'react/react-in-jsx-scope': 'off',
|
||||
'import/no-unresolved': 'off',
|
||||
|
||||
'@typescript-eslint/consistent-type-imports': [
|
||||
'error',
|
||||
{
|
||||
prefer: 'type-imports',
|
||||
disallowTypeAnnotations: true,
|
||||
fixStyle: 'separate-type-imports',
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
globalIgnores([
|
||||
'scripts/**/*',
|
||||
'packages/plugin-runtime/**/*',
|
||||
'packages/plugin-runtime-types/**/*',
|
||||
'src-tauri/**/*',
|
||||
'src-web/tailwind.config.cjs',
|
||||
'src-web/vite.config.ts',
|
||||
]),
|
||||
globalIgnores([
|
||||
'**/node_modules/',
|
||||
'**/dist/',
|
||||
'**/build/',
|
||||
'**/.eslintrc.cjs',
|
||||
'**/.prettierrc.cjs',
|
||||
'src-web/postcss.config.cjs',
|
||||
'src-web/vite.config.ts',
|
||||
]),
|
||||
]);
|
||||
2286
package-lock.json
generated
2286
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
18
package.json
18
package.json
@@ -14,7 +14,8 @@
|
||||
"plugins/auth-bearer",
|
||||
"plugins/auth-jwt",
|
||||
"plugins/auth-oauth2",
|
||||
"plugins/exporter-curl",
|
||||
"plugins/action-copy-curl",
|
||||
"plugins/action-copy-grpcurl",
|
||||
"plugins/filter-jsonpath",
|
||||
"plugins/filter-xpath",
|
||||
"plugins/importer-curl",
|
||||
@@ -33,6 +34,7 @@
|
||||
"plugins/template-function-response",
|
||||
"plugins/template-function-uuid",
|
||||
"plugins/template-function-xml",
|
||||
"plugins/themes-yaak",
|
||||
"src-tauri/yaak-crypto",
|
||||
"src-tauri/yaak-git",
|
||||
"src-tauri/yaak-fonts",
|
||||
@@ -67,20 +69,24 @@
|
||||
"jotai": "^2.12.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/compat": "^1.3.0",
|
||||
"@eslint/eslintrc": "^3.3.1",
|
||||
"@eslint/js": "^9.29.0",
|
||||
"@tauri-apps/cli": "2.4.1",
|
||||
"@typescript-eslint/eslint-plugin": "^8.27.0",
|
||||
"@typescript-eslint/parser": "^8.27.0",
|
||||
"@yaakapp/cli": "^0.1.5",
|
||||
"eslint": "^8",
|
||||
"eslint-config-prettier": "^8",
|
||||
"eslint-plugin-import": "^2.31.0",
|
||||
"eslint": "^9.29.0",
|
||||
"eslint-config-prettier": "^10.1.5",
|
||||
"eslint-plugin-import": "^2.32.0",
|
||||
"eslint-plugin-jsx-a11y": "^6.10.2",
|
||||
"eslint-plugin-react": "^7.37.2",
|
||||
"eslint-plugin-react-hooks": "^5.1.0",
|
||||
"eslint-plugin-react": "^7.37.5",
|
||||
"eslint-plugin-react-hooks": "^5.2.0",
|
||||
"nodejs-file-downloader": "^4.13.0",
|
||||
"npm-run-all": "^4.1.5",
|
||||
"prettier": "^3.4.2",
|
||||
"typescript": "^5.8.3",
|
||||
"vitest": "^3.2.4",
|
||||
"workspaces-run": "^1.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,5 +24,5 @@ the [Quick Start Guide](https://feedback.yaak.app/help/articles/6911763-plugins-
|
||||
If you prefer starting from scratch, manually install the types package:
|
||||
|
||||
```shell
|
||||
npm install @yaakapp/api
|
||||
npm install -D @yaakapp/api
|
||||
```
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@yaakapp/api",
|
||||
"version": "0.6.4",
|
||||
"version": "0.6.6",
|
||||
"keywords": [
|
||||
"api-client",
|
||||
"insomnia-alternative",
|
||||
|
||||
8
packages/plugin-runtime-types/src/bindings/gen_api.ts
Normal file
8
packages/plugin-runtime-types/src/bindings/gen_api.ts
Normal file
@@ -0,0 +1,8 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
import type { PluginVersion } from "./gen_search.js";
|
||||
|
||||
export type PluginNameVersion = { name: string, version: string, };
|
||||
|
||||
export type PluginSearchResponse = { plugins: Array<PluginVersion>, };
|
||||
|
||||
export type PluginUpdatesResponse = { plugins: Array<PluginNameVersion>, };
|
||||
@@ -6,6 +6,10 @@ export type BootRequest = { dir: string, watch: boolean, };
|
||||
|
||||
export type BootResponse = { name: string, version: string, };
|
||||
|
||||
export type CallGrpcRequestActionArgs = { grpcRequest: GrpcRequest, protoFiles: Array<string>, };
|
||||
|
||||
export type CallGrpcRequestActionRequest = { index: number, pluginRefId: string, args: CallGrpcRequestActionArgs, };
|
||||
|
||||
export type CallHttpAuthenticationActionArgs = { contextId: string, values: { [key in string]?: JsonPrimitive }, };
|
||||
|
||||
export type CallHttpAuthenticationActionRequest = { index: number, pluginRefId: string, args: CallHttpAuthenticationActionArgs, };
|
||||
@@ -336,14 +340,14 @@ export type GetCookieValueRequest = { name: string, };
|
||||
|
||||
export type GetCookieValueResponse = { value: string | null, };
|
||||
|
||||
export type GetGrpcRequestActionsResponse = { actions: Array<GrpcRequestAction>, pluginRefId: string, };
|
||||
|
||||
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, };
|
||||
@@ -356,6 +360,12 @@ export type GetKeyValueResponse = { value?: string, };
|
||||
|
||||
export type GetTemplateFunctionsResponse = { functions: Array<TemplateFunction>, pluginRefId: string, };
|
||||
|
||||
export type GetThemesRequest = Record<string, never>;
|
||||
|
||||
export type GetThemesResponse = { themes: Array<Theme>, };
|
||||
|
||||
export type GrpcRequestAction = { label: string, icon?: Icon, };
|
||||
|
||||
export type HttpAuthenticationAction = { label: string, icon?: Icon, };
|
||||
|
||||
export type HttpHeader = { name: string, value: string, };
|
||||
@@ -372,7 +382,7 @@ export type ImportResponse = { resources: ImportResources, };
|
||||
|
||||
export type InternalEvent = { id: string, pluginRefId: string, pluginName: string, replyId: string | null, windowContext: PluginWindowContext, 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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "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": "window_close_event" } | { "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 InternalEventPayload = { "type": "boot_request" } & BootRequest | { "type": "boot_response" } & BootResponse | { "type": "reload_request" } & EmptyPayload | { "type": "reload_response" } & BootResponse | { "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": "list_cookie_names_request" } & ListCookieNamesRequest | { "type": "list_cookie_names_response" } & ListCookieNamesResponse | { "type": "get_cookie_value_request" } & GetCookieValueRequest | { "type": "get_cookie_value_response" } & GetCookieValueResponse | { "type": "get_http_request_actions_request" } & EmptyPayload | { "type": "get_http_request_actions_response" } & GetHttpRequestActionsResponse | { "type": "call_http_request_action_request" } & CallHttpRequestActionRequest | { "type": "get_grpc_request_actions_request" } & EmptyPayload | { "type": "get_grpc_request_actions_response" } & GetGrpcRequestActionsResponse | { "type": "call_grpc_request_action_request" } & CallGrpcRequestActionRequest | { "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": "render_grpc_request_request" } & RenderGrpcRequestRequest | { "type": "render_grpc_request_response" } & RenderGrpcRequestResponse | { "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": "window_close_event" } | { "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": "get_themes_request" } & GetThemesRequest | { "type": "get_themes_response" } & GetThemesResponse | { "type": "empty_response" } & EmptyPayload | { "type": "error_response" } & ErrorResponse;
|
||||
|
||||
export type JsonPrimitive = string | number | boolean | null;
|
||||
|
||||
@@ -404,6 +414,10 @@ required?: boolean, };
|
||||
|
||||
export type PromptTextResponse = { value: string | null, };
|
||||
|
||||
export type RenderGrpcRequestRequest = { grpcRequest: GrpcRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderGrpcRequestResponse = { grpcRequest: GrpcRequest, };
|
||||
|
||||
export type RenderHttpRequestRequest = { httpRequest: HttpRequest, purpose: RenderPurpose, };
|
||||
|
||||
export type RenderHttpRequestResponse = { httpRequest: HttpRequest, };
|
||||
@@ -436,6 +450,32 @@ export type TemplateRenderRequest = { data: JsonValue, purpose: RenderPurpose, }
|
||||
|
||||
export type TemplateRenderResponse = { data: JsonValue, };
|
||||
|
||||
export type Theme = {
|
||||
/**
|
||||
* How the theme is identified. This should never be changed
|
||||
*/
|
||||
id: string,
|
||||
/**
|
||||
* The friendly name of the theme to be displayed to the user
|
||||
*/
|
||||
label: string,
|
||||
/**
|
||||
* Whether the theme will be used for dark or light appearance
|
||||
*/
|
||||
dark: boolean,
|
||||
/**
|
||||
* The default top-level colors for the theme
|
||||
*/
|
||||
base: ThemeComponentColors,
|
||||
/**
|
||||
* Optionally override theme for individual UI components for more control
|
||||
*/
|
||||
components?: ThemeComponents, };
|
||||
|
||||
export type ThemeComponentColors = { surface?: string, surfaceHighlight?: string, surfaceActive?: string, text?: string, textSubtle?: string, textSubtlest?: string, border?: string, borderSubtle?: string, borderFocus?: string, shadow?: string, backdrop?: string, selection?: string, primary?: string, secondary?: string, info?: string, success?: string, notice?: string, warning?: string, danger?: string, };
|
||||
|
||||
export type ThemeComponents = { dialog?: ThemeComponentColors, menu?: ThemeComponentColors, toast?: ThemeComponentColors, sidebar?: ThemeComponentColors, responsePane?: ThemeComponentColors, appHeader?: ThemeComponentColors, button?: ThemeComponentColors, banner?: ThemeComponentColors, templateTag?: ThemeComponentColors, urlBar?: ThemeComponentColors, editor?: ThemeComponentColors, input?: ThemeComponentColors, };
|
||||
|
||||
export type WindowNavigateEvent = { url: string, };
|
||||
|
||||
export type WindowSize = { width: number, height: number, };
|
||||
|
||||
5
packages/plugin-runtime-types/src/bindings/gen_search.ts
Normal file
5
packages/plugin-runtime-types/src/bindings/gen_search.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
|
||||
|
||||
export type PluginMetadata = { version: string, name: string, displayName: string, description: string | null, homepageUrl: string | null, repositoryUrl: string | null, };
|
||||
|
||||
export type PluginVersion = { id: string, version: string, url: string, description: string | null, name: string, displayName: string, homepageUrl: string | null, repositoryUrl: string | null, checksum: string, readme: string | null, yanked: boolean, };
|
||||
@@ -9,6 +9,8 @@ import type {
|
||||
OpenWindowRequest,
|
||||
PromptTextRequest,
|
||||
PromptTextResponse,
|
||||
RenderGrpcRequestRequest,
|
||||
RenderGrpcRequestResponse,
|
||||
RenderHttpRequestRequest,
|
||||
RenderHttpRequestResponse,
|
||||
SendHttpRequestRequest,
|
||||
@@ -45,6 +47,9 @@ export interface Context {
|
||||
listNames(): Promise<ListCookieNamesResponse['names']>;
|
||||
getValue(args: GetCookieValueRequest): Promise<GetCookieValueResponse['value']>;
|
||||
};
|
||||
grpcRequest: {
|
||||
render(args: RenderGrpcRequestRequest): Promise<RenderGrpcRequestResponse['grpcRequest']>;
|
||||
};
|
||||
httpRequest: {
|
||||
send(args: SendHttpRequestRequest): Promise<SendHttpRequestResponse['httpResponse']>;
|
||||
getById(args: GetHttpRequestByIdRequest): Promise<GetHttpRequestByIdResponse['httpRequest']>;
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { CallGrpcRequestActionArgs, GrpcRequestAction } from '../bindings/gen_events';
|
||||
import type { Context } from './Context';
|
||||
|
||||
export type GrpcRequestActionPlugin = GrpcRequestAction & {
|
||||
onSelect(ctx: Context, args: CallGrpcRequestActionArgs): Promise<void> | void;
|
||||
};
|
||||
@@ -1,5 +1,5 @@
|
||||
import { ImportResources } from '../bindings/gen_events';
|
||||
import { AtLeast } from '../helpers';
|
||||
import { AtLeast, MaybePromise } from '../helpers';
|
||||
import type { Context } from './Context';
|
||||
|
||||
type RootFields = 'name' | 'id' | 'model';
|
||||
@@ -21,5 +21,8 @@ export type ImportPluginResponse = null | {
|
||||
export type ImporterPlugin = {
|
||||
name: string;
|
||||
description?: string;
|
||||
onImport(ctx: Context, args: { text: string }): Promise<ImportPluginResponse>;
|
||||
onImport(
|
||||
ctx: Context,
|
||||
args: { text: string },
|
||||
): MaybePromise<ImportPluginResponse | null | undefined>;
|
||||
};
|
||||
|
||||
@@ -1,8 +1,3 @@
|
||||
import { Index } from "../themes";
|
||||
import { Context } from "./Context";
|
||||
import { Theme } from '../bindings/gen_events';
|
||||
|
||||
export type ThemePlugin = {
|
||||
name: string;
|
||||
description?: string;
|
||||
getTheme(ctx: Context, fileContents: string): Promise<Index>;
|
||||
};
|
||||
export type ThemePlugin = Theme;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { AuthenticationPlugin } from './AuthenticationPlugin';
|
||||
import type { FilterPlugin } from './FilterPlugin';
|
||||
import { GrpcRequestActionPlugin } from './GrpcRequestActionPlugin';
|
||||
import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin';
|
||||
import type { ImporterPlugin } from './ImporterPlugin';
|
||||
import type { TemplateFunctionPlugin } from './TemplateFunctionPlugin';
|
||||
@@ -12,9 +13,10 @@ export type { Context } from './Context';
|
||||
*/
|
||||
export type PluginDefinition = {
|
||||
importer?: ImporterPlugin;
|
||||
theme?: ThemePlugin;
|
||||
themes?: ThemePlugin[];
|
||||
filter?: FilterPlugin;
|
||||
authentication?: AuthenticationPlugin;
|
||||
httpRequestActions?: HttpRequestActionPlugin[];
|
||||
grpcRequestActions?: GrpcRequestActionPlugin[];
|
||||
templateFunctions?: TemplateFunctionPlugin[];
|
||||
};
|
||||
|
||||
@@ -2,12 +2,17 @@
|
||||
"compilerOptions": {
|
||||
"module": "node16",
|
||||
"target": "es6",
|
||||
"lib": ["es2021"],
|
||||
"lib": [
|
||||
"es2021",
|
||||
"dom"
|
||||
],
|
||||
"declaration": true,
|
||||
"declarationDir": "./lib",
|
||||
"outDir": "./lib",
|
||||
"strict": true,
|
||||
"types": ["node"]
|
||||
"types": [
|
||||
"node"
|
||||
]
|
||||
},
|
||||
"files": [
|
||||
"src/index.ts"
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import {
|
||||
BootRequest,
|
||||
BootResponse,
|
||||
DeleteKeyValueResponse,
|
||||
FindHttpResponsesResponse,
|
||||
FormInput,
|
||||
GetCookieValueRequest,
|
||||
GetCookieValueResponse,
|
||||
GetHttpRequestByIdResponse,
|
||||
GetKeyValueResponse,
|
||||
GetKeyValueResponse, GrpcRequestAction,
|
||||
HttpAuthenticationAction,
|
||||
HttpRequestAction,
|
||||
InternalEvent,
|
||||
@@ -15,6 +16,7 @@ import {
|
||||
PluginWindowContext,
|
||||
PromptTextResponse,
|
||||
RenderHttpRequestResponse,
|
||||
RenderGrpcRequestResponse,
|
||||
SendHttpRequestResponse,
|
||||
TemplateFunction,
|
||||
TemplateFunctionArg,
|
||||
@@ -52,9 +54,22 @@ export class PluginInstance {
|
||||
|
||||
// Reload plugin if the JS or package.json changes
|
||||
const windowContextNone: PluginWindowContext = { type: 'none' };
|
||||
|
||||
this.#mod = {};
|
||||
this.#pkg = JSON.parse(readFileSync(this.#pathPkg(), 'utf8'));
|
||||
|
||||
const bootResponse: BootResponse = {
|
||||
name: this.#pkg.name ?? 'unknown',
|
||||
version: this.#pkg.version ?? '0.0.1',
|
||||
};
|
||||
|
||||
const fileChangeCallback = async () => {
|
||||
this.#importModule();
|
||||
return this.#sendPayload(windowContextNone, { type: 'reload_response' }, null);
|
||||
return this.#sendPayload(
|
||||
windowContextNone,
|
||||
{ type: 'reload_response', ...bootResponse },
|
||||
null,
|
||||
);
|
||||
};
|
||||
|
||||
if (this.#workerData.bootRequest.watch) {
|
||||
@@ -62,12 +77,6 @@ export class PluginInstance {
|
||||
watchFile(this.#pathPkg(), fileChangeCallback);
|
||||
}
|
||||
|
||||
this.#mod = {};
|
||||
this.#pkg = JSON.parse(readFileSync(this.#pathPkg(), 'utf8'));
|
||||
|
||||
// TODO: Re-implement this now that we're not using workers
|
||||
// prefixStdout(`[plugin][${this.#pkg.name}] %s`);
|
||||
|
||||
this.#importModule();
|
||||
}
|
||||
|
||||
@@ -137,6 +146,24 @@ export class PluginInstance {
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'get_grpc_request_actions_request' &&
|
||||
Array.isArray(this.#mod?.grpcRequestActions)
|
||||
) {
|
||||
const reply: GrpcRequestAction[] = this.#mod.grpcRequestActions.map((a) => ({
|
||||
...a,
|
||||
// Add everything except onSelect
|
||||
onSelect: undefined,
|
||||
}));
|
||||
const replyPayload: InternalEventPayload = {
|
||||
type: 'get_grpc_request_actions_response',
|
||||
pluginRefId: this.#workerData.pluginRefId,
|
||||
actions: reply,
|
||||
};
|
||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'get_http_request_actions_request' &&
|
||||
Array.isArray(this.#mod?.httpRequestActions)
|
||||
@@ -155,6 +182,15 @@ export class PluginInstance {
|
||||
return;
|
||||
}
|
||||
|
||||
if (payload.type === 'get_themes_request' && Array.isArray(this.#mod?.themes)) {
|
||||
const replyPayload: InternalEventPayload = {
|
||||
type: 'get_themes_response',
|
||||
themes: this.#mod.themes,
|
||||
};
|
||||
this.#sendPayload(windowContext, replyPayload, replyId);
|
||||
return;
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'get_template_functions_request' &&
|
||||
Array.isArray(this.#mod?.templateFunctions)
|
||||
@@ -191,13 +227,12 @@ export class PluginInstance {
|
||||
if (payload.type === 'get_http_authentication_config_request' && this.#mod?.authentication) {
|
||||
const { args, actions } = this.#mod.authentication;
|
||||
const resolvedArgs: FormInput[] = [];
|
||||
for (let i = 0; i < args.length; i++) {
|
||||
let v = args[i];
|
||||
if ('dynamic' in v) {
|
||||
for (const v of args) {
|
||||
if (v && 'dynamic' in v) {
|
||||
const dynamicAttrs = await v.dynamic(ctx, payload);
|
||||
const { dynamic, ...other } = v;
|
||||
resolvedArgs.push({ ...other, ...dynamicAttrs } as FormInput);
|
||||
} else {
|
||||
} else if (v) {
|
||||
resolvedArgs.push(v);
|
||||
}
|
||||
}
|
||||
@@ -258,6 +293,18 @@ export class PluginInstance {
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'call_grpc_request_action_request' &&
|
||||
Array.isArray(this.#mod.grpcRequestActions)
|
||||
) {
|
||||
const action = this.#mod.grpcRequestActions[payload.index];
|
||||
if (typeof action?.onSelect === 'function') {
|
||||
await action.onSelect(ctx, payload.args);
|
||||
this.#sendEmpty(windowContext, replyId);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
payload.type === 'call_template_function_request' &&
|
||||
Array.isArray(this.#mod?.templateFunctions)
|
||||
@@ -455,6 +502,19 @@ export class PluginInstance {
|
||||
return httpResponses;
|
||||
},
|
||||
},
|
||||
grpcRequest: {
|
||||
render: async (args) => {
|
||||
const payload = {
|
||||
type: 'render_grpc_request_request',
|
||||
...args,
|
||||
} as const;
|
||||
const { grpcRequest } = await this.#sendAndWaitForReply<RenderGrpcRequestResponse>(
|
||||
event.windowContext,
|
||||
payload,
|
||||
);
|
||||
return grpcRequest;
|
||||
},
|
||||
},
|
||||
httpRequest: {
|
||||
getById: async (args) => {
|
||||
const payload = {
|
||||
@@ -579,20 +639,20 @@ function applyFormInputDefaults(
|
||||
}
|
||||
}
|
||||
|
||||
const watchedFiles: Record<string, Stats> = {};
|
||||
const watchedFiles: Record<string, Stats | null> = {};
|
||||
|
||||
/**
|
||||
* Watch a file and trigger callback on change.
|
||||
* Watch a file and trigger a callback on change.
|
||||
*
|
||||
* We also track the stat for each file because fs.watch() will
|
||||
* trigger a "change" event when the access date changes
|
||||
* trigger a "change" event when the access date changes.
|
||||
*/
|
||||
function watchFile(filepath: string, cb: (filepath: string) => void) {
|
||||
function watchFile(filepath: string, cb: () => void) {
|
||||
watch(filepath, () => {
|
||||
const stat = statSync(filepath);
|
||||
if (stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) {
|
||||
cb(filepath);
|
||||
const stat = statSync(filepath, { throwIfNoEntry: false });
|
||||
if (stat == null || stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) {
|
||||
watchedFiles[filepath] = stat ?? null;
|
||||
cb();
|
||||
}
|
||||
watchedFiles[filepath] = stat;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -53,3 +53,7 @@ async function handleIncoming(msg: string) {
|
||||
|
||||
plugin.sendToWorker(pluginEvent);
|
||||
}
|
||||
|
||||
process.on('unhandledRejection', (reason, promise) => {
|
||||
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
|
||||
});
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/exporter-curl",
|
||||
"name": "@yaak/action-copy-curl",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,27 @@
|
||||
import { HttpRequest, PluginDefinition } from '@yaakapp/api';
|
||||
import type { HttpRequest, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
const NEWLINE = '\\\n ';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
httpRequestActions: [{
|
||||
label: 'Copy as Curl',
|
||||
icon: 'copy',
|
||||
async onSelect(ctx, args) {
|
||||
const rendered_request = await ctx.httpRequest.render({ httpRequest: args.httpRequest, purpose: 'preview' });
|
||||
const data = await convertToCurl(rendered_request);
|
||||
await ctx.clipboard.copyText(data);
|
||||
await ctx.toast.show({ message: 'Curl copied to clipboard', icon: 'copy', color: 'success' });
|
||||
httpRequestActions: [
|
||||
{
|
||||
label: 'Copy as Curl',
|
||||
icon: 'copy',
|
||||
async onSelect(ctx, args) {
|
||||
const rendered_request = await ctx.httpRequest.render({
|
||||
httpRequest: args.httpRequest,
|
||||
purpose: 'preview',
|
||||
});
|
||||
const data = await convertToCurl(rendered_request);
|
||||
await ctx.clipboard.copyText(data);
|
||||
await ctx.toast.show({
|
||||
message: 'Command copied to clipboard',
|
||||
icon: 'copy',
|
||||
color: 'success',
|
||||
});
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
};
|
||||
|
||||
export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
@@ -22,7 +31,6 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
if (request.method) xs.push('-X', request.method);
|
||||
if (request.url) xs.push(quote(request.url));
|
||||
|
||||
|
||||
xs.push(NEWLINE);
|
||||
|
||||
// Add URL params
|
||||
@@ -51,7 +59,10 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
} else if (typeof request.body?.query === 'string') {
|
||||
const body = { query: request.body.query || '', variables: maybeParseJSON(request.body.variables, undefined) };
|
||||
const body = {
|
||||
query: request.body.query || '',
|
||||
variables: maybeParseJSON(request.body.variables, undefined),
|
||||
};
|
||||
xs.push('--data-raw', `${quote(JSON.stringify(body))}`);
|
||||
xs.push(NEWLINE);
|
||||
} else if (typeof request.body?.text === 'string') {
|
||||
@@ -84,7 +95,7 @@ export async function convertToCurl(request: Partial<HttpRequest>) {
|
||||
}
|
||||
|
||||
function quote(arg: string): string {
|
||||
const escaped = arg.replace(/'/g, '\\\'');
|
||||
const escaped = arg.replace(/'/g, "\\'");
|
||||
return `'${escaped}'`;
|
||||
}
|
||||
|
||||
@@ -92,10 +103,10 @@ function onlyEnabled(v: { name?: string; enabled?: boolean }): boolean {
|
||||
return v.enabled !== false && !!v.name;
|
||||
}
|
||||
|
||||
function maybeParseJSON(v: any, fallback: any): string {
|
||||
function maybeParseJSON<T>(v: string, fallback: T) {
|
||||
try {
|
||||
return JSON.parse(v);
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return fallback;
|
||||
}
|
||||
}
|
||||
10
plugins/action-copy-grpcurl/package.json
Normal file
10
plugins/action-copy-grpcurl/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@yaak/action-copy-grpcurl",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
134
plugins/action-copy-grpcurl/src/index.ts
Normal file
134
plugins/action-copy-grpcurl/src/index.ts
Normal file
@@ -0,0 +1,134 @@
|
||||
import type { GrpcRequest, PluginDefinition } from '@yaakapp/api';
|
||||
import path from 'node:path';
|
||||
|
||||
const NEWLINE = '\\\n ';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
grpcRequestActions: [
|
||||
{
|
||||
label: 'Copy as gRPCurl',
|
||||
icon: 'copy',
|
||||
async onSelect(ctx, args) {
|
||||
const rendered_request = await ctx.grpcRequest.render({
|
||||
grpcRequest: args.grpcRequest,
|
||||
purpose: 'preview',
|
||||
});
|
||||
const data = await convert(rendered_request, args.protoFiles);
|
||||
await ctx.clipboard.copyText(data);
|
||||
await ctx.toast.show({
|
||||
message: 'Command copied to clipboard',
|
||||
icon: 'copy',
|
||||
color: 'success',
|
||||
});
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
export async function convert(request: Partial<GrpcRequest>, allProtoFiles: string[]) {
|
||||
const xs = ['grpcurl'];
|
||||
|
||||
if (request.url?.startsWith('http://')) {
|
||||
xs.push('-plaintext');
|
||||
}
|
||||
|
||||
const protoIncludes = allProtoFiles.filter((f) => !f.endsWith('.proto'));
|
||||
const protoFiles = allProtoFiles.filter((f) => f.endsWith('.proto'));
|
||||
|
||||
const inferredIncludes = new Set<string>();
|
||||
for (const f of protoFiles) {
|
||||
const protoDir = findParentProtoDir(f);
|
||||
if (protoDir) {
|
||||
inferredIncludes.add(protoDir);
|
||||
} else {
|
||||
inferredIncludes.add(path.join(f, '..'));
|
||||
inferredIncludes.add(path.join(f, '..', '..'));
|
||||
}
|
||||
}
|
||||
|
||||
for (const f of protoIncludes) {
|
||||
xs.push('-import-path', quote(f));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
for (const f of inferredIncludes.values()) {
|
||||
xs.push('-import-path', quote(f));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
for (const f of protoFiles) {
|
||||
xs.push('-proto', quote(f));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
// Add headers
|
||||
for (const h of (request.metadata ?? []).filter(onlyEnabled)) {
|
||||
xs.push('-H', quote(`${h.name}: ${h.value}`));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
// Add basic authentication
|
||||
if (request.authenticationType === 'basic') {
|
||||
const user = request.authentication?.username ?? '';
|
||||
const pass = request.authentication?.password ?? '';
|
||||
const encoded = btoa(`${user}:${pass}`);
|
||||
xs.push('-H', quote(`Authorization: Basic ${encoded}`));
|
||||
xs.push(NEWLINE);
|
||||
} else if (request.authenticationType === 'bearer') {
|
||||
// Add bearer authentication
|
||||
xs.push('-H', quote(`Authorization: Bearer ${request.authentication?.token ?? ''}`));
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
// Add form params
|
||||
if (request.message) {
|
||||
xs.push('-d', `${quote(JSON.stringify(JSON.parse(request.message)))}`);
|
||||
xs.push(NEWLINE);
|
||||
}
|
||||
|
||||
// Add the server address
|
||||
if (request.url) {
|
||||
const server = request.url.replace(/^https?:\/\//, ''); // remove protocol
|
||||
xs.push(server);
|
||||
}
|
||||
|
||||
// Add service + method
|
||||
if (request.service && request.method) {
|
||||
xs.push(`${request.service}/${request.method}`);
|
||||
}
|
||||
|
||||
xs.push(NEWLINE);
|
||||
|
||||
// Remove trailing newline
|
||||
if (xs[xs.length - 1] === NEWLINE) {
|
||||
xs.splice(xs.length - 1, 1);
|
||||
}
|
||||
|
||||
return xs.join(' ');
|
||||
}
|
||||
|
||||
function quote(arg: string): string {
|
||||
const escaped = arg.replace(/'/g, "\\'");
|
||||
return `'${escaped}'`;
|
||||
}
|
||||
|
||||
function onlyEnabled(v: { name?: string; enabled?: boolean }): boolean {
|
||||
return v.enabled !== false && !!v.name;
|
||||
}
|
||||
|
||||
function findParentProtoDir(startPath: string): string | null {
|
||||
let dir = path.resolve(startPath);
|
||||
|
||||
while (true) {
|
||||
if (path.basename(dir) === 'proto') {
|
||||
return dir;
|
||||
}
|
||||
|
||||
const parent = path.dirname(dir);
|
||||
if (parent === dir) {
|
||||
return null; // Reached root
|
||||
}
|
||||
|
||||
dir = parent;
|
||||
}
|
||||
}
|
||||
110
plugins/action-copy-grpcurl/tests/index.test.ts
Normal file
110
plugins/action-copy-grpcurl/tests/index.test.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { describe, expect, test } from 'vitest';
|
||||
import { convert } from '../src';
|
||||
|
||||
describe('exporter-curl', () => {
|
||||
test('Simple example', async () => {
|
||||
expect(
|
||||
await convert(
|
||||
{
|
||||
url: 'https://yaak.app',
|
||||
},
|
||||
[],
|
||||
),
|
||||
).toEqual([`grpcurl yaak.app`].join(` \\\n `));
|
||||
});
|
||||
test('Basic metadata', async () => {
|
||||
expect(
|
||||
await convert(
|
||||
{
|
||||
url: 'https://yaak.app',
|
||||
metadata: [
|
||||
{ name: 'aaa', value: 'AAA' },
|
||||
{ enabled: true, name: 'bbb', value: 'BBB' },
|
||||
{ enabled: false, name: 'disabled', value: 'ddd' },
|
||||
],
|
||||
},
|
||||
[],
|
||||
),
|
||||
).toEqual([`grpcurl -H 'aaa: AAA'`, `-H 'bbb: BBB'`, `yaak.app`].join(` \\\n `));
|
||||
});
|
||||
test('Single proto file', async () => {
|
||||
expect(await convert({ url: 'https://yaak.app' }, ['/foo/bar/baz.proto'])).toEqual(
|
||||
[
|
||||
`grpcurl -import-path '/foo/bar'`,
|
||||
`-import-path '/foo'`,
|
||||
`-proto '/foo/bar/baz.proto'`,
|
||||
`yaak.app`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
test('Multiple proto files, same dir', async () => {
|
||||
expect(
|
||||
await convert({ url: 'https://yaak.app' }, ['/foo/bar/aaa.proto', '/foo/bar/bbb.proto']),
|
||||
).toEqual(
|
||||
[
|
||||
`grpcurl -import-path '/foo/bar'`,
|
||||
`-import-path '/foo'`,
|
||||
`-proto '/foo/bar/aaa.proto'`,
|
||||
`-proto '/foo/bar/bbb.proto'`,
|
||||
`yaak.app`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
test('Multiple proto files, different dir', async () => {
|
||||
expect(
|
||||
await convert({ url: 'https://yaak.app' }, ['/aaa/bbb/ccc.proto', '/xxx/yyy/zzz.proto']),
|
||||
).toEqual(
|
||||
[
|
||||
`grpcurl -import-path '/aaa/bbb'`,
|
||||
`-import-path '/aaa'`,
|
||||
`-import-path '/xxx/yyy'`,
|
||||
`-import-path '/xxx'`,
|
||||
`-proto '/aaa/bbb/ccc.proto'`,
|
||||
`-proto '/xxx/yyy/zzz.proto'`,
|
||||
`yaak.app`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
test('Single include dir', async () => {
|
||||
expect(await convert({ url: 'https://yaak.app' }, ['/aaa/bbb'])).toEqual(
|
||||
[`grpcurl -import-path '/aaa/bbb'`, `yaak.app`].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
test('Multiple include dir', async () => {
|
||||
expect(await convert({ url: 'https://yaak.app' }, ['/aaa/bbb', '/xxx/yyy'])).toEqual(
|
||||
[`grpcurl -import-path '/aaa/bbb'`, `-import-path '/xxx/yyy'`, `yaak.app`].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
test('Mixed proto and dirs', async () => {
|
||||
expect(
|
||||
await convert({ url: 'https://yaak.app' }, ['/aaa/bbb', '/xxx/yyy', '/foo/bar.proto']),
|
||||
).toEqual(
|
||||
[
|
||||
`grpcurl -import-path '/aaa/bbb'`,
|
||||
`-import-path '/xxx/yyy'`,
|
||||
`-import-path '/foo'`,
|
||||
`-import-path '/'`,
|
||||
`-proto '/foo/bar.proto'`,
|
||||
`yaak.app`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
test('Sends data', async () => {
|
||||
expect(
|
||||
await convert(
|
||||
{
|
||||
url: 'https://yaak.app',
|
||||
message: JSON.stringify({ foo: 'bar', baz: 1.0 }, null, 2),
|
||||
},
|
||||
['/foo.proto'],
|
||||
),
|
||||
).toEqual(
|
||||
[
|
||||
`grpcurl -import-path '/'`,
|
||||
`-proto '/foo.proto'`,
|
||||
`-d '{"foo":"bar","baz":1}'`,
|
||||
`yaak.app`,
|
||||
].join(` \\\n `),
|
||||
);
|
||||
});
|
||||
});
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/auth-basic",
|
||||
"name": "@yaak/auth-basic",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/auth-bearer",
|
||||
"name": "@yaak/auth-bearer",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/auth-jwt",
|
||||
"name": "@yaak/auth-jwt",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonwebtoken": "^9.0.2"
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/auth-oauth2",
|
||||
"name": "@yaak/auth-oauth2",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { Context, HttpRequest, HttpUrlParameter } from '@yaakapp/api';
|
||||
import type { Context, HttpRequest, HttpUrlParameter } from '@yaakapp/api';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { AccessTokenRawResponse } from './store';
|
||||
import type { AccessTokenRawResponse } from './store';
|
||||
|
||||
export async function getAccessToken(
|
||||
export async function fetchAccessToken(
|
||||
ctx: Context,
|
||||
{
|
||||
accessTokenUrl,
|
||||
19
plugins/auth-oauth2/src/getAccessTokenIfNotExpired.ts
Normal file
19
plugins/auth-oauth2/src/getAccessTokenIfNotExpired.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import type { Context } from '@yaakapp/api';
|
||||
import type { AccessToken } from './store';
|
||||
import { getToken } from './store';
|
||||
|
||||
export async function getAccessTokenIfNotExpired(
|
||||
ctx: Context,
|
||||
contextId: string,
|
||||
): Promise<AccessToken | null> {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token == null || isTokenExpired(token)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return token;
|
||||
}
|
||||
|
||||
export function isTokenExpired(token: AccessToken) {
|
||||
return token.expiresAt && Date.now() > token.expiresAt;
|
||||
}
|
||||
@@ -1,29 +1,34 @@
|
||||
import { Context, HttpRequest } from '@yaakapp/api';
|
||||
import type { Context, HttpRequest } from '@yaakapp/api';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { AccessToken, AccessTokenRawResponse, deleteToken, getToken, storeToken } from './store';
|
||||
import { isTokenExpired } from './getAccessTokenIfNotExpired';
|
||||
import type { AccessToken, AccessTokenRawResponse } from './store';
|
||||
import { deleteToken, getToken, storeToken } from './store';
|
||||
|
||||
export async function getOrRefreshAccessToken(ctx: Context, contextId: string, {
|
||||
scope,
|
||||
accessTokenUrl,
|
||||
credentialsInBody,
|
||||
clientId,
|
||||
clientSecret,
|
||||
forceRefresh,
|
||||
}: {
|
||||
scope: string | null;
|
||||
accessTokenUrl: string;
|
||||
credentialsInBody: boolean;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
forceRefresh?: boolean;
|
||||
}): Promise<AccessToken | null> {
|
||||
export async function getOrRefreshAccessToken(
|
||||
ctx: Context,
|
||||
contextId: string,
|
||||
{
|
||||
scope,
|
||||
accessTokenUrl,
|
||||
credentialsInBody,
|
||||
clientId,
|
||||
clientSecret,
|
||||
forceRefresh,
|
||||
}: {
|
||||
scope: string | null;
|
||||
accessTokenUrl: string;
|
||||
credentialsInBody: boolean;
|
||||
clientId: string;
|
||||
clientSecret: string;
|
||||
forceRefresh?: boolean;
|
||||
},
|
||||
): Promise<AccessToken | null> {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const now = Date.now();
|
||||
const isExpired = token.expiresAt && now > token.expiresAt;
|
||||
const isExpired = isTokenExpired(token);
|
||||
|
||||
// Return the current access token if it's still valid
|
||||
if (!isExpired && !forceRefresh) {
|
||||
@@ -79,7 +84,9 @@ export async function getOrRefreshAccessToken(ctx: Context, contextId: string, {
|
||||
console.log('[oauth2] Got refresh token response', resp.status);
|
||||
|
||||
if (resp.status < 200 || resp.status >= 300) {
|
||||
throw new Error('Failed to refresh access token with status=' + resp.status + ' and body=' + body);
|
||||
throw new Error(
|
||||
'Failed to refresh access token with status=' + resp.status + ' and body=' + body,
|
||||
);
|
||||
}
|
||||
|
||||
let response;
|
||||
@@ -90,7 +97,9 @@ export async function getOrRefreshAccessToken(ctx: Context, contextId: string, {
|
||||
}
|
||||
|
||||
if (response.error) {
|
||||
throw new Error(`Failed to fetch access token with ${response.error} -> ${response.error_description}`);
|
||||
throw new Error(
|
||||
`Failed to fetch access token with ${response.error} -> ${response.error_description}`,
|
||||
);
|
||||
}
|
||||
|
||||
const newResponse: AccessTokenRawResponse = {
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import { Context } from '@yaakapp/api';
|
||||
import type { Context } from '@yaakapp/api';
|
||||
import { createHash, randomBytes } from 'node:crypto';
|
||||
import { getAccessToken } from '../getAccessToken';
|
||||
import { fetchAccessToken } from '../fetchAccessToken';
|
||||
import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken';
|
||||
import { AccessToken, getDataDirKey, storeToken } from '../store';
|
||||
import type { AccessToken } from '../store';
|
||||
import { getDataDirKey, storeToken } from '../store';
|
||||
|
||||
export const PKCE_SHA256 = 'S256';
|
||||
export const PKCE_PLAIN = 'plain';
|
||||
@@ -34,8 +35,8 @@ export async function getAuthorizationCode(
|
||||
audience: string | null;
|
||||
credentialsInBody: boolean;
|
||||
pkce: {
|
||||
challengeMethod: string | null;
|
||||
codeVerifier: string | null;
|
||||
challengeMethod: string;
|
||||
codeVerifier: string;
|
||||
} | null;
|
||||
tokenName: 'access_token' | 'id_token';
|
||||
},
|
||||
@@ -59,26 +60,25 @@ export async function getAuthorizationCode(
|
||||
if (state) authorizationUrl.searchParams.set('state', state);
|
||||
if (audience) authorizationUrl.searchParams.set('audience', audience);
|
||||
if (pkce) {
|
||||
const verifier = pkce.codeVerifier || createPkceCodeVerifier();
|
||||
const challengeMethod = pkce.challengeMethod || DEFAULT_PKCE_METHOD;
|
||||
authorizationUrl.searchParams.set(
|
||||
'code_challenge',
|
||||
createPkceCodeChallenge(verifier, challengeMethod),
|
||||
pkceCodeChallenge(pkce.codeVerifier, pkce.challengeMethod),
|
||||
);
|
||||
authorizationUrl.searchParams.set('code_challenge_method', challengeMethod);
|
||||
authorizationUrl.searchParams.set('code_challenge_method', pkce.challengeMethod);
|
||||
}
|
||||
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
const logsEnabled = (await ctx.store.get('enable_logs')) ?? false;
|
||||
console.log('[oauth2] Authorizing', authorizationUrlStr);
|
||||
const logsEnabled = (await ctx.store.get('enable_logs')) ?? false;
|
||||
const dataDirKey = await getDataDirKey(ctx, contextId);
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
console.log('[oauth2] Authorizing', authorizationUrlStr);
|
||||
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
const code = await new Promise<string>(async (resolve, reject) => {
|
||||
let foundCode = false;
|
||||
|
||||
let { close } = await ctx.window.openUrl({
|
||||
const { close } = await ctx.window.openUrl({
|
||||
url: authorizationUrlStr,
|
||||
label: 'oauth-authorization-url',
|
||||
dataDirKey: await getDataDirKey(ctx, contextId),
|
||||
dataDirKey,
|
||||
async onClose() {
|
||||
if (!foundCode) {
|
||||
reject(new Error('Authorization window closed'));
|
||||
@@ -89,6 +89,7 @@ export async function getAuthorizationCode(
|
||||
if (logsEnabled) console.log('[oauth2] Navigated to', urlStr);
|
||||
|
||||
if (url.searchParams.has('error')) {
|
||||
close();
|
||||
return reject(new Error(`Failed to authorize: ${url.searchParams.get('error')}`));
|
||||
}
|
||||
|
||||
@@ -101,37 +102,35 @@ export async function getAuthorizationCode(
|
||||
// Close the window here, because we don't need it anymore!
|
||||
foundCode = true;
|
||||
close();
|
||||
|
||||
console.log('[oauth2] Code found');
|
||||
const response = await getAccessToken(ctx, {
|
||||
grantType: 'authorization_code',
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
audience,
|
||||
credentialsInBody,
|
||||
params: [
|
||||
{ name: 'code', value: code },
|
||||
...(redirectUri ? [{ name: 'redirect_uri', value: redirectUri }] : []),
|
||||
],
|
||||
});
|
||||
|
||||
try {
|
||||
resolve(await storeToken(ctx, contextId, response, tokenName));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
resolve(code);
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
console.log('[oauth2] Code found');
|
||||
const response = await fetchAccessToken(ctx, {
|
||||
grantType: 'authorization_code',
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
scope,
|
||||
audience,
|
||||
credentialsInBody,
|
||||
params: [
|
||||
{ name: 'code', value: code },
|
||||
...(pkce ? [{ name: 'code_verifier', value: pkce.codeVerifier }] : []),
|
||||
...(redirectUri ? [{ name: 'redirect_uri', value: redirectUri }] : []),
|
||||
],
|
||||
});
|
||||
|
||||
return storeToken(ctx, contextId, response, tokenName);
|
||||
}
|
||||
|
||||
function createPkceCodeVerifier() {
|
||||
export function genPkceCodeVerifier() {
|
||||
return encodeForPkce(randomBytes(32));
|
||||
}
|
||||
|
||||
function createPkceCodeChallenge(verifier: string, method: string) {
|
||||
function pkceCodeChallenge(verifier: string, method: string) {
|
||||
if (method === 'plain') {
|
||||
return verifier;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Context } from '@yaakapp/api';
|
||||
import { getAccessToken } from '../getAccessToken';
|
||||
import type { Context } from '@yaakapp/api';
|
||||
import { fetchAccessToken } from '../fetchAccessToken';
|
||||
import { isTokenExpired } from '../getAccessTokenIfNotExpired';
|
||||
import { getToken, storeToken } from '../store';
|
||||
|
||||
export async function getClientCredentials(
|
||||
@@ -22,13 +23,11 @@ export async function getClientCredentials(
|
||||
},
|
||||
) {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token) {
|
||||
// resolve(token.response.access_token);
|
||||
// TODO: Refresh token if expired
|
||||
// return;
|
||||
if (token && !isTokenExpired(token)) {
|
||||
return token;
|
||||
}
|
||||
|
||||
const response = await getAccessToken(ctx, {
|
||||
const response = await fetchAccessToken(ctx, {
|
||||
grantType: 'client_credentials',
|
||||
accessTokenUrl,
|
||||
audience,
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
import { Context } from '@yaakapp/api';
|
||||
import { AccessToken, AccessTokenRawResponse, getToken, storeToken } from '../store';
|
||||
import type { Context } from '@yaakapp/api';
|
||||
import { isTokenExpired } from '../getAccessTokenIfNotExpired';
|
||||
import type { AccessToken, AccessTokenRawResponse} from '../store';
|
||||
import { getToken, storeToken } from '../store';
|
||||
|
||||
export function getImplicit(
|
||||
export async function getImplicit(
|
||||
ctx: Context,
|
||||
contextId: string,
|
||||
{
|
||||
@@ -24,31 +26,30 @@ export function getImplicit(
|
||||
tokenName: 'access_token' | 'id_token';
|
||||
},
|
||||
): Promise<AccessToken> {
|
||||
return new Promise(async (resolve, reject) => {
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token) {
|
||||
// resolve(token.response.access_token);
|
||||
// TODO: Refresh token if expired
|
||||
// return;
|
||||
}
|
||||
const token = await getToken(ctx, contextId);
|
||||
if (token != null && !isTokenExpired(token)) {
|
||||
return token;
|
||||
}
|
||||
|
||||
const authorizationUrl = new URL(`${authorizationUrlRaw ?? ''}`);
|
||||
authorizationUrl.searchParams.set('response_type', 'token');
|
||||
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 (audience) authorizationUrl.searchParams.set('audience', audience);
|
||||
if (responseType.includes('id_token')) {
|
||||
authorizationUrl.searchParams.set(
|
||||
'nonce',
|
||||
String(Math.floor(Math.random() * 9999999999999) + 1),
|
||||
);
|
||||
}
|
||||
const authorizationUrl = new URL(`${authorizationUrlRaw ?? ''}`);
|
||||
authorizationUrl.searchParams.set('response_type', 'token');
|
||||
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 (audience) authorizationUrl.searchParams.set('audience', audience);
|
||||
if (responseType.includes('id_token')) {
|
||||
authorizationUrl.searchParams.set(
|
||||
'nonce',
|
||||
String(Math.floor(Math.random() * 9999999999999) + 1),
|
||||
);
|
||||
}
|
||||
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
// eslint-disable-next-line no-async-promise-executor
|
||||
const newToken = await new Promise<AccessToken>(async (resolve, reject) => {
|
||||
let foundAccessToken = false;
|
||||
let { close } = await ctx.window.openUrl({
|
||||
const authorizationUrlStr = authorizationUrl.toString();
|
||||
const { close } = await ctx.window.openUrl({
|
||||
url: authorizationUrlStr,
|
||||
label: 'oauth-authorization-url',
|
||||
async onClose() {
|
||||
@@ -76,11 +77,13 @@ export function getImplicit(
|
||||
|
||||
const response = Object.fromEntries(params) as unknown as AccessTokenRawResponse;
|
||||
try {
|
||||
resolve(await storeToken(ctx, contextId, response));
|
||||
resolve(storeToken(ctx, contextId, response));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
return newToken;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,8 @@
|
||||
import { Context } from '@yaakapp/api';
|
||||
import { getAccessToken } from '../getAccessToken';
|
||||
import type { Context } from '@yaakapp/api';
|
||||
import { fetchAccessToken } from '../fetchAccessToken';
|
||||
import { getOrRefreshAccessToken } from '../getOrRefreshAccessToken';
|
||||
import { AccessToken, storeToken } from '../store';
|
||||
import type { AccessToken} from '../store';
|
||||
import { storeToken } from '../store';
|
||||
|
||||
export async function getPassword(
|
||||
ctx: Context,
|
||||
@@ -37,7 +38,7 @@ export async function getPassword(
|
||||
return token;
|
||||
}
|
||||
|
||||
const response = await getAccessToken(ctx, {
|
||||
const response = await fetchAccessToken(ctx, {
|
||||
accessTokenUrl,
|
||||
clientId,
|
||||
clientSecret,
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import {
|
||||
import type {
|
||||
Context,
|
||||
FormInputSelectOption,
|
||||
GetHttpAuthenticationConfigRequest,
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
PluginDefinition,
|
||||
} from '@yaakapp/api';
|
||||
import {
|
||||
genPkceCodeVerifier,
|
||||
DEFAULT_PKCE_METHOD,
|
||||
getAuthorizationCode,
|
||||
PKCE_PLAIN,
|
||||
@@ -14,7 +15,8 @@ import {
|
||||
import { getClientCredentials } from './grants/clientCredentials';
|
||||
import { getImplicit } from './grants/implicit';
|
||||
import { getPassword } from './grants/password';
|
||||
import { AccessToken, deleteToken, getToken, resetDataDirKey } from './store';
|
||||
import type { AccessToken } from './store';
|
||||
import { deleteToken, getToken, resetDataDirKey } from './store';
|
||||
|
||||
type GrantType = 'authorization_code' | 'implicit' | 'password' | 'client_credentials';
|
||||
|
||||
@@ -219,9 +221,9 @@ export const plugin: PluginDefinition = {
|
||||
},
|
||||
{
|
||||
type: 'text',
|
||||
name: 'pkceCodeVerifier',
|
||||
name: 'pkceCodeChallenge',
|
||||
label: 'Code Verifier',
|
||||
placeholder: 'Automatically generated if not provided',
|
||||
placeholder: 'Automatically generated when not set',
|
||||
optional: true,
|
||||
dynamic: hiddenIfNot(['authorization_code'], ({ usePkce }) => !!usePkce),
|
||||
},
|
||||
@@ -325,8 +327,8 @@ export const plugin: PluginDefinition = {
|
||||
credentialsInBody,
|
||||
pkce: values.usePkce
|
||||
? {
|
||||
challengeMethod: stringArg(values, 'pkceChallengeMethod'),
|
||||
codeVerifier: stringArgOrNull(values, 'pkceCodeVerifier'),
|
||||
challengeMethod: stringArg(values, 'pkceChallengeMethod') || DEFAULT_PKCE_METHOD,
|
||||
codeVerifier: stringArg(values, 'pkceCodeVerifier') || genPkceCodeVerifier(),
|
||||
}
|
||||
: null,
|
||||
tokenName: tokenName,
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/filter-jsonpath",
|
||||
"name": "@yaak/filter-jsonpath",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonpath-plus": "^10.3.0"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/filter-xpath",
|
||||
"name": "@yaak/filter-xpath",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.10",
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/importer-curl",
|
||||
"name": "@yaak/importer-curl",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"shell-quote": "^1.8.1"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/importer-insomnia",
|
||||
"name": "@yaak/importer-insomnia",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"yaml": "^2.4.2"
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/importer-openapi",
|
||||
"name": "@yaak/importer-openapi",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"openapi-to-postmanv2": "^5.0.0",
|
||||
|
||||
@@ -1,32 +1,23 @@
|
||||
import { Context, Environment, Folder, HttpRequest, PluginDefinition, Workspace } from '@yaakapp/api';
|
||||
import { convertPostman } from '@yaak/importer-postman/src';
|
||||
import type { Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { ImportPluginResponse } from '@yaakapp/api/lib/plugins/ImporterPlugin';
|
||||
import { convert } from 'openapi-to-postmanv2';
|
||||
import { convertPostman } from '@yaakapp/importer-postman/src';
|
||||
|
||||
type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
|
||||
|
||||
interface ExportResources {
|
||||
workspaces: AtLeast<Workspace, 'name' | 'id' | 'model'>[];
|
||||
environments: AtLeast<Environment, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||
httpRequests: AtLeast<HttpRequest, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||
folders: AtLeast<Folder, 'name' | 'id' | 'model' | 'workspaceId'>[];
|
||||
}
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
importer: {
|
||||
name: 'OpenAPI',
|
||||
description: 'Import OpenAPI collections',
|
||||
onImport(_ctx: Context, args: { text: string }) {
|
||||
return convertOpenApi(args.text) as any;
|
||||
return convertOpenApi(args.text);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export async function convertOpenApi(
|
||||
contents: string,
|
||||
): Promise<{ resources: ExportResources } | undefined> {
|
||||
export async function convertOpenApi(contents: string): Promise<ImportPluginResponse | undefined> {
|
||||
let postmanCollection;
|
||||
try {
|
||||
postmanCollection = await new Promise((resolve, reject) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
convert({ type: 'string', data: contents }, {}, (err, result: any) => {
|
||||
if (err != null) reject(err);
|
||||
|
||||
@@ -35,7 +26,7 @@ export async function convertOpenApi(
|
||||
}
|
||||
});
|
||||
});
|
||||
} catch (err) {
|
||||
} catch {
|
||||
// Probably not an OpenAPI file, so skip it
|
||||
return undefined;
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/importer-postman",
|
||||
"name": "@yaak/importer-postman",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"main": "./build/index.js",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,15 @@
|
||||
import {
|
||||
import type {
|
||||
Context,
|
||||
Environment,
|
||||
Folder,
|
||||
HttpRequest,
|
||||
HttpRequestHeader,
|
||||
HttpUrlParameter,
|
||||
PartialImportResources,
|
||||
PluginDefinition,
|
||||
Workspace,
|
||||
} from '@yaakapp/api';
|
||||
import type { ImportPluginResponse } from '@yaakapp/api/lib/plugins/ImporterPlugin';
|
||||
|
||||
const POSTMAN_2_1_0_SCHEMA = 'https://schema.getpostman.com/json/collection/v2.1.0/collection.json';
|
||||
const POSTMAN_2_0_0_SCHEMA = 'https://schema.getpostman.com/json/collection/v2.0.0/collection.json';
|
||||
@@ -27,19 +29,19 @@ export const plugin: PluginDefinition = {
|
||||
name: 'Postman',
|
||||
description: 'Import postman collections',
|
||||
onImport(_ctx: Context, args: { text: string }) {
|
||||
return convertPostman(args.text) as any;
|
||||
return convertPostman(args.text);
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
export function convertPostman(
|
||||
contents: string,
|
||||
): { resources: ExportResources } | undefined {
|
||||
export function convertPostman(contents: string): ImportPluginResponse | undefined {
|
||||
const root = parseJSONToRecord(contents);
|
||||
if (root == null) return;
|
||||
|
||||
const info = toRecord(root.info);
|
||||
const isValidSchema = VALID_SCHEMAS.includes(info.schema);
|
||||
const isValidSchema = VALID_SCHEMAS.includes(
|
||||
typeof info.schema === 'string' ? info.schema : 'n/a',
|
||||
);
|
||||
if (!isValidSchema || !Array.isArray(root.item)) {
|
||||
return;
|
||||
}
|
||||
@@ -53,11 +55,17 @@ export function convertPostman(
|
||||
folders: [],
|
||||
};
|
||||
|
||||
const rawDescription = info.description;
|
||||
const description =
|
||||
typeof rawDescription === 'object' && rawDescription !== null && 'content' in rawDescription
|
||||
? String(rawDescription.content)
|
||||
: String(rawDescription);
|
||||
|
||||
const workspace: ExportResources['workspaces'][0] = {
|
||||
model: 'workspace',
|
||||
id: generateId('workspace'),
|
||||
name: info.name || 'Postman Import',
|
||||
description: info.description?.content ?? info.description,
|
||||
name: info.name ? String(info.name) : 'Postman Import',
|
||||
description,
|
||||
};
|
||||
exportResources.workspaces.push(workspace);
|
||||
|
||||
@@ -68,14 +76,14 @@ export function convertPostman(
|
||||
name: 'Global Variables',
|
||||
workspaceId: workspace.id,
|
||||
variables:
|
||||
root.variable?.map((v: any) => ({
|
||||
toArray<{ key: string; value: string }>(root.variable).map((v) => ({
|
||||
name: v.key,
|
||||
value: v.value,
|
||||
})) ?? [],
|
||||
};
|
||||
exportResources.environments.push(environment);
|
||||
|
||||
const importItem = (v: Record<string, any>, folderId: string | null = null) => {
|
||||
const importItem = (v: Record<string, unknown>, folderId: string | null = null) => {
|
||||
if (typeof v.name === 'string' && Array.isArray(v.item)) {
|
||||
const folder: ExportResources['folders'][0] = {
|
||||
model: 'folder',
|
||||
@@ -94,7 +102,11 @@ export function convertPostman(
|
||||
const requestAuthPath = importAuth(r.auth);
|
||||
const authPatch = requestAuthPath.authenticationType == null ? globalAuth : requestAuthPath;
|
||||
|
||||
const headers: HttpRequestHeader[] = toArray(r.header).map((h) => {
|
||||
const headers: HttpRequestHeader[] = toArray<{
|
||||
key: string;
|
||||
value: string;
|
||||
disabled?: boolean;
|
||||
}>(r.header).map((h) => {
|
||||
return {
|
||||
name: h.key,
|
||||
value: h.value,
|
||||
@@ -104,7 +116,9 @@ export function convertPostman(
|
||||
|
||||
// Add body headers only if they don't already exist
|
||||
for (const bodyPatchHeader of bodyPatch.headers) {
|
||||
const existingHeader = headers.find(h => h.name.toLowerCase() === bodyPatchHeader.name.toLowerCase());
|
||||
const existingHeader = headers.find(
|
||||
(h) => h.name.toLowerCase() === bodyPatchHeader.name.toLowerCase(),
|
||||
);
|
||||
if (existingHeader) {
|
||||
continue;
|
||||
}
|
||||
@@ -119,8 +133,8 @@ export function convertPostman(
|
||||
workspaceId: workspace.id,
|
||||
folderId,
|
||||
name: v.name,
|
||||
description: v.description || undefined,
|
||||
method: r.method || 'GET',
|
||||
description: v.description ? String(v.description) : undefined,
|
||||
method: typeof r.method === 'string' ? r.method : 'GET',
|
||||
url,
|
||||
urlParameters,
|
||||
body: bodyPatch.body,
|
||||
@@ -139,17 +153,19 @@ export function convertPostman(
|
||||
importItem(item);
|
||||
}
|
||||
|
||||
const resources = deleteUndefinedAttrs(convertTemplateSyntax(exportResources));
|
||||
const resources = deleteUndefinedAttrs(
|
||||
convertTemplateSyntax(exportResources),
|
||||
) as PartialImportResources;
|
||||
|
||||
return { resources };
|
||||
}
|
||||
|
||||
function convertUrl(url: string | any): Pick<HttpRequest, 'url' | 'urlParameters'> {
|
||||
if (typeof url === 'string') {
|
||||
return { url, urlParameters: [] };
|
||||
function convertUrl(rawUrl: string | unknown): Pick<HttpRequest, 'url' | 'urlParameters'> {
|
||||
if (typeof rawUrl === 'string') {
|
||||
return { url: rawUrl, urlParameters: [] };
|
||||
}
|
||||
|
||||
url = toRecord(url);
|
||||
const url = toRecord(rawUrl);
|
||||
|
||||
let v = '';
|
||||
|
||||
@@ -199,10 +215,8 @@ function convertUrl(url: string | any): Pick<HttpRequest, 'url' | 'urlParameters
|
||||
return { url: v, urlParameters: params };
|
||||
}
|
||||
|
||||
function importAuth(
|
||||
rawAuth: any,
|
||||
): Pick<HttpRequest, 'authentication' | 'authenticationType'> {
|
||||
const auth = toRecord(rawAuth);
|
||||
function importAuth(rawAuth: unknown): Pick<HttpRequest, 'authentication' | 'authenticationType'> {
|
||||
const auth = toRecord<{ username?: string; password?: string; token?: string }>(rawAuth);
|
||||
if ('basic' in auth) {
|
||||
return {
|
||||
authenticationType: 'basic',
|
||||
@@ -223,8 +237,22 @@ function importAuth(
|
||||
}
|
||||
}
|
||||
|
||||
function importBody(rawBody: any): Pick<HttpRequest, 'body' | 'bodyType' | 'headers'> {
|
||||
const body = toRecord(rawBody);
|
||||
function importBody(rawBody: unknown): Pick<HttpRequest, 'body' | 'bodyType' | 'headers'> {
|
||||
const body = toRecord(rawBody) as {
|
||||
mode: string;
|
||||
graphql: { query?: string; variables?: string };
|
||||
urlencoded?: { key?: string; value?: string; disabled?: boolean }[];
|
||||
formdata?: {
|
||||
key?: string;
|
||||
value?: string;
|
||||
disabled?: boolean;
|
||||
contentType?: string;
|
||||
src?: string;
|
||||
}[];
|
||||
raw?: string;
|
||||
options?: { raw?: { language?: string } };
|
||||
file?: { src?: string };
|
||||
};
|
||||
if (body.mode === 'graphql') {
|
||||
return {
|
||||
headers: [
|
||||
@@ -237,7 +265,10 @@ function importBody(rawBody: any): Pick<HttpRequest, 'body' | 'bodyType' | 'head
|
||||
bodyType: 'graphql',
|
||||
body: {
|
||||
text: JSON.stringify(
|
||||
{ query: body.graphql.query, variables: parseJSONToRecord(body.graphql.variables) },
|
||||
{
|
||||
query: body.graphql?.query || '',
|
||||
variables: parseJSONToRecord(body.graphql?.variables || '{}'),
|
||||
},
|
||||
null,
|
||||
2,
|
||||
),
|
||||
@@ -254,7 +285,7 @@ function importBody(rawBody: any): Pick<HttpRequest, 'body' | 'bodyType' | 'head
|
||||
],
|
||||
bodyType: 'application/x-www-form-urlencoded',
|
||||
body: {
|
||||
form: toArray(body.urlencoded).map((f) => ({
|
||||
form: toArray<NonNullable<typeof body.urlencoded>[0]>(body.urlencoded).map((f) => ({
|
||||
enabled: !f.disabled,
|
||||
name: f.key ?? '',
|
||||
value: f.value ?? '',
|
||||
@@ -272,19 +303,19 @@ function importBody(rawBody: any): Pick<HttpRequest, 'body' | 'bodyType' | 'head
|
||||
],
|
||||
bodyType: 'multipart/form-data',
|
||||
body: {
|
||||
form: toArray(body.formdata).map((f) =>
|
||||
form: toArray<NonNullable<typeof body.formdata>[0]>(body.formdata).map((f) =>
|
||||
f.src != null
|
||||
? {
|
||||
enabled: !f.disabled,
|
||||
contentType: f.contentType ?? null,
|
||||
name: f.key ?? '',
|
||||
file: f.src ?? '',
|
||||
}
|
||||
enabled: !f.disabled,
|
||||
contentType: f.contentType ?? null,
|
||||
name: f.key ?? '',
|
||||
file: f.src ?? '',
|
||||
}
|
||||
: {
|
||||
enabled: !f.disabled,
|
||||
name: f.key ?? '',
|
||||
value: f.value ?? '',
|
||||
},
|
||||
enabled: !f.disabled,
|
||||
name: f.key ?? '',
|
||||
value: f.value ?? '',
|
||||
},
|
||||
),
|
||||
},
|
||||
};
|
||||
@@ -315,21 +346,23 @@ function importBody(rawBody: any): Pick<HttpRequest, 'body' | 'bodyType' | 'head
|
||||
}
|
||||
}
|
||||
|
||||
function parseJSONToRecord(jsonStr: string): Record<string, any> | null {
|
||||
function parseJSONToRecord<T>(jsonStr: string): Record<string, T> | null {
|
||||
try {
|
||||
return toRecord(JSON.parse(jsonStr));
|
||||
} catch (err) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
function toRecord(value: any): Record<string, any> {
|
||||
if (Object.prototype.toString.call(value) === '[object Object]') return value;
|
||||
else return {};
|
||||
function toRecord<T>(value: Record<string, T> | unknown): Record<string, T> {
|
||||
if (value && typeof value === 'object' && !Array.isArray(value)) {
|
||||
return value as Record<string, T>;
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function toArray(value: any): any[] {
|
||||
if (Object.prototype.toString.call(value) === '[object Array]') return value;
|
||||
function toArray<T>(value: unknown): T[] {
|
||||
if (Object.prototype.toString.call(value) === '[object Array]') return value as T[];
|
||||
else return [];
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/importer-yaak",
|
||||
"name": "@yaak/importer-yaak",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.js",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-cookie",
|
||||
"name": "@yaak/template-function-cookie",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-encode",
|
||||
"name": "@yaak/template-function-encode",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [
|
||||
@@ -7,7 +7,7 @@ export const plugin: PluginDefinition = {
|
||||
description: 'Encode a value to base64',
|
||||
args: [{ label: 'Plain Text', type: 'text', name: 'value', multiLine: true }],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
return Buffer.from(args.values.value ?? '').toString('base64');
|
||||
return Buffer.from(String(args.values.value ?? '')).toString('base64');
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -15,7 +15,7 @@ export const plugin: PluginDefinition = {
|
||||
description: 'Decode a value from base64',
|
||||
args: [{ label: 'Encoded Value', type: 'text', name: 'value', multiLine: true }],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
return Buffer.from(args.values.value ?? '', 'base64').toString('utf-8');
|
||||
return Buffer.from(String(args.values.value ?? ''), 'base64').toString('utf-8');
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -23,7 +23,7 @@ export const plugin: PluginDefinition = {
|
||||
description: 'Encode a value for use in a URL (percent-encoding)',
|
||||
args: [{ label: 'Plain Text', type: 'text', name: 'value', multiLine: true }],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
return encodeURIComponent(args.values.value ?? '');
|
||||
return encodeURIComponent(String(args.values.value ?? ''));
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -32,7 +32,7 @@ export const plugin: PluginDefinition = {
|
||||
args: [{ label: 'Encoded Value', type: 'text', name: 'value', multiLine: true }],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
try {
|
||||
return decodeURIComponent(args.values.value ?? '');
|
||||
return decodeURIComponent(String(args.values.value ?? ''));
|
||||
} catch {
|
||||
return '';
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-fs",
|
||||
"name": "@yaak/template-function-fs",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,19 +1,21 @@
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import fs from 'node:fs';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [{
|
||||
name: 'fs.readFile',
|
||||
description: 'Read the contents of a file as utf-8',
|
||||
args: [{ title: 'Select File', type: 'file', name: 'path', label: 'File' }],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.path) return null;
|
||||
templateFunctions: [
|
||||
{
|
||||
name: 'fs.readFile',
|
||||
description: 'Read the contents of a file as utf-8',
|
||||
args: [{ title: 'Select File', type: 'file', name: 'path', label: 'File' }],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.path) return null;
|
||||
|
||||
try {
|
||||
return fs.promises.readFile(args.values.path, 'utf-8');
|
||||
} catch (err) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
return fs.promises.readFile(String(args.values.path ?? ''), 'utf-8');
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-hash",
|
||||
"name": "@yaak/template-function-hash",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-json",
|
||||
"name": "@yaak/template-function-json",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonpath-plus": "^10.3.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import { JSONPath } from 'jsonpath-plus';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
@@ -7,7 +7,13 @@ export const plugin: PluginDefinition = {
|
||||
name: 'json.jsonpath',
|
||||
description: 'Filter JSON-formatted text using JSONPath syntax',
|
||||
args: [
|
||||
{ type: 'text', name: 'input', label: 'Input', multiLine: true, placeholder: '{ "foo": "bar" }' },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'input',
|
||||
label: 'Input',
|
||||
multiLine: true,
|
||||
placeholder: '{ "foo": "bar" }',
|
||||
},
|
||||
{ type: 'text', name: 'query', label: 'Query', placeholder: '$..foo' },
|
||||
{ type: 'checkbox', name: 'formatted', label: 'Format Output' },
|
||||
],
|
||||
@@ -28,7 +34,7 @@ export const plugin: PluginDefinition = {
|
||||
} else {
|
||||
return JSON.stringify(filtered);
|
||||
}
|
||||
} catch (e) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
@@ -37,12 +43,39 @@ export const plugin: PluginDefinition = {
|
||||
name: 'json.escape',
|
||||
description: 'Escape a JSON string, useful when using the output in JSON values',
|
||||
args: [
|
||||
{ type: 'text', name: 'input', label: 'Input', multiLine: true, placeholder: 'Hello "World"' },
|
||||
{
|
||||
type: 'text',
|
||||
name: 'input',
|
||||
label: 'Input',
|
||||
multiLine: true,
|
||||
placeholder: 'Hello "World"',
|
||||
},
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
const input = String(args.values.input ?? '');
|
||||
return input.replace(/\\/g, '\\\\').replace(/"/g, '\\"');
|
||||
},
|
||||
},
|
||||
{
|
||||
name: 'json.minify',
|
||||
description: 'Remove unnecessary whitespace from a valid JSON string.',
|
||||
args: [
|
||||
{
|
||||
type: 'editor',
|
||||
language: 'json',
|
||||
name: 'input',
|
||||
label: 'Input',
|
||||
placeholder: '{ "foo": "bar" }',
|
||||
},
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
const input = String(args.values.input ?? '');
|
||||
try {
|
||||
return JSON.stringify(JSON.parse(input));
|
||||
} catch {
|
||||
return input;
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-prompt",
|
||||
"name": "@yaak/template-function-prompt",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [{
|
||||
@@ -15,10 +15,10 @@ export const plugin: PluginDefinition = {
|
||||
|
||||
return await ctx.prompt.text({
|
||||
id: `prompt-${args.values.label}`,
|
||||
label: args.values.title ?? '',
|
||||
title: args.values.title ?? '',
|
||||
defaultValue: args.values.defaultValue,
|
||||
placeholder: args.values.placeholder,
|
||||
label: String(args.values.title ?? ''),
|
||||
title: String(args.values.title ?? ''),
|
||||
defaultValue: String(args.values.defaultValue),
|
||||
placeholder: String(args.values.placeholder),
|
||||
});
|
||||
},
|
||||
}],
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-regex",
|
||||
"name": "@yaak/template-function-regex",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,28 +1,32 @@
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [{
|
||||
name: 'regex.match',
|
||||
description: 'Extract',
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'regex',
|
||||
label: 'Regular Expression',
|
||||
placeholder: '^\w+=(?<value>\w*)$',
|
||||
defaultValue: '^(.*)$',
|
||||
description: 'A JavaScript regular expression, evaluated using the Node.js RegExp engine. Capture groups or named groups can be used to extract values.',
|
||||
},
|
||||
{ type: 'text', name: 'input', label: 'Input Text', multiLine: true },
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.regex) return '';
|
||||
templateFunctions: [
|
||||
{
|
||||
name: 'regex.match',
|
||||
description: 'Extract',
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'regex',
|
||||
label: 'Regular Expression',
|
||||
placeholder: '^\\w+=(?<value>\\w*)$',
|
||||
defaultValue: '^(.*)$',
|
||||
description:
|
||||
'A JavaScript regular expression, evaluated using the Node.js RegExp engine. Capture groups or named groups can be used to extract values.',
|
||||
},
|
||||
{ type: 'text', name: 'input', label: 'Input Text', multiLine: true },
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.regex || !args.values.input) return '';
|
||||
|
||||
const regex = new RegExp(String(args.values.regex));
|
||||
const match = args.values.input?.match(regex);
|
||||
return match?.groups
|
||||
? Object.values(match.groups)[0] ?? ''
|
||||
: match?.[1] ?? match?.[0] ?? '';
|
||||
const input = String(args.values.input);
|
||||
const regex = new RegExp(String(args.values.regex));
|
||||
const match = input.match(regex);
|
||||
return match?.groups
|
||||
? (Object.values(match.groups)[0] ?? '')
|
||||
: (match?.[1] ?? match?.[0] ?? '');
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
};
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-request",
|
||||
"name": "@yaak/template-function-request",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,21 +1,26 @@
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [
|
||||
{
|
||||
name: 'request.body',
|
||||
args: [{
|
||||
name: 'requestId',
|
||||
label: 'Http Request',
|
||||
type: 'http_request',
|
||||
}],
|
||||
args: [
|
||||
{
|
||||
name: 'requestId',
|
||||
label: 'Http Request',
|
||||
type: 'http_request',
|
||||
},
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
const httpRequest = await ctx.httpRequest.getById({ id: args.values.requestId ?? 'n/a' });
|
||||
const requestId = String(args.values.requestId ?? 'n/a');
|
||||
const httpRequest = await ctx.httpRequest.getById({ id: requestId });
|
||||
if (httpRequest == null) return null;
|
||||
return String(await ctx.templates.render({
|
||||
data: httpRequest.body?.text ?? '',
|
||||
purpose: args.purpose,
|
||||
}));
|
||||
return String(
|
||||
await ctx.templates.render({
|
||||
data: httpRequest.body?.text ?? '',
|
||||
purpose: args.purpose,
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
@@ -30,15 +35,22 @@ export const plugin: PluginDefinition = {
|
||||
name: 'header',
|
||||
label: 'Header Name',
|
||||
type: 'text',
|
||||
}],
|
||||
},
|
||||
],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
const httpRequest = await ctx.httpRequest.getById({ id: args.values.requestId ?? 'n/a' });
|
||||
const headerName = String(args.values.header ?? '');
|
||||
const requestId = String(args.values.requestId ?? 'n/a');
|
||||
const httpRequest = await ctx.httpRequest.getById({ id: requestId });
|
||||
if (httpRequest == null) return null;
|
||||
const header = httpRequest.headers.find(h => h.name.toLowerCase() === args.values.header?.toLowerCase());
|
||||
return String(await ctx.templates.render({
|
||||
data: header?.value ?? '',
|
||||
purpose: args.purpose,
|
||||
}));
|
||||
const header = httpRequest.headers.find(
|
||||
(h) => h.name.toLowerCase() === headerName.toLowerCase(),
|
||||
);
|
||||
return String(
|
||||
await ctx.templates.render({
|
||||
data: header?.value ?? '',
|
||||
purpose: args.purpose,
|
||||
}),
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-response",
|
||||
"name": "@yaak/template-function-response",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"jsonpath-plus": "^10.3.0",
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { DOMParser } from '@xmldom/xmldom';
|
||||
import {
|
||||
import type {
|
||||
CallTemplateFunctionArgs,
|
||||
Context,
|
||||
FormInput,
|
||||
@@ -47,14 +47,14 @@ export const plugin: PluginDefinition = {
|
||||
if (!args.values.request || !args.values.header) return null;
|
||||
|
||||
const response = await getResponse(ctx, {
|
||||
requestId: args.values.request,
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ?? null,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
const header = response.headers.find(
|
||||
h => h.name.toLowerCase() === String(args.values.header ?? '').toLowerCase(),
|
||||
(h) => h.name.toLowerCase() === String(args.values.header ?? '').toLowerCase(),
|
||||
);
|
||||
return header?.value ?? null;
|
||||
},
|
||||
@@ -77,9 +77,9 @@ export const plugin: PluginDefinition = {
|
||||
if (!args.values.request || !args.values.path) return null;
|
||||
|
||||
const response = await getResponse(ctx, {
|
||||
requestId: args.values.request,
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ?? null,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -90,19 +90,19 @@ export const plugin: PluginDefinition = {
|
||||
let body;
|
||||
try {
|
||||
body = readFileSync(response.bodyPath, 'utf-8');
|
||||
} catch (_) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
return filterJSONPath(body, args.values.path);
|
||||
} catch (err) {
|
||||
return filterJSONPath(body, String(args.values.path || ''));
|
||||
} catch {
|
||||
// Probably not JSON, try XPath
|
||||
}
|
||||
|
||||
try {
|
||||
return filterXPath(body, args.values.path);
|
||||
} catch (err) {
|
||||
return filterXPath(body, String(args.values.path || ''));
|
||||
} catch {
|
||||
// Probably not XML
|
||||
}
|
||||
|
||||
@@ -113,17 +113,14 @@ export const plugin: PluginDefinition = {
|
||||
name: 'response.body.raw',
|
||||
description: 'Access the entire response body, as text',
|
||||
aliases: ['response'],
|
||||
args: [
|
||||
requestArg,
|
||||
behaviorArg,
|
||||
],
|
||||
args: [requestArg, behaviorArg],
|
||||
async onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
if (!args.values.request) return null;
|
||||
|
||||
const response = await getResponse(ctx, {
|
||||
requestId: args.values.request,
|
||||
requestId: String(args.values.request || ''),
|
||||
purpose: args.purpose,
|
||||
behavior: args.values.behavior ?? null,
|
||||
behavior: args.values.behavior ? String(args.values.behavior) : null,
|
||||
});
|
||||
if (response == null) return null;
|
||||
|
||||
@@ -134,7 +131,7 @@ export const plugin: PluginDefinition = {
|
||||
let body;
|
||||
try {
|
||||
body = readFileSync(response.bodyPath, 'utf-8');
|
||||
} catch (_) {
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -173,11 +170,18 @@ function filterXPath(body: string, path: string): string {
|
||||
}
|
||||
}
|
||||
|
||||
async function getResponse(ctx: Context, { requestId, behavior, purpose }: {
|
||||
requestId: string,
|
||||
behavior: string | null,
|
||||
purpose: RenderPurpose,
|
||||
}): Promise<HttpResponse | null> {
|
||||
async function getResponse(
|
||||
ctx: Context,
|
||||
{
|
||||
requestId,
|
||||
behavior,
|
||||
purpose,
|
||||
}: {
|
||||
requestId: string;
|
||||
behavior: string | null;
|
||||
purpose: RenderPurpose;
|
||||
},
|
||||
): Promise<HttpResponse | null> {
|
||||
if (!requestId) return null;
|
||||
|
||||
const httpRequest = await ctx.httpRequest.getById({ id: requestId ?? 'n/a' });
|
||||
@@ -195,9 +199,7 @@ async function getResponse(ctx: Context, { requestId, behavior, purpose }: {
|
||||
|
||||
// Previews happen a ton, and we don't want to send too many times on "always," so treat
|
||||
// it as "smart" during preview.
|
||||
let finalBehavior = (behavior === 'always' && purpose === 'preview')
|
||||
? 'smart'
|
||||
: behavior;
|
||||
const finalBehavior = behavior === 'always' && purpose === 'preview' ? 'smart' : behavior;
|
||||
|
||||
// Send if no responses and "smart," or "always"
|
||||
if ((finalBehavior === 'smart' && response == null) || finalBehavior === 'always') {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-uuid",
|
||||
"name": "@yaak/template-function-uuid",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"uuid": "^11.1.0"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import { v1, v3, v4, v5, v6, v7 } from 'uuid';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
{
|
||||
"name": "@yaakapp/template-function-xml",
|
||||
"name": "@yaak/template-function-xml",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js"
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
},
|
||||
"dependencies": {
|
||||
"@xmldom/xmldom": "^0.8.10",
|
||||
|
||||
@@ -1,29 +1,37 @@
|
||||
import { DOMParser } from '@xmldom/xmldom';
|
||||
import { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import type { CallTemplateFunctionArgs, Context, PluginDefinition } from '@yaakapp/api';
|
||||
import xpath from 'xpath';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
templateFunctions: [{
|
||||
name: 'xml.xpath',
|
||||
description: 'Filter XML-formatted text using XPath syntax',
|
||||
args: [
|
||||
{ type: 'text', name: 'input', label: 'Input', multiLine: true, placeholder: '<foo></foo>' },
|
||||
{ type: 'text', name: 'query', label: 'Query', placeholder: '//foo' },
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
try {
|
||||
const doc = new DOMParser().parseFromString(String(args.values.input), 'text/xml');
|
||||
let result = xpath.select(String(args.values.query), doc, false);
|
||||
if (Array.isArray(result)) {
|
||||
return String(result.map(c => String(c.firstChild))[0] ?? '');
|
||||
} else if (result instanceof Node) {
|
||||
return String(result.firstChild);
|
||||
} else {
|
||||
return String(result);
|
||||
templateFunctions: [
|
||||
{
|
||||
name: 'xml.xpath',
|
||||
description: 'Filter XML-formatted text using XPath syntax',
|
||||
args: [
|
||||
{
|
||||
type: 'text',
|
||||
name: 'input',
|
||||
label: 'Input',
|
||||
multiLine: true,
|
||||
placeholder: '<foo></foo>',
|
||||
},
|
||||
{ type: 'text', name: 'query', label: 'Query', placeholder: '//foo' },
|
||||
],
|
||||
async onRender(_ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null> {
|
||||
try {
|
||||
const doc = new DOMParser().parseFromString(String(args.values.input), 'text/xml');
|
||||
const result = xpath.select(String(args.values.query), doc, false);
|
||||
if (Array.isArray(result)) {
|
||||
return String(result.map((c) => String(c.firstChild))[0] ?? '');
|
||||
} else if (result instanceof Node) {
|
||||
return String(result.firstChild);
|
||||
} else {
|
||||
return String(result);
|
||||
}
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
} catch (e) {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
},
|
||||
}],
|
||||
],
|
||||
};
|
||||
|
||||
10
plugins/themes-yaak/package.json
Normal file
10
plugins/themes-yaak/package.json
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"name": "@yaak/themes-yaak",
|
||||
"private": true,
|
||||
"version": "0.0.1",
|
||||
"scripts": {
|
||||
"build": "yaakcli build ./src/index.ts",
|
||||
"dev": "yaakcli dev ./src/index.js",
|
||||
"lint": "tsc --noEmit"
|
||||
}
|
||||
}
|
||||
599
plugins/themes-yaak/src/index.ts
Normal file
599
plugins/themes-yaak/src/index.ts
Normal file
@@ -0,0 +1,599 @@
|
||||
import type { PluginDefinition } from '@yaakapp/api';
|
||||
|
||||
export const plugin: PluginDefinition = {
|
||||
themes: [
|
||||
{
|
||||
id: 'catppuccin-frappe',
|
||||
label: 'Catppuccin Frappé',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(231,19%,20%)',
|
||||
text: 'hsl(227,70%,87%)',
|
||||
textSubtle: 'hsl(228,29%,73%)',
|
||||
textSubtlest: 'hsl(227,17%,58%)',
|
||||
primary: 'hsl(277,59%,76%)',
|
||||
secondary: 'hsl(228,39%,80%)',
|
||||
info: 'hsl(222,74%,74%)',
|
||||
success: 'hsl(96,44%,68%)',
|
||||
notice: 'hsl(40,62%,73%)',
|
||||
warning: 'hsl(20,79%,70%)',
|
||||
danger: 'hsl(359,68%,71%)',
|
||||
},
|
||||
components: {
|
||||
dialog: {
|
||||
surface: 'hsl(240,21%,12%)',
|
||||
},
|
||||
sidebar: {
|
||||
surface: 'hsl(229,19%,23%)',
|
||||
border: 'hsl(229,19%,27%)',
|
||||
},
|
||||
appHeader: {
|
||||
surface: 'hsl(229,20%,17%)',
|
||||
border: 'hsl(229,20%,25%)',
|
||||
},
|
||||
responsePane: {
|
||||
surface: 'hsl(229,19%,23%)',
|
||||
border: 'hsl(229,19%,27%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(277,59%,68%)',
|
||||
secondary: 'hsl(228,39%,72%)',
|
||||
info: 'hsl(222,74%,67%)',
|
||||
success: 'hsl(96,44%,61%)',
|
||||
notice: 'hsl(40,62%,66%)',
|
||||
warning: 'hsl(20,79%,63%)',
|
||||
danger: 'hsl(359,68%,64%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'catppuccin-macchiato',
|
||||
label: 'Catppuccin Macchiato',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(233,23%,15%)',
|
||||
text: 'hsl(227,68%,88%)',
|
||||
textSubtle: 'hsl(227,27%,72%)',
|
||||
textSubtlest: 'hsl(228,15%,57%)',
|
||||
primary: 'hsl(267,83%,80%)',
|
||||
secondary: 'hsl(228,39%,80%)',
|
||||
info: 'hsl(220,83%,75%)',
|
||||
success: 'hsl(105,48%,72%)',
|
||||
notice: 'hsl(40,70%,78%)',
|
||||
warning: 'hsl(21,86%,73%)',
|
||||
danger: 'hsl(351,74%,73%)',
|
||||
},
|
||||
components: {
|
||||
dialog: {
|
||||
surface: 'hsl(240,21%,12%)',
|
||||
},
|
||||
sidebar: {
|
||||
surface: 'hsl(232,23%,18%)',
|
||||
border: 'hsl(231,23%,22%)',
|
||||
},
|
||||
appHeader: {
|
||||
surface: 'hsl(236,23%,12%)',
|
||||
border: 'hsl(236,23%,21%)',
|
||||
},
|
||||
responsePane: {
|
||||
surface: 'hsl(232,23%,18%)',
|
||||
border: 'hsl(231,23%,22%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(267,82%,72%)',
|
||||
secondary: 'hsl(228,39%,72%)',
|
||||
info: 'hsl(220,83%,68%)',
|
||||
success: 'hsl(105,48%,65%)',
|
||||
notice: 'hsl(40,70%,70%)',
|
||||
warning: 'hsl(21,86%,66%)',
|
||||
danger: 'hsl(351,74%,66%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'catppuccin-mocha',
|
||||
label: 'Catppuccin Mocha',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(240,21%,12%)',
|
||||
text: 'hsl(226,64%,88%)',
|
||||
textSubtle: 'hsl(228,24%,72%)',
|
||||
textSubtlest: 'hsl(230,13%,55%)',
|
||||
primary: 'hsl(267,83%,80%)',
|
||||
secondary: 'hsl(227,35%,80%)',
|
||||
info: 'hsl(217,92%,76%)',
|
||||
success: 'hsl(115,54%,76%)',
|
||||
notice: 'hsl(41,86%,83%)',
|
||||
warning: 'hsl(23,92%,75%)',
|
||||
danger: 'hsl(343,81%,75%)',
|
||||
},
|
||||
components: {
|
||||
dialog: {
|
||||
surface: 'hsl(240,21%,12%)',
|
||||
},
|
||||
sidebar: {
|
||||
surface: 'hsl(240,21%,15%)',
|
||||
border: 'hsl(240,21%,19%)',
|
||||
},
|
||||
appHeader: {
|
||||
surface: 'hsl(240,23%,9%)',
|
||||
border: 'hsl(240,22%,18%)',
|
||||
},
|
||||
responsePane: {
|
||||
surface: 'hsl(240,21%,15%)',
|
||||
border: 'hsl(240,21%,19%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(267,67%,65%)',
|
||||
secondary: 'hsl(227,28%,64%)',
|
||||
info: 'hsl(217,74%,61%)',
|
||||
success: 'hsl(115,43%,61%)',
|
||||
notice: 'hsl(41,69%,66%)',
|
||||
warning: 'hsl(23,74%,60%)',
|
||||
danger: 'hsl(343,65%,60%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'catppuccin-latte',
|
||||
label: 'Catppuccin Latte',
|
||||
dark: false,
|
||||
base: {
|
||||
surface: 'hsl(220,23%,95%)',
|
||||
text: 'hsl(234,16%,35%)',
|
||||
textSubtle: 'hsl(233,10%,47%)',
|
||||
textSubtlest: 'hsl(231,10%,59%)',
|
||||
primary: 'hsl(266,85%,58%)',
|
||||
secondary: 'hsl(233,10%,47%)',
|
||||
info: 'hsl(231,97%,72%)',
|
||||
success: 'hsl(183,74%,35%)',
|
||||
notice: 'hsl(35,77%,49%)',
|
||||
warning: 'hsl(22,99%,52%)',
|
||||
danger: 'hsl(355,76%,59%)',
|
||||
},
|
||||
components: {
|
||||
sidebar: {
|
||||
surface: 'hsl(220,22%,92%)',
|
||||
border: 'hsl(220,22%,87%)',
|
||||
},
|
||||
appHeader: {
|
||||
surface: 'hsl(220,21%,89%)',
|
||||
border: 'hsl(220,22%,87%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'dracula',
|
||||
label: 'Dracula',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(231,15%,18%)',
|
||||
surfaceHighlight: 'hsl(230,15%,24%)',
|
||||
text: 'hsl(60,30%,96%)',
|
||||
textSubtle: 'hsl(232,14%,65%)',
|
||||
textSubtlest: 'hsl(232,14%,50%)',
|
||||
primary: 'hsl(265,89%,78%)',
|
||||
secondary: 'hsl(225,27%,51%)',
|
||||
info: 'hsl(191,97%,77%)',
|
||||
success: 'hsl(135,94%,65%)',
|
||||
notice: 'hsl(65,92%,76%)',
|
||||
warning: 'hsl(31,100%,71%)',
|
||||
danger: 'hsl(0,100%,67%)',
|
||||
},
|
||||
components: {
|
||||
sidebar: {
|
||||
backdrop: 'hsl(230,15%,24%)',
|
||||
},
|
||||
appHeader: {
|
||||
backdrop: 'hsl(235,14%,15%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'github-dark',
|
||||
label: 'GitHub',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(213,30%,7%)',
|
||||
surfaceHighlight: 'hsl(213,16%,13%)',
|
||||
text: 'hsl(212,27%,89%)',
|
||||
textSubtle: 'hsl(212,9%,57%)',
|
||||
textSubtlest: 'hsl(217,8%,45%)',
|
||||
border: 'hsl(215,21%,11%)',
|
||||
primary: 'hsl(262,78%,74%)',
|
||||
secondary: 'hsl(217,8%,50%)',
|
||||
info: 'hsl(215,84%,64%)',
|
||||
success: 'hsl(129,48%,52%)',
|
||||
notice: 'hsl(39,71%,58%)',
|
||||
warning: 'hsl(22,83%,60%)',
|
||||
danger: 'hsl(3,83%,65%)',
|
||||
},
|
||||
components: {
|
||||
button: {
|
||||
primary: 'hsl(262,79%,71%)',
|
||||
secondary: 'hsl(217,8%,45%)',
|
||||
info: 'hsl(215,84%,60%)',
|
||||
success: 'hsl(129,48%,47%)',
|
||||
notice: 'hsl(39,71%,53%)',
|
||||
warning: 'hsl(22,83%,56%)',
|
||||
danger: 'hsl(3,83%,61%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'github-light',
|
||||
label: 'GitHub',
|
||||
dark: false,
|
||||
base: {
|
||||
surface: 'hsl(0,0%,100%)',
|
||||
surfaceHighlight: 'hsl(210,29%,94%)',
|
||||
text: 'hsl(213,13%,14%)',
|
||||
textSubtle: 'hsl(212,9%,43%)',
|
||||
textSubtlest: 'hsl(203,8%,55%)',
|
||||
border: 'hsl(210,15%,92%)',
|
||||
borderSubtle: 'hsl(210,15%,92%)',
|
||||
primary: 'hsl(261,69%,59%)',
|
||||
secondary: 'hsl(212,8%,47%)',
|
||||
info: 'hsl(212,92%,48%)',
|
||||
success: 'hsl(137,66%,32%)',
|
||||
notice: 'hsl(40,100%,40%)',
|
||||
warning: 'hsl(24,100%,44%)',
|
||||
danger: 'hsl(356,71%,48%)',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'gruvbox',
|
||||
label: 'Gruvbox',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(0,0%,16%)',
|
||||
surfaceHighlight: 'hsl(20,3%,19%)',
|
||||
text: 'hsl(53,74%,91%)',
|
||||
textSubtle: 'hsl(39,24%,66%)',
|
||||
textSubtlest: 'hsl(30,12%,51%)',
|
||||
primary: 'hsl(344,47%,68%)',
|
||||
secondary: 'hsl(157,16%,58%)',
|
||||
info: 'hsl(104,35%,62%)',
|
||||
success: 'hsl(61,66%,44%)',
|
||||
notice: 'hsl(42,95%,58%)',
|
||||
warning: 'hsl(27,99%,55%)',
|
||||
danger: 'hsl(6,96%,59%)',
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'hotdog-stand',
|
||||
label: 'Hotdog Stand',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(0,100%,50%)',
|
||||
surfaceHighlight: 'hsl(0,0%,0%)',
|
||||
text: 'hsl(0,0%,100%)',
|
||||
textSubtle: 'hsl(0,0%,100%)',
|
||||
textSubtlest: 'hsl(60,100%,50%)',
|
||||
border: 'hsl(0,0%,0%)',
|
||||
primary: 'hsl(60,100%,50%)',
|
||||
secondary: 'hsl(60,100%,50%)',
|
||||
info: 'hsl(60,100%,50%)',
|
||||
success: 'hsl(60,100%,50%)',
|
||||
notice: 'hsl(60,100%,50%)',
|
||||
warning: 'hsl(60,100%,50%)',
|
||||
danger: 'hsl(60,100%,50%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(0,0%,0%)',
|
||||
text: 'hsl(0,0%,100%)',
|
||||
textSubtle: 'hsl(60,100%,50%)',
|
||||
textSubtlest: 'hsl(0,100%,50%)',
|
||||
},
|
||||
menu: {
|
||||
surface: 'hsl(0,0%,0%)',
|
||||
border: 'hsl(0,100%,50%)',
|
||||
surfaceHighlight: 'hsl(0,100%,50%)',
|
||||
text: 'hsl(0,0%,100%)',
|
||||
textSubtle: 'hsl(60,100%,50%)',
|
||||
textSubtlest: 'hsl(60,100%,50%)',
|
||||
},
|
||||
button: {
|
||||
surface: 'hsl(0,0%,0%)',
|
||||
text: 'hsl(0,0%,100%)',
|
||||
primary: 'hsl(0,0%,0%)',
|
||||
secondary: 'hsl(0,0%,100%)',
|
||||
info: 'hsl(0,0%,0%)',
|
||||
success: 'hsl(60,100%,50%)',
|
||||
notice: 'hsl(60,100%,50%)',
|
||||
warning: 'hsl(0,0%,0%)',
|
||||
danger: 'hsl(0,100%,50%)',
|
||||
},
|
||||
editor: {
|
||||
primary: 'hsl(0,0%,100%)',
|
||||
secondary: 'hsl(0,0%,100%)',
|
||||
info: 'hsl(0,0%,100%)',
|
||||
success: 'hsl(0,0%,100%)',
|
||||
notice: 'hsl(60,100%,50%)',
|
||||
warning: 'hsl(0,0%,100%)',
|
||||
danger: 'hsl(0,0%,100%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'monokai-pro',
|
||||
label: 'Monokai Pro',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(285,5%,17%)',
|
||||
text: 'hsl(60,25%,98%)',
|
||||
textSubtle: 'hsl(0,1%,75%)',
|
||||
textSubtlest: 'hsl(300,0%,57%)',
|
||||
primary: 'hsl(250,77%,78%)',
|
||||
secondary: 'hsl(0,1%,75%)',
|
||||
info: 'hsl(186,71%,69%)',
|
||||
success: 'hsl(90,59%,66%)',
|
||||
notice: 'hsl(45,100%,70%)',
|
||||
warning: 'hsl(20,96%,70%)',
|
||||
danger: 'hsl(345,100%,69%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(300,5%,13%)',
|
||||
text: 'hsl(0,1%,75%)',
|
||||
textSubtle: 'hsl(300,0%,57%)',
|
||||
textSubtlest: 'hsl(300,1%,44%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(250,77%,70%)',
|
||||
secondary: 'hsl(0,1%,68%)',
|
||||
info: 'hsl(186,71%,62%)',
|
||||
success: 'hsl(90,59%,59%)',
|
||||
notice: 'hsl(45,100%,63%)',
|
||||
warning: 'hsl(20,96%,63%)',
|
||||
danger: 'hsl(345,100%,62%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'monokai-pro-classic',
|
||||
label: 'Monokai Pro Classic',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(70,8%,15%)',
|
||||
text: 'hsl(69,100%,97%)',
|
||||
textSubtle: 'hsl(65,9%,73%)',
|
||||
textSubtlest: 'hsl(66,4%,55%)',
|
||||
primary: 'hsl(261,100%,75%)',
|
||||
secondary: 'hsl(202,8%,72%)',
|
||||
info: 'hsl(190,81%,67%)',
|
||||
success: 'hsl(80,76%,53%)',
|
||||
notice: 'hsl(54,70%,68%)',
|
||||
warning: 'hsl(32,98%,56%)',
|
||||
danger: 'hsl(338,95%,56%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(72,9%,11%)',
|
||||
text: 'hsl(202,8%,72%)',
|
||||
textSubtle: 'hsl(213,4%,48%)',
|
||||
textSubtlest: 'hsl(223,6%,44%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(261,100%,68%)',
|
||||
secondary: 'hsl(202,8%,65%)',
|
||||
info: 'hsl(190,81%,60%)',
|
||||
success: 'hsl(80,76%,48%)',
|
||||
notice: 'hsl(54,71%,61%)',
|
||||
warning: 'hsl(32,98%,50%)',
|
||||
danger: 'hsl(338,95%,50%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'monokai-pro-machine',
|
||||
label: 'Monokai Pro Machine',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(200,16%,18%)',
|
||||
text: 'hsl(173,24%,93%)',
|
||||
textSubtle: 'hsl(185,6%,57%)',
|
||||
textSubtlest: 'hsl(189,6%,45%)',
|
||||
primary: 'hsl(258,86%,80%)',
|
||||
secondary: 'hsl(175,9%,75%)',
|
||||
info: 'hsl(194,81%,72%)',
|
||||
success: 'hsl(98,67%,69%)',
|
||||
notice: 'hsl(52,100%,72%)',
|
||||
warning: 'hsl(28,100%,72%)',
|
||||
danger: 'hsl(353,100%,71%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(196,16%,14%)',
|
||||
text: 'hsl(202,8%,72%)',
|
||||
textSubtle: 'hsl(213,4%,48%)',
|
||||
textSubtlest: 'hsl(223,6%,44%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(258,86%,72%)',
|
||||
secondary: 'hsl(175,9%,68%)',
|
||||
info: 'hsl(194,80%,65%)',
|
||||
success: 'hsl(98,67%,62%)',
|
||||
notice: 'hsl(52,100%,65%)',
|
||||
warning: 'hsl(28,100%,65%)',
|
||||
danger: 'hsl(353,100%,64%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'monokai-pro-octagon',
|
||||
label: 'Monokai Pro Octagon',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(233,18%,19%)',
|
||||
text: 'hsl(173,24%,93%)',
|
||||
textSubtle: 'hsl(202,8%,72%)',
|
||||
textSubtlest: 'hsl(213,4%,48%)',
|
||||
primary: 'hsl(292,30%,70%)',
|
||||
secondary: 'hsl(202,8%,72%)',
|
||||
info: 'hsl(155,37%,72%)',
|
||||
success: 'hsl(75,60%,61%)',
|
||||
notice: 'hsl(44,100%,71%)',
|
||||
warning: 'hsl(23,100%,68%)',
|
||||
danger: 'hsl(352,100%,70%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(235,18%,14%)',
|
||||
text: 'hsl(202,8%,72%)',
|
||||
textSubtle: 'hsl(213,4%,48%)',
|
||||
textSubtlest: 'hsl(223,6%,44%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(292,26%,63%)',
|
||||
secondary: 'hsl(201,7%,65%)',
|
||||
info: 'hsl(155,33%,65%)',
|
||||
success: 'hsl(75,54%,55%)',
|
||||
notice: 'hsl(44,90%,64%)',
|
||||
warning: 'hsl(23,90%,61%)',
|
||||
danger: 'hsl(352,90%,63%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'monokai-pro-ristretto',
|
||||
label: 'Monokai Pro Ristretto',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(0,9%,16%)',
|
||||
text: 'hsl(351,100%,97%)',
|
||||
textSubtle: 'hsl(355,9%,74%)',
|
||||
textSubtlest: 'hsl(354,4%,56%)',
|
||||
primary: 'hsl(239,63%,79%)',
|
||||
secondary: 'hsl(355,9%,74%)',
|
||||
info: 'hsl(170,53%,69%)',
|
||||
success: 'hsl(88,57%,66%)',
|
||||
notice: 'hsl(41,92%,70%)',
|
||||
warning: 'hsl(13,85%,70%)',
|
||||
danger: 'hsl(349,97%,70%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(0,8%,12%)',
|
||||
text: 'hsl(355,9%,74%)',
|
||||
textSubtle: 'hsl(354,4%,56%)',
|
||||
textSubtlest: 'hsl(353,4%,43%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(239,63%,71%)',
|
||||
secondary: 'hsl(355,9%,67%)',
|
||||
info: 'hsl(170,53%,62%)',
|
||||
success: 'hsl(88,57%,59%)',
|
||||
notice: 'hsl(41,92%,63%)',
|
||||
warning: 'hsl(13,86%,63%)',
|
||||
danger: 'hsl(349,97%,63%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'monokai-pro-spectrum',
|
||||
label: 'Monokai Pro Spectrum',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(0,0%,13%)',
|
||||
text: 'hsl(266,100%,97%)',
|
||||
textSubtle: 'hsl(264,7%,73%)',
|
||||
textSubtlest: 'hsl(266,3%,55%)',
|
||||
primary: 'hsl(247,61%,72%)',
|
||||
secondary: 'hsl(264,7%,73%)',
|
||||
info: 'hsl(188,74%,63%)',
|
||||
success: 'hsl(133,54%,66%)',
|
||||
notice: 'hsl(51,96%,69%)',
|
||||
warning: 'hsl(23,98%,66%)',
|
||||
danger: 'hsl(343,96%,68%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(0,0%,10%)',
|
||||
text: 'hsl(264,7%,73%)',
|
||||
textSubtle: 'hsl(266,3%,55%)',
|
||||
textSubtlest: 'hsl(264,2%,41%)',
|
||||
},
|
||||
button: {
|
||||
primary: 'hsl(247,61%,65%)',
|
||||
secondary: 'hsl(264,7%,66%)',
|
||||
info: 'hsl(188,74%,57%)',
|
||||
success: 'hsl(133,54%,59%)',
|
||||
notice: 'hsl(51,96%,62%)',
|
||||
warning: 'hsl(23,98%,59%)',
|
||||
danger: 'hsl(343,96%,61%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'moonlight',
|
||||
label: 'Moonlight',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(234,23%,17%)',
|
||||
text: 'hsl(225,71%,90%)',
|
||||
textSubtle: 'hsl(230,28%,62%)',
|
||||
textSubtlest: 'hsl(232,26%,43%)',
|
||||
primary: 'hsl(262,100%,82%)',
|
||||
secondary: 'hsl(232,18%,65%)',
|
||||
info: 'hsl(217,100%,74%)',
|
||||
success: 'hsl(174,66%,54%)',
|
||||
notice: 'hsl(35,100%,73%)',
|
||||
warning: 'hsl(17,100%,71%)',
|
||||
danger: 'hsl(356,100%,73%)',
|
||||
},
|
||||
components: {
|
||||
appHeader: {
|
||||
surface: 'hsl(233,23%,15%)',
|
||||
},
|
||||
sidebar: {
|
||||
surface: 'hsl(233,23%,15%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'nord',
|
||||
label: 'Nord',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(220,16%,22%)',
|
||||
surfaceHighlight: 'hsl(220,14%,28%)',
|
||||
text: 'hsl(220,28%,93%)',
|
||||
textSubtle: 'hsl(220,26%,90%)',
|
||||
textSubtlest: 'hsl(220,24%,86%)',
|
||||
primary: 'hsl(193,38%,68%)',
|
||||
secondary: 'hsl(210,34%,63%)',
|
||||
info: 'hsl(174,25%,69%)',
|
||||
success: 'hsl(89,26%,66%)',
|
||||
notice: 'hsl(40,66%,73%)',
|
||||
warning: 'hsl(17,48%,64%)',
|
||||
danger: 'hsl(353,43%,56%)',
|
||||
},
|
||||
components: {
|
||||
sidebar: {
|
||||
backdrop: 'hsl(220,16%,22%)',
|
||||
},
|
||||
appHeader: {
|
||||
backdrop: 'hsl(220,14%,28%)',
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
id: 'relaxing',
|
||||
label: 'Relaxing',
|
||||
dark: true,
|
||||
base: {
|
||||
surface: 'hsl(267,33%,17%)',
|
||||
text: 'hsl(275,49%,92%)',
|
||||
primary: 'hsl(267,84%,81%)',
|
||||
secondary: 'hsl(227,35%,80%)',
|
||||
info: 'hsl(217,92%,76%)',
|
||||
success: 'hsl(115,54%,76%)',
|
||||
notice: 'hsl(41,86%,83%)',
|
||||
warning: 'hsl(23,92%,75%)',
|
||||
danger: 'hsl(343,81%,75%)',
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
@@ -1,11 +0,0 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package yaak.plugins.runtime;
|
||||
|
||||
service PluginRuntime {
|
||||
rpc EventStream (stream EventStreamEvent) returns (stream EventStreamEvent);
|
||||
}
|
||||
|
||||
message EventStreamEvent {
|
||||
string event = 1;
|
||||
}
|
||||
628
src-tauri/Cargo.lock
generated
628
src-tauri/Cargo.lock
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +1,9 @@
|
||||
[workspace]
|
||||
members = [
|
||||
"yaak-crypto",
|
||||
"yaak-fonts",
|
||||
"yaak-git",
|
||||
"yaak-grpc",
|
||||
"yaak-fonts",
|
||||
"yaak-http",
|
||||
"yaak-license",
|
||||
"yaak-mac-window",
|
||||
@@ -40,13 +40,13 @@ tauri-build = { version = "2.2.0", features = [] }
|
||||
openssl-sys = { version = "0.9.105", features = ["vendored"] } # For Ubuntu installation to work
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.31", features = ["serde"] }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
cookie = "0.18.1"
|
||||
encoding_rs = "0.8.35"
|
||||
eventsource-client = { git = "https://github.com/yaakapp/rust-eventsource-client", version = "0.14.0" }
|
||||
http = { version = "1.2.0", default-features = false }
|
||||
log = "0.4.27"
|
||||
md5 = "0.7.0"
|
||||
md5 = "0.8.0"
|
||||
mime_guess = "2.0.5"
|
||||
rand = "0.9.0"
|
||||
reqwest = { workspace = true, features = ["multipart", "cookies", "gzip", "brotli", "deflate", "json", "rustls-tls-manual-roots-no-provider", "socks"] }
|
||||
@@ -54,29 +54,30 @@ reqwest_cookie_store = "0.8.0"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true, features = ["raw_value"] }
|
||||
tauri = { workspace = true, features = ["devtools", "protocol-asset"] }
|
||||
tauri-plugin-clipboard-manager = "2.2.2"
|
||||
tauri-plugin-clipboard-manager = "2.3.0"
|
||||
tauri-plugin-dialog = { workspace = true }
|
||||
tauri-plugin-fs = "2.2.0"
|
||||
tauri-plugin-log = { version = "2.3.1", features = ["colored"] }
|
||||
tauri-plugin-opener = "2.2.6"
|
||||
tauri-plugin-os = "2.2.1"
|
||||
tauri-plugin-fs = "2.4.0"
|
||||
tauri-plugin-log = { version = "2.6.0", features = ["colored"] }
|
||||
tauri-plugin-opener = "2.4.0"
|
||||
tauri-plugin-os = "2.3.0"
|
||||
tauri-plugin-shell = { workspace = true }
|
||||
tauri-plugin-single-instance = "2.2.2"
|
||||
tauri-plugin-updater = "2.6.1"
|
||||
tauri-plugin-window-state = "2.2.1"
|
||||
tauri-plugin-deep-link = "2.4.0"
|
||||
tauri-plugin-single-instance = { version = "2.3.0", features = ["deep-link"] }
|
||||
tauri-plugin-updater = "2.9.0"
|
||||
tauri-plugin-window-state = "2.3.0"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true, features = ["sync"] }
|
||||
tokio-stream = "0.1.17"
|
||||
uuid = "1.12.1"
|
||||
yaak-common = { workspace = true }
|
||||
yaak-crypto = { workspace = true }
|
||||
yaak-fonts = { workspace = true }
|
||||
yaak-git = { path = "yaak-git" }
|
||||
yaak-grpc = { path = "yaak-grpc" }
|
||||
yaak-http = { workspace = true }
|
||||
yaak-license = { path = "yaak-license" }
|
||||
yaak-mac-window = { path = "yaak-mac-window" }
|
||||
yaak-models = { workspace = true }
|
||||
yaak-fonts = { workspace = true }
|
||||
yaak-plugins = { workspace = true }
|
||||
yaak-sse = { workspace = true }
|
||||
yaak-sync = { workspace = true }
|
||||
@@ -84,19 +85,23 @@ yaak-templates = { workspace = true }
|
||||
yaak-ws = { path = "yaak-ws" }
|
||||
|
||||
[workspace.dependencies]
|
||||
reqwest = "0.12.19"
|
||||
chrono = "0.4.41"
|
||||
hex = "0.4.3"
|
||||
reqwest = "0.12.20"
|
||||
serde = "1.0.219"
|
||||
serde_json = "1.0.140"
|
||||
tauri = "2.5.1"
|
||||
tauri-plugin = "2.2.0"
|
||||
tauri-plugin-dialog = "2.2.2"
|
||||
tauri-plugin-shell = "2.2.1"
|
||||
tauri = "2.6.2"
|
||||
tauri-plugin = "2.3.0"
|
||||
tauri-plugin-dialog = "2.3.0"
|
||||
tauri-plugin-shell = "2.3.0"
|
||||
tokio = "1.45.1"
|
||||
thiserror = "2.0.12"
|
||||
ts-rs = "11.0.1"
|
||||
rustls = { version = "0.23.27", default-features = false }
|
||||
rustls-platform-verifier = "0.6.0"
|
||||
sha2 = "0.10.9"
|
||||
yaak-common = { path = "yaak-common" }
|
||||
yaak-crypto = { path = "yaak-crypto" }
|
||||
yaak-fonts = { path = "yaak-fonts" }
|
||||
yaak-http = { path = "yaak-http" }
|
||||
yaak-models = { path = "yaak-models" }
|
||||
@@ -104,4 +109,3 @@ yaak-plugins = { path = "yaak-plugins" }
|
||||
yaak-sse = { path = "yaak-sse" }
|
||||
yaak-sync = { path = "yaak-sync" }
|
||||
yaak-templates = { path = "yaak-templates" }
|
||||
yaak-crypto = { path = "yaak-crypto" }
|
||||
|
||||
@@ -52,11 +52,12 @@
|
||||
"opener:allow-reveal-item-in-dir",
|
||||
"shell:allow-open",
|
||||
"yaak-crypto:default",
|
||||
"yaak-git:default",
|
||||
"yaak-fonts:default",
|
||||
"yaak-git:default",
|
||||
"yaak-license:default",
|
||||
"yaak-mac-window:default",
|
||||
"yaak-models:default",
|
||||
"yaak-plugins:default",
|
||||
"yaak-sync:default",
|
||||
"yaak-ws:default"
|
||||
]
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<!-- Enable for v8 execution -->
|
||||
<!-- Enable for NodeJS execution -->
|
||||
<key>com.apple.security.cs.allow-unsigned-executable-memory</key>
|
||||
<true/>
|
||||
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
use crate::error::Result;
|
||||
use tauri::{command, AppHandle, Manager, Runtime, WebviewWindow};
|
||||
use tauri::{command, AppHandle, Manager, Runtime, State, WebviewWindow};
|
||||
use tauri_plugin_dialog::{DialogExt, MessageDialogKind};
|
||||
use yaak_crypto::manager::EncryptionManagerExt;
|
||||
use yaak_plugins::events::PluginWindowContext;
|
||||
use yaak_plugins::native_template_functions::{decrypt_secure_template_function, encrypt_secure_template_function};
|
||||
use yaak_plugins::events::{GetThemesResponse, PluginWindowContext};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::native_template_functions::{
|
||||
decrypt_secure_template_function, encrypt_secure_template_function,
|
||||
};
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn cmd_show_workspace_key<R: Runtime>(
|
||||
@@ -38,3 +41,11 @@ pub(crate) async fn cmd_secure_template<R: Runtime>(
|
||||
let window_context = &PluginWindowContext::new(&window);
|
||||
Ok(encrypt_secure_template_function(&app_handle, window_context, template)?)
|
||||
}
|
||||
|
||||
#[command]
|
||||
pub(crate) async fn cmd_get_themes<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> Result<Vec<GetThemesResponse>> {
|
||||
Ok(plugin_manager.get_themes(&window).await?)
|
||||
}
|
||||
|
||||
@@ -12,7 +12,7 @@ pub enum Error {
|
||||
|
||||
#[error(transparent)]
|
||||
SyncError(#[from] yaak_sync::error::Error),
|
||||
|
||||
|
||||
#[error(transparent)]
|
||||
CryptoError(#[from] yaak_crypto::error::Error),
|
||||
|
||||
@@ -28,18 +28,21 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
PluginError(#[from] yaak_plugins::error::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] yaak_common::error::Error),
|
||||
|
||||
#[error("Updater error: {0}")]
|
||||
UpdaterError(#[from] tauri_plugin_updater::Error),
|
||||
|
||||
|
||||
#[error("JSON error: {0}")]
|
||||
JsonError(#[from] serde_json::error::Error),
|
||||
|
||||
|
||||
#[error("Tauri error: {0}")]
|
||||
TauriError(#[from] tauri::Error),
|
||||
|
||||
|
||||
#[error("Event source error: {0}")]
|
||||
EventSourceError(#[from] eventsource_client::Error),
|
||||
|
||||
|
||||
#[error("I/O error: {0}")]
|
||||
IOError(#[from] io::Error),
|
||||
|
||||
|
||||
@@ -43,18 +43,6 @@ pub async fn store_launch_history<R: Runtime>(app_handle: &AppHandle<R>) -> Laun
|
||||
info
|
||||
}
|
||||
|
||||
pub fn get_os() -> &'static str {
|
||||
if cfg!(target_os = "windows") {
|
||||
"windows"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"macos"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"linux"
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get_num_launches<R: Runtime>(app_handle: &AppHandle<R>) -> i32 {
|
||||
app_handle.db().get_key_value_int(NAMESPACE, NUM_LAUNCHES_KEY, 0)
|
||||
}
|
||||
|
||||
105
src-tauri/src/import.rs
Normal file
105
src-tauri/src/import.rs
Normal file
@@ -0,0 +1,105 @@
|
||||
use crate::error::Result;
|
||||
use log::info;
|
||||
use std::collections::BTreeMap;
|
||||
use std::fs::read_to_string;
|
||||
use tauri::{Manager, Runtime, WebviewWindow};
|
||||
use yaak_models::models::{
|
||||
Environment, Folder, GrpcRequest, HttpRequest, WebsocketRequest, Workspace,
|
||||
};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::{BatchUpsertResult, UpdateSource, maybe_gen_id, maybe_gen_id_opt};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
|
||||
pub(crate) async fn import_data<R: Runtime>(
|
||||
window: &WebviewWindow<R>,
|
||||
file_path: &str,
|
||||
) -> Result<BatchUpsertResult> {
|
||||
let plugin_manager = window.state::<PluginManager>();
|
||||
let file =
|
||||
read_to_string(file_path).unwrap_or_else(|_| panic!("Unable to read file {}", file_path));
|
||||
let file_contents = file.as_str();
|
||||
let import_result = plugin_manager.import_data(window, file_contents).await?;
|
||||
|
||||
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
|
||||
|
||||
let resources = import_result.resources;
|
||||
|
||||
let workspaces: Vec<Workspace> = resources
|
||||
.workspaces
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Workspace>(v.id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let environments: Vec<Environment> = resources
|
||||
.environments
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Environment>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let folders: Vec<Folder> = resources
|
||||
.folders
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Folder>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let http_requests: Vec<HttpRequest> = resources
|
||||
.http_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<HttpRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let grpc_requests: Vec<GrpcRequest> = resources
|
||||
.grpc_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<GrpcRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let websocket_requests: Vec<WebsocketRequest> = resources
|
||||
.websocket_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<WebsocketRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("Importing data");
|
||||
|
||||
let upserted = window.with_tx(|tx| {
|
||||
tx.batch_upsert(
|
||||
workspaces,
|
||||
environments,
|
||||
folders,
|
||||
http_requests,
|
||||
grpc_requests,
|
||||
websocket_requests,
|
||||
&UpdateSource::Import,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(upserted)
|
||||
}
|
||||
@@ -3,14 +3,15 @@ use crate::encoding::read_response_body;
|
||||
use crate::error::Error::GenericError;
|
||||
use crate::grpc::{build_metadata, metadata_to_map, resolve_grpc_request};
|
||||
use crate::http_request::send_http_request;
|
||||
use crate::import::import_data;
|
||||
use crate::notifications::YaakNotifier;
|
||||
use crate::render::{render_grpc_request, render_template};
|
||||
use crate::updates::{UpdateMode, UpdateTrigger, YaakUpdater};
|
||||
use crate::uri_scheme::handle_uri_scheme;
|
||||
use crate::uri_scheme::handle_deep_link;
|
||||
use error::Result as YaakResult;
|
||||
use eventsource_client::{EventParser, SSE};
|
||||
use log::{debug, error, info, warn};
|
||||
use std::collections::{BTreeMap, HashMap};
|
||||
use std::collections::HashMap;
|
||||
use std::fs::{File, create_dir_all};
|
||||
use std::path::PathBuf;
|
||||
use std::str::FromStr;
|
||||
@@ -19,31 +20,30 @@ use std::{fs, panic};
|
||||
use tauri::{AppHandle, Emitter, RunEvent, State, WebviewWindow, is_dev};
|
||||
use tauri::{Listener, Runtime};
|
||||
use tauri::{Manager, WindowEvent};
|
||||
use tauri_plugin_deep_link::DeepLinkExt;
|
||||
use tauri_plugin_log::fern::colors::ColoredLevelConfig;
|
||||
use tauri_plugin_log::{Builder, Target, TargetKind};
|
||||
use tauri_plugin_window_state::{AppHandleExt, StateFlags};
|
||||
use tokio::fs::read_to_string;
|
||||
use tokio::sync::Mutex;
|
||||
use tokio::task::block_in_place;
|
||||
use yaak_common::window::WorkspaceWindowTrait;
|
||||
use yaak_grpc::manager::{DynamicMessage, GrpcHandle};
|
||||
use yaak_grpc::{Code, ServiceDefinition, deserialize_message, serialize_message};
|
||||
use yaak_models::models::{
|
||||
CookieJar, Environment, Folder, GrpcConnection, GrpcConnectionState, GrpcEvent, GrpcEventType,
|
||||
GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, WebsocketRequest, Workspace,
|
||||
WorkspaceMeta,
|
||||
CookieJar, Environment, GrpcConnection, GrpcConnectionState, GrpcEvent, GrpcEventType,
|
||||
GrpcRequest, HttpRequest, HttpResponse, HttpResponseState, Plugin, Workspace, WorkspaceMeta,
|
||||
};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::{
|
||||
BatchUpsertResult, UpdateSource, get_workspace_export_resources, maybe_gen_id, maybe_gen_id_opt,
|
||||
};
|
||||
use yaak_models::util::{BatchUpsertResult, UpdateSource, get_workspace_export_resources};
|
||||
use yaak_plugins::events::{
|
||||
BootResponse, CallHttpRequestActionRequest, FilterResponse,
|
||||
GetHttpAuthenticationConfigResponse, GetHttpAuthenticationSummaryResponse,
|
||||
GetHttpRequestActionsResponse, GetTemplateFunctionsResponse, InternalEvent,
|
||||
InternalEventPayload, JsonPrimitive, PluginWindowContext, RenderPurpose,
|
||||
CallGrpcRequestActionRequest, CallHttpRequestActionRequest, FilterResponse,
|
||||
GetGrpcRequestActionsResponse, GetHttpAuthenticationConfigResponse,
|
||||
GetHttpAuthenticationSummaryResponse, GetHttpRequestActionsResponse,
|
||||
GetTemplateFunctionsResponse, InternalEvent, InternalEventPayload, JsonPrimitive,
|
||||
PluginWindowContext, RenderPurpose,
|
||||
};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_meta::PluginMetadata;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
use yaak_sse::sse::ServerSentEvent;
|
||||
use yaak_templates::format::format_json;
|
||||
@@ -55,6 +55,7 @@ mod error;
|
||||
mod grpc;
|
||||
mod history;
|
||||
mod http_request;
|
||||
mod import;
|
||||
mod notifications;
|
||||
mod plugin_events;
|
||||
mod render;
|
||||
@@ -778,98 +779,9 @@ async fn cmd_get_sse_events(file_path: &str) -> YaakResult<Vec<ServerSentEvent>>
|
||||
#[tauri::command]
|
||||
async fn cmd_import_data<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
app_handle: AppHandle<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
file_path: &str,
|
||||
) -> YaakResult<BatchUpsertResult> {
|
||||
let file = read_to_string(file_path)
|
||||
.await
|
||||
.unwrap_or_else(|_| panic!("Unable to read file {}", file_path));
|
||||
let file_contents = file.as_str();
|
||||
let import_result = plugin_manager.import_data(&window, file_contents).await?;
|
||||
|
||||
let mut id_map: BTreeMap<String, String> = BTreeMap::new();
|
||||
|
||||
let resources = import_result.resources;
|
||||
|
||||
let workspaces: Vec<Workspace> = resources
|
||||
.workspaces
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Workspace>(v.id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let environments: Vec<Environment> = resources
|
||||
.environments
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Environment>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let folders: Vec<Folder> = resources
|
||||
.folders
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<Folder>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let http_requests: Vec<HttpRequest> = resources
|
||||
.http_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<HttpRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let grpc_requests: Vec<GrpcRequest> = resources
|
||||
.grpc_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<GrpcRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
let websocket_requests: Vec<WebsocketRequest> = resources
|
||||
.websocket_requests
|
||||
.into_iter()
|
||||
.map(|mut v| {
|
||||
v.id = maybe_gen_id::<WebsocketRequest>(v.id.as_str(), &mut id_map);
|
||||
v.workspace_id = maybe_gen_id::<Workspace>(v.workspace_id.as_str(), &mut id_map);
|
||||
v.folder_id = maybe_gen_id_opt::<Folder>(v.folder_id, &mut id_map);
|
||||
v
|
||||
})
|
||||
.collect();
|
||||
|
||||
info!("Importing data");
|
||||
|
||||
let upserted = app_handle.with_tx(|tx| {
|
||||
tx.batch_upsert(
|
||||
workspaces,
|
||||
environments,
|
||||
folders,
|
||||
http_requests,
|
||||
grpc_requests,
|
||||
websocket_requests,
|
||||
&UpdateSource::Import,
|
||||
)
|
||||
})?;
|
||||
|
||||
Ok(upserted)
|
||||
import_data(&window, file_path).await
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -880,6 +792,14 @@ async fn cmd_http_request_actions<R: Runtime>(
|
||||
Ok(plugin_manager.get_http_request_actions(&window).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_grpc_request_actions<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> YaakResult<Vec<GetGrpcRequestActionsResponse>> {
|
||||
Ok(plugin_manager.get_grpc_request_actions(&window).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_template_functions<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
@@ -919,6 +839,15 @@ async fn cmd_call_http_request_action<R: Runtime>(
|
||||
Ok(plugin_manager.call_http_request_action(&window, req).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_call_grpc_request_action<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
req: CallGrpcRequestActionRequest,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> YaakResult<()> {
|
||||
Ok(plugin_manager.call_grpc_request_action(&window, req).await?)
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn cmd_call_http_authentication_action<R: Runtime>(
|
||||
window: WebviewWindow<R>,
|
||||
@@ -1066,7 +995,7 @@ async fn cmd_install_plugin<R: Runtime>(
|
||||
app_handle: AppHandle<R>,
|
||||
window: WebviewWindow<R>,
|
||||
) -> YaakResult<Plugin> {
|
||||
plugin_manager.add_plugin_by_dir(&PluginWindowContext::new(&window), &directory, true).await?;
|
||||
plugin_manager.add_plugin_by_dir(&PluginWindowContext::new(&window), &directory).await?;
|
||||
|
||||
Ok(app_handle.db().upsert_plugin(
|
||||
&Plugin {
|
||||
@@ -1129,14 +1058,13 @@ async fn cmd_plugin_info<R: Runtime>(
|
||||
id: &str,
|
||||
app_handle: AppHandle<R>,
|
||||
plugin_manager: State<'_, PluginManager>,
|
||||
) -> YaakResult<BootResponse> {
|
||||
) -> YaakResult<PluginMetadata> {
|
||||
let plugin = app_handle.db().get_plugin(id)?;
|
||||
Ok(plugin_manager
|
||||
.get_plugin_by_dir(plugin.directory.as_str())
|
||||
.await
|
||||
.ok_or(GenericError("Failed to find plugin for info".to_string()))?
|
||||
.info()
|
||||
.await)
|
||||
.info())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
@@ -1255,6 +1183,7 @@ pub fn run() {
|
||||
.plugin(tauri_plugin_clipboard_manager::init())
|
||||
.plugin(tauri_plugin_opener::init())
|
||||
.plugin(tauri_plugin_window_state::Builder::default().build())
|
||||
.plugin(tauri_plugin_deep_link::init())
|
||||
.plugin(tauri_plugin_shell::init())
|
||||
.plugin(tauri_plugin_updater::Builder::default().build())
|
||||
.plugin(tauri_plugin_dialog::init())
|
||||
@@ -1272,6 +1201,21 @@ pub fn run() {
|
||||
|
||||
builder
|
||||
.setup(|app| {
|
||||
{
|
||||
let app_handle = app.app_handle().clone();
|
||||
app.deep_link().on_open_url(move |event| {
|
||||
info!("Handling deep link open");
|
||||
let app_handle = app_handle.clone();
|
||||
tauri::async_runtime::spawn(async move {
|
||||
for url in event.urls() {
|
||||
if let Err(e) = handle_deep_link(&app_handle, &url).await {
|
||||
warn!("Failed to handle deep link {}: {e:?}", url.to_string());
|
||||
};
|
||||
}
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
let app_data_dir = app.path().app_data_dir().unwrap();
|
||||
create_dir_all(app_data_dir.clone()).expect("Problem creating App directory!");
|
||||
|
||||
@@ -1294,6 +1238,7 @@ pub fn run() {
|
||||
.invoke_handler(tauri::generate_handler![
|
||||
cmd_call_http_authentication_action,
|
||||
cmd_call_http_request_action,
|
||||
cmd_call_grpc_request_action,
|
||||
cmd_check_for_updates,
|
||||
cmd_create_grpc_request,
|
||||
cmd_curl_to_request,
|
||||
@@ -1310,6 +1255,7 @@ pub fn run() {
|
||||
cmd_get_workspace_meta,
|
||||
cmd_grpc_go,
|
||||
cmd_grpc_reflect,
|
||||
cmd_grpc_request_actions,
|
||||
cmd_http_request_actions,
|
||||
cmd_import_data,
|
||||
cmd_install_plugin,
|
||||
@@ -1329,10 +1275,10 @@ pub fn run() {
|
||||
//
|
||||
// Migrated commands
|
||||
crate::commands::cmd_decrypt_template,
|
||||
crate::commands::cmd_get_themes,
|
||||
crate::commands::cmd_secure_template,
|
||||
crate::commands::cmd_show_workspace_key,
|
||||
])
|
||||
.register_uri_scheme_protocol("yaak", handle_uri_scheme)
|
||||
.build(tauri::generate_context!())
|
||||
.expect("error while running tauri application")
|
||||
.run(|app_handle, event| {
|
||||
@@ -1377,7 +1323,7 @@ pub fn run() {
|
||||
tokio::time::sleep(Duration::from_millis(4000)).await;
|
||||
let val: State<'_, Mutex<YaakNotifier>> = w.state();
|
||||
let mut n = val.lock().await;
|
||||
if let Err(e) = n.check(&w).await {
|
||||
if let Err(e) = n.maybe_check(&w).await {
|
||||
warn!("Failed to check for notifications {}", e)
|
||||
}
|
||||
});
|
||||
|
||||
@@ -1,13 +1,14 @@
|
||||
use std::time::SystemTime;
|
||||
|
||||
use crate::error::Result;
|
||||
use crate::history::{get_num_launches, get_os};
|
||||
use chrono::{DateTime, Duration, Utc};
|
||||
use crate::history::get_num_launches;
|
||||
use chrono::{DateTime, Utc};
|
||||
use log::debug;
|
||||
use reqwest::Method;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow};
|
||||
use yaak_common::api_client::yaak_api_client;
|
||||
use yaak_common::platform::get_os;
|
||||
use yaak_license::{LicenseCheckStatus, check_license};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::UpdateSource;
|
||||
@@ -27,6 +28,7 @@ pub struct YaakNotifier {
|
||||
#[serde(default, rename_all = "camelCase")]
|
||||
pub struct YaakNotification {
|
||||
timestamp: DateTime<Utc>,
|
||||
timeout: Option<f64>,
|
||||
id: String,
|
||||
message: String,
|
||||
action: Option<YaakNotificationAction>,
|
||||
@@ -61,7 +63,7 @@ impl YaakNotifier {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub async fn check<R: Runtime>(&mut self, window: &WebviewWindow<R>) -> Result<()> {
|
||||
pub async fn maybe_check<R: Runtime>(&mut self, window: &WebviewWindow<R>) -> Result<()> {
|
||||
let app_handle = window.app_handle();
|
||||
let ignore_check = self.last_check.elapsed().unwrap().as_secs() < MAX_UPDATE_CHECK_SECONDS;
|
||||
|
||||
@@ -80,7 +82,7 @@ impl YaakNotifier {
|
||||
let settings = window.db().get_settings();
|
||||
let num_launches = get_num_launches(app_handle).await;
|
||||
let info = app_handle.package_info().clone();
|
||||
let req = reqwest::Client::default()
|
||||
let req = yaak_api_client(app_handle)?
|
||||
.request(Method::GET, "https://notify.yaak.app/notifications")
|
||||
.query(&[
|
||||
("version", info.version.to_string().as_str()),
|
||||
@@ -95,22 +97,9 @@ impl YaakNotifier {
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let result = resp.json::<Value>().await?;
|
||||
|
||||
// Support both single and multiple notifications.
|
||||
// TODO: Remove support for single after April 2025
|
||||
let notifications = match result {
|
||||
Value::Array(a) => a
|
||||
.into_iter()
|
||||
.map(|a| serde_json::from_value(a).unwrap())
|
||||
.collect::<Vec<YaakNotification>>(),
|
||||
a @ _ => vec![serde_json::from_value(a).unwrap()],
|
||||
};
|
||||
|
||||
for notification in notifications {
|
||||
let age = notification.timestamp.signed_duration_since(Utc::now());
|
||||
for notification in resp.json::<Vec<YaakNotification>>().await? {
|
||||
let seen = get_kv(app_handle).await?;
|
||||
if seen.contains(¬ification.id) || (age > Duration::days(2)) {
|
||||
if seen.contains(¬ification.id) {
|
||||
debug!("Already seen notification {}", notification.id);
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
use crate::http_request::send_http_request;
|
||||
use crate::render::{render_http_request, render_json_value};
|
||||
use crate::window::{CreateWindowConfig, create_window};
|
||||
use crate::render::{render_grpc_request, render_http_request, render_json_value};
|
||||
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,
|
||||
@@ -13,13 +13,7 @@ use tauri_plugin_clipboard_manager::ClipboardExt;
|
||||
use yaak_models::models::{HttpResponse, Plugin};
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::UpdateSource;
|
||||
use yaak_plugins::events::{
|
||||
Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse, GetCookieValueResponse,
|
||||
GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload,
|
||||
ListCookieNamesResponse, PluginWindowContext, RenderHttpRequestResponse,
|
||||
SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest, TemplateRenderResponse,
|
||||
WindowNavigateEvent,
|
||||
};
|
||||
use yaak_plugins::events::{Color, DeleteKeyValueResponse, EmptyPayload, FindHttpResponsesResponse, GetCookieValueResponse, GetHttpRequestByIdResponse, GetKeyValueResponse, Icon, InternalEvent, InternalEventPayload, ListCookieNamesResponse, PluginWindowContext, RenderGrpcRequestResponse, RenderHttpRequestResponse, SendHttpRequestResponse, SetKeyValueResponse, ShowToastRequest, TemplateRenderResponse, WindowNavigateEvent};
|
||||
use yaak_plugins::manager::PluginManager;
|
||||
use yaak_plugins::plugin_handle::PluginHandle;
|
||||
use yaak_plugins::template_callback::PluginTemplateCallback;
|
||||
@@ -68,6 +62,30 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
http_request,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::RenderGrpcRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render grpc request");
|
||||
|
||||
let workspace =
|
||||
workspace_from_window(&window).expect("Failed to get workspace_id from window URL");
|
||||
let environment = environment_from_window(&window);
|
||||
let base_environment = app_handle
|
||||
.db()
|
||||
.get_base_environment(&workspace.id)
|
||||
.expect("Failed to get base environment");
|
||||
let cb = PluginTemplateCallback::new(app_handle, &window_context, req.purpose);
|
||||
let grpc_request = render_grpc_request(
|
||||
&req.grpc_request,
|
||||
&base_environment,
|
||||
environment.as_ref(),
|
||||
&cb,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to render grpc request");
|
||||
Some(InternalEventPayload::RenderGrpcRequestResponse(RenderGrpcRequestResponse {
|
||||
grpc_request,
|
||||
}))
|
||||
}
|
||||
InternalEventPayload::RenderHttpRequestRequest(req) => {
|
||||
let window = get_window_from_window_context(app_handle, &window_context)
|
||||
.expect("Failed to find window for render http request");
|
||||
@@ -86,8 +104,8 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
environment.as_ref(),
|
||||
&cb,
|
||||
)
|
||||
.await
|
||||
.expect("Failed to render http request");
|
||||
.await
|
||||
.expect("Failed to render http request");
|
||||
Some(InternalEventPayload::RenderHttpRequestResponse(RenderHttpRequestResponse {
|
||||
http_request,
|
||||
}))
|
||||
@@ -115,7 +133,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!(
|
||||
"Plugin error from {}: {}",
|
||||
plugin_handle.name().await,
|
||||
plugin_handle.info().name,
|
||||
resp.error
|
||||
),
|
||||
color: Some(Color::Danger),
|
||||
@@ -126,7 +144,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
Box::pin(handle_plugin_event(app_handle, &toast_event, plugin_handle)).await;
|
||||
None
|
||||
}
|
||||
InternalEventPayload::ReloadResponse(_) => {
|
||||
InternalEventPayload::ReloadResponse(r) => {
|
||||
let plugins = app_handle.db().list_plugins().unwrap();
|
||||
for plugin in plugins {
|
||||
if plugin.directory != plugin_handle.dir {
|
||||
@@ -142,7 +160,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
let toast_event = plugin_handle.build_event_to_send(
|
||||
&window_context,
|
||||
&InternalEventPayload::ShowToastRequest(ShowToastRequest {
|
||||
message: format!("Reloaded plugin {}", plugin_handle.dir),
|
||||
message: format!("Reloaded plugin {}@{}", r.name, r.version),
|
||||
icon: Some(Icon::Info),
|
||||
..Default::default()
|
||||
}),
|
||||
@@ -188,7 +206,7 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
cookie_jar,
|
||||
&mut tokio::sync::watch::channel(false).1, // No-op cancel channel
|
||||
)
|
||||
.await;
|
||||
.await;
|
||||
|
||||
let http_response = match result {
|
||||
Ok(r) => r,
|
||||
@@ -257,17 +275,17 @@ pub(crate) async fn handle_plugin_event<R: Runtime>(
|
||||
None
|
||||
}
|
||||
InternalEventPayload::SetKeyValueRequest(req) => {
|
||||
let name = plugin_handle.name().await;
|
||||
let name = plugin_handle.info().name;
|
||||
app_handle.db().set_plugin_key_value(&name, &req.key, &req.value);
|
||||
Some(InternalEventPayload::SetKeyValueResponse(SetKeyValueResponse {}))
|
||||
}
|
||||
InternalEventPayload::GetKeyValueRequest(req) => {
|
||||
let name = plugin_handle.name().await;
|
||||
let name = plugin_handle.info().name;
|
||||
let value = app_handle.db().get_plugin_key_value(&name, &req.key).map(|v| v.value);
|
||||
Some(InternalEventPayload::GetKeyValueResponse(GetKeyValueResponse { value }))
|
||||
}
|
||||
InternalEventPayload::DeleteKeyValueRequest(req) => {
|
||||
let name = plugin_handle.name().await;
|
||||
let name = plugin_handle.info().name;
|
||||
let deleted = app_handle.db().delete_plugin_key_value(&name, &req.key).unwrap();
|
||||
Some(InternalEventPayload::DeleteKeyValueResponse(DeleteKeyValueResponse { deleted }))
|
||||
}
|
||||
|
||||
@@ -1,25 +1,66 @@
|
||||
use crate::error::Result;
|
||||
use crate::import::import_data;
|
||||
use log::{info, warn};
|
||||
use tauri::{Manager, Runtime, UriSchemeContext};
|
||||
use std::collections::HashMap;
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, Url};
|
||||
use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind};
|
||||
use yaak_plugins::events::{Color, ShowToastRequest};
|
||||
use yaak_plugins::install::download_and_install;
|
||||
|
||||
pub(crate) fn handle_uri_scheme<R: Runtime>(
|
||||
a: UriSchemeContext<R>,
|
||||
req: http::Request<Vec<u8>>,
|
||||
) -> http::Response<Vec<u8>> {
|
||||
println!("------------- Yaak URI scheme invoked!");
|
||||
let uri = req.uri();
|
||||
let window = a
|
||||
.app_handle()
|
||||
.get_webview_window(a.webview_label())
|
||||
.expect("Failed to get webview window for URI scheme event");
|
||||
info!("Yaak URI scheme invoked with {uri:?} {window:?}");
|
||||
pub(crate) async fn handle_deep_link<R: Runtime>(
|
||||
app_handle: &AppHandle<R>,
|
||||
url: &Url,
|
||||
) -> Result<()> {
|
||||
let command = url.domain().unwrap_or_default();
|
||||
info!("Yaak URI scheme invoked {}?{}", command, url.query().unwrap_or_default());
|
||||
|
||||
let path = uri.path();
|
||||
if path == "/data/import" {
|
||||
warn!("TODO: import data")
|
||||
} else if path == "/plugins/install" {
|
||||
warn!("TODO: install plugin")
|
||||
let query_map: HashMap<String, String> = url.query_pairs().into_owned().collect();
|
||||
let windows = app_handle.webview_windows();
|
||||
let (_, window) = windows.iter().next().unwrap();
|
||||
|
||||
match command {
|
||||
"install-plugin" => {
|
||||
let name = query_map.get("name").unwrap();
|
||||
let version = query_map.get("version").cloned();
|
||||
_ = window.set_focus();
|
||||
let confirmed_install = app_handle
|
||||
.dialog()
|
||||
.message(format!("Install plugin {name} {version:?}?",))
|
||||
.kind(MessageDialogKind::Info)
|
||||
.buttons(MessageDialogButtons::OkCustom("Install".to_string()))
|
||||
.blocking_show();
|
||||
if !confirmed_install {
|
||||
// Cancelled installation
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let pv = download_and_install(window, name, version).await?;
|
||||
app_handle.emit(
|
||||
"show_toast",
|
||||
ShowToastRequest {
|
||||
message: format!("Installed {name}@{}", pv.version),
|
||||
color: Some(Color::Success),
|
||||
icon: None,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
"import-data" => {
|
||||
let file_path = query_map.get("path").unwrap();
|
||||
let results = import_data(window, file_path).await?;
|
||||
_ = window.set_focus();
|
||||
window.emit(
|
||||
"show_toast",
|
||||
ShowToastRequest {
|
||||
message: format!("Imported data for {} workspaces", results.workspaces.len()),
|
||||
color: Some(Color::Success),
|
||||
icon: None,
|
||||
},
|
||||
)?;
|
||||
}
|
||||
_ => {
|
||||
warn!("Unknown deep link command: {command}");
|
||||
}
|
||||
}
|
||||
|
||||
let msg = format!("No handler found for {path}");
|
||||
tauri::http::Response::builder().status(404).body(msg.as_bytes().to_vec()).unwrap()
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -23,7 +23,6 @@
|
||||
},
|
||||
"plugins": {
|
||||
"deep-link": {
|
||||
"mobile": [],
|
||||
"desktop": {
|
||||
"schemes": [
|
||||
"yaak"
|
||||
|
||||
@@ -6,4 +6,7 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
tauri = { workspace = true }
|
||||
reqwest = { workspace = true, features = ["system-proxy", "gzip"] }
|
||||
thiserror = { workspace = true }
|
||||
regex = "1.11.0"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
24
src-tauri/yaak-common/src/api_client.rs
Normal file
24
src-tauri/yaak-common/src/api_client.rs
Normal file
@@ -0,0 +1,24 @@
|
||||
use crate::error::Result;
|
||||
use crate::platform::{get_ua_arch, get_ua_platform};
|
||||
use reqwest::Client;
|
||||
use std::time::Duration;
|
||||
use tauri::http::{HeaderMap, HeaderValue};
|
||||
use tauri::{AppHandle, Runtime};
|
||||
|
||||
pub fn yaak_api_client<R: Runtime>(app_handle: &AppHandle<R>) -> Result<Client> {
|
||||
let platform = get_ua_platform();
|
||||
let version = app_handle.package_info().version.clone();
|
||||
let arch = get_ua_arch();
|
||||
let ua = format!("Yaak/{version} ({platform}; {arch})");
|
||||
let mut default_headers = HeaderMap::new();
|
||||
default_headers.insert("Accept", HeaderValue::from_str("application/json").unwrap());
|
||||
|
||||
let client = reqwest::ClientBuilder::new()
|
||||
.timeout(Duration::from_secs(20))
|
||||
.default_headers(default_headers)
|
||||
.gzip(true)
|
||||
.user_agent(ua)
|
||||
.build()?;
|
||||
|
||||
Ok(client)
|
||||
}
|
||||
19
src-tauri/yaak-common/src/error.rs
Normal file
19
src-tauri/yaak-common/src/error.rs
Normal file
@@ -0,0 +1,19 @@
|
||||
use serde::{Serialize, Serializer};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug)]
|
||||
pub enum Error {
|
||||
#[error(transparent)]
|
||||
ReqwestError(#[from] reqwest::Error),
|
||||
}
|
||||
|
||||
impl Serialize for Error {
|
||||
fn serialize<S>(&self, serializer: S) -> std::result::Result<S::Ok, S::Error>
|
||||
where
|
||||
S: Serializer,
|
||||
{
|
||||
serializer.serialize_str(self.to_string().as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
pub type Result<T> = std::result::Result<T, Error>;
|
||||
@@ -1 +1,4 @@
|
||||
pub mod window;
|
||||
pub mod window;
|
||||
pub mod platform;
|
||||
pub mod api_client;
|
||||
pub mod error;
|
||||
36
src-tauri/yaak-common/src/platform.rs
Normal file
36
src-tauri/yaak-common/src/platform.rs
Normal file
@@ -0,0 +1,36 @@
|
||||
pub fn get_os() -> &'static str {
|
||||
if cfg!(target_os = "windows") {
|
||||
"windows"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"macos"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"linux"
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ua_platform() -> &'static str {
|
||||
if cfg!(target_os = "windows") {
|
||||
"Win"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"Mac"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"Linux"
|
||||
} else {
|
||||
"Unknown"
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_ua_arch() -> &'static str {
|
||||
if cfg!(target_arch = "x86_64") {
|
||||
"x86_64"
|
||||
} else if cfg!(target_arch = "x86") {
|
||||
"i386"
|
||||
} else if cfg!(target_arch = "arm") {
|
||||
"ARM"
|
||||
} else if cfg!(target_arch = "aarch64") {
|
||||
"ARM64"
|
||||
} else {
|
||||
"Unknown"
|
||||
}}
|
||||
@@ -13,7 +13,7 @@ keyring = { version = "4.0.0-rc.1" }
|
||||
log = "0.4.26"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
tauri = { workspace = true }
|
||||
thiserror = "2.0.12"
|
||||
thiserror = { workspace = true }
|
||||
yaak-models = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
|
||||
@@ -6,7 +6,7 @@ edition = "2024"
|
||||
publish = false
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
chrono = { workspace = true, features = ["serde"] }
|
||||
git2 = { version = "0.20.0", features = ["vendored-libgit2", "vendored-openssl"] }
|
||||
log = "0.4.22"
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
|
||||
@@ -15,7 +15,7 @@ pub enum Error {
|
||||
#[error("Yaml error: {0}")]
|
||||
YamlParseError(#[from] serde_yaml::Error),
|
||||
|
||||
#[error("Yaml error: {0}")]
|
||||
#[error(transparent)]
|
||||
ModelError(#[from] yaak_models::error::Error),
|
||||
|
||||
#[error("Sync error: {0}")]
|
||||
@@ -24,10 +24,10 @@ pub enum Error {
|
||||
#[error("I/o error: {0}")]
|
||||
IoError(#[from] io::Error),
|
||||
|
||||
#[error("Yaml error: {0}")]
|
||||
#[error("JSON error: {0}")]
|
||||
JsonParseError(#[from] serde_json::Error),
|
||||
|
||||
#[error("Yaml error: {0}")]
|
||||
#[error("UTF8 error: {0}")]
|
||||
Utf8ConversionError(#[from] FromUtf8Error),
|
||||
|
||||
#[error("Git error: {0}")]
|
||||
|
||||
@@ -265,7 +265,7 @@ pub fn git_status(dir: &Path) -> Result<GitStatusSummary> {
|
||||
None => None,
|
||||
Some(t) => match t.get_path(&Path::new(&rela_path)) {
|
||||
Ok(entry) => {
|
||||
let obj = entry.to_object(&repo).unwrap();
|
||||
let obj = entry.to_object(&repo)?;
|
||||
let content = obj.as_blob().unwrap().content();
|
||||
let name = Path::new(entry.name().unwrap_or_default());
|
||||
SyncModel::from_bytes(content.into(), name)?.map(|m| m.0)
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
use std::collections::BTreeMap;
|
||||
use std::collections::{BTreeMap, HashSet};
|
||||
use std::env::temp_dir;
|
||||
use std::ops::Deref;
|
||||
use std::path::PathBuf;
|
||||
use std::path::{Path, PathBuf};
|
||||
use std::str::FromStr;
|
||||
|
||||
use crate::client::AutoReflectionClient;
|
||||
use anyhow::anyhow;
|
||||
use async_recursion::async_recursion;
|
||||
use log::{debug, warn};
|
||||
use log::{debug, info, warn};
|
||||
use prost::Message;
|
||||
use prost_reflect::{DescriptorPool, MethodDescriptor};
|
||||
use prost_types::{FileDescriptorProto, FileDescriptorSet};
|
||||
@@ -46,24 +46,51 @@ pub async fn fill_pool_from_files(
|
||||
desc_path.to_string_lossy().to_string(),
|
||||
];
|
||||
|
||||
let mut include_dirs = HashSet::new();
|
||||
let mut include_protos = HashSet::new();
|
||||
|
||||
for p in paths {
|
||||
if p.as_path().exists() {
|
||||
args.push(p.to_string_lossy().to_string());
|
||||
} else {
|
||||
if !p.exists() {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Dirs are added as includes
|
||||
if p.is_dir() {
|
||||
include_dirs.insert(p.to_string_lossy().to_string());
|
||||
continue;
|
||||
}
|
||||
|
||||
let parent = p.as_path().parent();
|
||||
if let Some(parent_path) = parent {
|
||||
args.push("-I".to_string());
|
||||
args.push(parent_path.to_string_lossy().to_string());
|
||||
args.push("-I".to_string());
|
||||
args.push(parent_path.parent().unwrap().to_string_lossy().to_string());
|
||||
match find_parent_proto_dir(parent_path) {
|
||||
None => {
|
||||
// Add parent/grandparent as fallback
|
||||
include_dirs.insert(parent_path.to_string_lossy().to_string());
|
||||
if let Some(grandparent_path) = parent_path.parent() {
|
||||
include_dirs.insert(grandparent_path.to_string_lossy().to_string());
|
||||
}
|
||||
}
|
||||
Some(p) => {
|
||||
include_dirs.insert(p.to_string_lossy().to_string());
|
||||
}
|
||||
};
|
||||
} else {
|
||||
debug!("ignoring {:?} since it does not exist.", parent)
|
||||
}
|
||||
|
||||
include_protos.insert(p.to_string_lossy().to_string());
|
||||
}
|
||||
|
||||
for d in include_dirs.clone() {
|
||||
args.push("-I".to_string());
|
||||
args.push(d);
|
||||
}
|
||||
for p in include_protos.clone() {
|
||||
args.push(p);
|
||||
}
|
||||
|
||||
info!("Invoking protoc with {}", args.join(" "));
|
||||
|
||||
let out = app_handle
|
||||
.shell()
|
||||
.sidecar("yaakprotoc")
|
||||
@@ -365,3 +392,22 @@ mod topology {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn find_parent_proto_dir(start_path: impl AsRef<Path>) -> Option<PathBuf> {
|
||||
let mut dir = start_path.as_ref().canonicalize().ok()?;
|
||||
|
||||
loop {
|
||||
if let Some(name) = dir.file_name().and_then(|n| n.to_str()) {
|
||||
if name == "proto" {
|
||||
return Some(dir);
|
||||
}
|
||||
}
|
||||
|
||||
let parent = dir.parent()?;
|
||||
if parent == dir {
|
||||
return None; // Reached root
|
||||
}
|
||||
|
||||
dir = parent.to_path_buf();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ tauri = { workspace = true }
|
||||
thiserror = { workspace = true }
|
||||
ts-rs = { workspace = true }
|
||||
yaak-models = { workspace = true }
|
||||
yaak-common = { workspace = true }
|
||||
|
||||
[build-dependencies]
|
||||
tauri-plugin = { workspace = true, features = ["build"] }
|
||||
|
||||
@@ -15,6 +15,9 @@ pub enum Error {
|
||||
#[error(transparent)]
|
||||
ModelError(#[from] yaak_models::error::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
CommonError(#[from] yaak_common::error::Error),
|
||||
|
||||
#[error("Internal server error")]
|
||||
ServerError,
|
||||
}
|
||||
|
||||
@@ -16,15 +16,3 @@ pub fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
.invoke_handler(generate_handler![check, activate, deactivate])
|
||||
.build()
|
||||
}
|
||||
|
||||
pub(crate) fn get_os() -> &'static str {
|
||||
if cfg!(target_os = "windows") {
|
||||
"windows"
|
||||
} else if cfg!(target_os = "macos") {
|
||||
"macos"
|
||||
} else if cfg!(target_os = "linux") {
|
||||
"linux"
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ use std::ops::Add;
|
||||
use std::time::Duration;
|
||||
use tauri::{AppHandle, Emitter, Manager, Runtime, WebviewWindow, is_dev};
|
||||
use ts_rs::TS;
|
||||
use yaak_common::api_client::yaak_api_client;
|
||||
use yaak_common::platform::get_os;
|
||||
use yaak_models::query_manager::QueryManagerExt;
|
||||
use yaak_models::util::UpdateSource;
|
||||
|
||||
@@ -68,7 +70,7 @@ pub async fn activate_license<R: Runtime>(
|
||||
let client = reqwest::Client::new();
|
||||
let payload = ActivateLicenseRequestPayload {
|
||||
license_key: license_key.to_string(),
|
||||
app_platform: crate::get_os().to_string(),
|
||||
app_platform: get_os().to_string(),
|
||||
app_version: window.app_handle().package_info().version.to_string(),
|
||||
};
|
||||
let response = client.post(build_url("/licenses/activate")).json(&payload).send().await?;
|
||||
@@ -107,7 +109,7 @@ pub async fn deactivate_license<R: Runtime>(window: &WebviewWindow<R>) -> Result
|
||||
let client = reqwest::Client::new();
|
||||
let path = format!("/licenses/activations/{}/deactivate", activation_id);
|
||||
let payload = DeactivateLicenseRequestPayload {
|
||||
app_platform: crate::get_os().to_string(),
|
||||
app_platform: get_os().to_string(),
|
||||
app_version: window.app_handle().package_info().version.to_string(),
|
||||
};
|
||||
let response = client.post(build_url(&path)).json(&payload).send().await?;
|
||||
@@ -149,7 +151,7 @@ pub enum LicenseCheckStatus {
|
||||
|
||||
pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<LicenseCheckStatus> {
|
||||
let payload = CheckActivationRequestPayload {
|
||||
app_platform: crate::get_os().to_string(),
|
||||
app_platform: get_os().to_string(),
|
||||
app_version: window.package_info().version.to_string(),
|
||||
};
|
||||
let activation_id = get_activation_id(window.app_handle()).await;
|
||||
@@ -169,7 +171,7 @@ pub async fn check_license<R: Runtime>(window: &WebviewWindow<R>) -> Result<Lice
|
||||
(true, _) => {
|
||||
info!("Checking license activation");
|
||||
// A license has been activated, so let's check the license server
|
||||
let client = reqwest::Client::new();
|
||||
let client = yaak_api_client(window.app_handle())?;
|
||||
let path = format!("/licenses/activations/{activation_id}/check");
|
||||
let response = client.post(build_url(&path)).json(&payload).send().await?;
|
||||
|
||||
|
||||
@@ -10,10 +10,10 @@ tauri-plugin = { workspace = true, features = ["build"] }
|
||||
|
||||
[target.'cfg(target_os = "macos")'.dependencies]
|
||||
cocoa = "0.26.0"
|
||||
hex_color = "3.0.0"
|
||||
log = "0.4.27"
|
||||
objc = "0.2.7"
|
||||
rand = "0.9.0"
|
||||
csscolorparser = "0.7.2"
|
||||
|
||||
[dependencies]
|
||||
tauri = { workspace = true }
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
use tauri::{Runtime, Window, command};
|
||||
use tauri::{command, Runtime, Window};
|
||||
|
||||
#[command]
|
||||
pub(crate) fn set_title<R: Runtime>(window: Window<R>, title: &str) {
|
||||
@@ -18,12 +18,13 @@ pub(crate) fn set_title<R: Runtime>(window: Window<R>, title: &str) {
|
||||
pub(crate) fn set_theme<R: Runtime>(window: Window<R>, bg_color: &str) {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
match hex_color::HexColor::parse_rgb(bg_color.trim()) {
|
||||
use log::warn;
|
||||
match csscolorparser::parse(bg_color.trim()) {
|
||||
Ok(color) => {
|
||||
crate::mac::update_window_theme(window, color);
|
||||
}
|
||||
Err(err) => {
|
||||
log::warn!("Failed to parse background color '{}': {}", bg_color, err)
|
||||
warn!("Failed to parse background color '{}': {}", bg_color, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#![allow(deprecated)]
|
||||
use hex_color::HexColor;
|
||||
use csscolorparser::Color;
|
||||
use objc::{msg_send, sel, sel_impl};
|
||||
use tauri::{Emitter, Runtime, Window};
|
||||
|
||||
@@ -35,12 +35,12 @@ pub(crate) fn update_window_title<R: Runtime>(window: Window<R>, title: String)
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_window_theme<R: Runtime>(window: Window<R>, color: HexColor) {
|
||||
pub(crate) fn update_window_theme<R: Runtime>(window: Window<R>, color: Color) {
|
||||
use cocoa::appkit::{
|
||||
NSAppearance, NSAppearanceNameVibrantDark, NSAppearanceNameVibrantLight, NSWindow,
|
||||
};
|
||||
|
||||
let brightness = (color.r as u64 + color.g as u64 + color.b as u64) / 3;
|
||||
let brightness = (color.r as f64 + color.g as f64 + color.b as f64) / 3.0;
|
||||
let label = window.label().to_string();
|
||||
|
||||
unsafe {
|
||||
@@ -49,7 +49,7 @@ pub(crate) fn update_window_theme<R: Runtime>(window: Window<R>, color: HexColor
|
||||
let _ = window.run_on_main_thread(move || {
|
||||
let handle = window_handle;
|
||||
|
||||
let selected_appearance = if brightness >= 128 {
|
||||
let selected_appearance = if brightness >= 0.5 {
|
||||
NSAppearance(NSAppearanceNameVibrantLight)
|
||||
} else {
|
||||
NSAppearance(NSAppearanceNameVibrantDark)
|
||||
|
||||
@@ -7,7 +7,7 @@ publish = false
|
||||
|
||||
[dependencies]
|
||||
chrono = { version = "0.4.38", features = ["serde"] }
|
||||
hex = "0.4.3"
|
||||
hex = { workspace = true }
|
||||
include_dir = "0.7"
|
||||
log = "0.4.22"
|
||||
nanoid = "0.4.0"
|
||||
@@ -18,10 +18,10 @@ sea-query = { version = "0.32.1", features = ["with-chrono", "attr"] }
|
||||
sea-query-rusqlite = { version = "0.7.0", features = ["with-chrono"] }
|
||||
serde = { workspace = true, features = ["derive"] }
|
||||
serde_json = { workspace = true }
|
||||
sha2 = "0.10.9"
|
||||
tauri = { workspace = true}
|
||||
sha2 = { workspace = true }
|
||||
tauri = { workspace = true }
|
||||
tauri-plugin-dialog = { workspace = true }
|
||||
thiserror = "2.0.11"
|
||||
thiserror = { workspace = true }
|
||||
tokio = { workspace = true }
|
||||
ts-rs = { workspace = true, features = ["chrono-impl", "serde-json-impl"] }
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt
|
||||
|
||||
export type PluginKeyValue = { model: "plugin_key_value", createdAt: string, updatedAt: string, pluginName: string, key: string, value: string, };
|
||||
|
||||
export type ProxySetting = { "type": "enabled", disabled: boolean, http: string, https: string, auth: ProxySettingAuth | null, bypass: string, } | { "type": "disabled" };
|
||||
export type ProxySetting = { "type": "enabled", http: string, https: string, auth: ProxySettingAuth | null, bypass: string, disabled: boolean, } | { "type": "disabled" };
|
||||
|
||||
export type ProxySettingAuth = { user: string, password: string, };
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user