Compare commits

..

662 Commits

Author SHA1 Message Date
Gregory Schier
221e768b33 Fix recent workspaces 2024-03-10 17:42:25 -07:00
Gregory Schier
c2dc7e0f4a Fix adding header if not exist 2024-03-10 17:10:16 -07:00
Gregory Schier
9e065c34ee Remove completion debug blur thing 2024-03-10 16:46:18 -07:00
Gregory Schier
2f91d541c5 Adjust detected content-type header 2024-03-10 16:26:06 -07:00
Gregory Schier
948fd487ab Clickable links in response viewer 2024-03-10 13:41:44 -07:00
Gregory Schier
ed6a5386a2 Better error handling for file not found 2024-03-10 11:02:32 -07:00
Gregory Schier
8a24c48fd3 Cancel file selection sets to undefined 2024-03-10 10:57:49 -07:00
Gregory Schier
d726a6f5bf Binary file uploads and missing workspace empty state 2024-03-10 10:56:38 -07:00
Gregory Schier
8d2a2a8532 Fix GraphQL Header backend 2024-02-28 13:38:22 -08:00
Gregory Schier
b838a6ffc1 Fix GraphQL content type on creation, and placeholder 2024-02-28 13:04:17 -08:00
Gregory Schier
2174a91b64 Include default protoc includes 2024-02-28 09:45:11 -08:00
Gregory Schier
083f83ccab Bump version 2024-02-28 08:51:34 -08:00
Gregory Schier
4f749be2e2 Fix dropdown arrow keys 2024-02-28 08:51:08 -08:00
Gregory Schier
cefdc3ecf3 Track GRPC 2024-02-28 07:32:05 -08:00
Gregory Schier
02960d2d64 Analytics ID 2024-02-28 07:27:19 -08:00
Gregory Schier
9e5226aa83 Analytics ID 2024-02-28 07:26:02 -08:00
Gregory Schier
63d7a44586 Remove Escape from hotkeys 2024-02-27 18:58:41 -08:00
Gregory Schier
c851dfe206 Fix sidebar focus 2024-02-27 10:33:20 -08:00
Gregory Schier
6adc15a249 Fix gap in dropdown menu items 2024-02-27 10:27:04 -08:00
Gregory Schier
9ac7aac296 Methods in recent dropdown 2024-02-27 10:20:35 -08:00
Gregory Schier
325d63e1b7 Many hotkey improvements 2024-02-27 10:10:38 -08:00
Gregory Schier
e639a77165 Info logs in build 2024-02-26 17:27:08 -08:00
Gregory Schier
c075efc752 Introspection tweak 2024-02-26 17:24:44 -08:00
Gregory Schier
c4f42f71c3 Tweak editor find/replace 2024-02-26 17:17:37 -08:00
Gregory Schier
535adfe200 Fix find/replace CM styling 2024-02-26 17:07:09 -08:00
Gregory Schier
85fa159f0d Fix lint errors 2024-02-26 07:43:08 -08:00
Gregory Schier
fd2fe46c95 Autocomplete icons and transfer proto files on duplicate 2024-02-26 07:39:53 -08:00
Gregory Schier
6e52f35626 Prompt folder name on create 2024-02-26 07:14:27 -08:00
Gregory Schier
a0d1e7023d Better creation from folder menu 2024-02-26 07:09:59 -08:00
Gregory Schier
97a2f00d59 Auto-fill link to changelog in release script 2024-02-25 18:42:04 -08:00
Gregory Schier
50ad4efad7 Protoc sidecar 2024-02-25 17:43:29 -08:00
Gregory Schier
79a3d9c8df Fix deletion in sidebar 2024-02-25 12:56:57 -08:00
Gregory Schier
b8e20d885f Fix create dropdown hotkey 2024-02-24 22:02:04 -08:00
Gregory Schier
752eb3dbd5 Try changing macOS version 2024-02-24 21:25:58 -08:00
Gregory Schier
616acdfb56 Bump some things 2024-02-24 21:23:02 -08:00
Gregory Schier
b2bcbababe Fix response pane height 2024-02-24 19:31:59 -08:00
Gregory Schier
9f5a3ef96a Don't build plugins 2024-02-24 18:58:51 -08:00
Gregory Schier
d2c5bdc3c8 Remove npm ci plugins 2024-02-24 18:28:07 -08:00
Gregory Schier
0d6899a12c Check in built plugins again 2024-02-24 18:27:36 -08:00
Gregory Schier
1b25cb0c4c Add pkg locks 2024-02-24 18:19:16 -08:00
Gregory Schier
783b7222df Install plugins in CI 2024-02-24 16:29:22 -08:00
Gregory Schier
ff3165ab30 Bump version 2024-02-24 16:22:55 -08:00
Gregory Schier
9780dc88a1 Got json-schema autocomplete working again 2024-02-24 16:22:22 -08:00
Gregory Schier
e4f0d2a341 Proto files off model 2024-02-24 14:16:58 -08:00
Gregory Schier
bcf0ae159d Better gRPC status on error 2024-02-24 12:41:43 -08:00
Gregory Schier
5664d41073 More analytics, and cancel requests 2024-02-24 11:30:07 -08:00
Gregory Schier
e75e6865ea Hook up empty state buttons for first-launch experience 2024-02-23 16:34:19 -08:00
Gregory Schier
fd5b495b70 gRPC in import/export 2024-02-23 16:16:13 -08:00
Gregory Schier
16506d1ddd Everything in messages now 2024-02-22 19:51:30 -08:00
Gregory Schier
e3016f7100 Format XML responses 2024-02-22 01:00:02 -08:00
Gregory Schier
766da4327c Refactor into grpc events 2024-02-22 00:49:22 -08:00
Gregory Schier
6f389b0010 Fix split layout placeholder 2024-02-18 09:12:44 -08:00
Gregory Schier
007ea88edd Add other body type 2024-02-18 08:59:14 -08:00
Gregory Schier
5409678855 Add metadata and squash migrations 2024-02-18 08:35:31 -08:00
Gregory Schier
4c6bd63b8b Better environment edit dialog 2024-02-18 07:44:53 -08:00
Gregory Schier
8db80d2e97 Allow editing base environment 2024-02-18 00:14:47 -08:00
Gregory Schier
c80fca8063 Render gRPC message body 2024-02-18 00:14:26 -08:00
Gregory Schier
7384398813 gRPC authentication 2024-02-17 23:47:28 -08:00
Gregory Schier
b57ea8adeb Even better dropdown filtering 2024-02-17 22:27:01 -08:00
Gregory Schier
8ff2caf3c3 Started gRPC tabs 2024-02-17 22:15:44 -08:00
Gregory Schier
a521b8f308 Better dropdown filtering 2024-02-17 22:03:42 -08:00
Gregory Schier
50ba167516 Prevent dragging folders into itself 2024-02-17 15:32:15 -08:00
Gregory Schier
cb102657ea Fix deps 2024-02-17 15:20:13 -08:00
Gregory Schier
a7d9e2432b Upgrade Vite deps and fix windows DnD 2024-02-17 14:57:12 -08:00
Gregory Schier
d842b168e6 Fix postman importer TS ref 2024-02-17 11:05:57 -08:00
Gregory Schier
870cb25980 Add more info to settings 2024-02-17 11:04:19 -08:00
Gregory Schier
fde0c5540b Adjust placeholder error colors 2024-02-15 21:15:18 -08:00
Gregory Schier
2ec9a1c19d Tweak tab padding 2024-02-15 16:38:14 -08:00
Gregory Schier
c2f5a3bf45 Responsive (scroll) workspace header 2024-02-15 16:30:08 -08:00
Gregory Schier
7c18eeae8c Better button highlight border 2024-02-15 15:55:32 -08:00
Gregory Schier
d7a1b4b7bc Refactor recentRequest/Env/Workspace 2024-02-15 15:14:18 -08:00
Gregory Schier
4566ede184 Better sidebar collapse, debuonce container uqeries, fix recent requests 2024-02-15 15:07:15 -08:00
Gregory Schier
f45c898be0 Better recent work/env/req logic 2024-02-13 17:21:54 -08:00
Gregory Schier
4e1700f8a4 Fix active environment on workspace change 2024-02-13 16:42:07 -08:00
Gregory Schier
f14311d14a Active environment in query param 2024-02-13 16:32:31 -08:00
Gregory Schier
470a7e2278 Better variable placeholder styles 2024-02-13 16:32:17 -08:00
Gregory Schier
2d67be481d Show GQL for graphql requests in sidebar 2024-02-13 16:32:00 -08:00
Gregory Schier
9f6ddb1558 Better highlight on JSON tree 2024-02-13 16:31:38 -08:00
Gregory Schier
853f07b9af Cmd+n to open dropdown 2024-02-11 14:17:09 -08:00
Gregory Schier
0eb6358387 Tab-to-indent in editor 2024-02-11 14:16:36 -08:00
Gregory Schier
d43e045f25 Make editor variables more prominent 2024-02-11 14:16:25 -08:00
Gregory Schier
17432fca29 Fix dropdown open index 2024-02-11 14:16:11 -08:00
Gregory Schier
d5931660c2 Catch URL error when URL = "{{HOST}}" 2024-02-11 09:04:27 -08:00
Gregory Schier
cd7678b7a1 Grap gRPC status codes 2024-02-11 08:52:12 -08:00
Gregory Schier
706be1188b Use basemsg 2024-02-11 08:29:57 -08:00
Gregory Schier
8989b61a13 Combine grpc handlers, fix duplicate 2024-02-10 10:41:45 -08:00
Gregory Schier
a997944f16 Fix response emit and sidebar flex 2024-02-09 16:16:02 -08:00
Gregory Schier
f8e8f5d3f2 Sidebar methods and fix model hooks 2024-02-09 16:09:24 -08:00
Gregory Schier
812e5238ac Fix editor selection/cursor and lint errors 2024-02-09 14:32:58 -08:00
Gregory Schier
16d4f2952d Remove built plugins from source control 2024-02-09 05:09:37 -08:00
Gregory Schier
ac9a6d5871 Merge branch 'grpc' 2024-02-09 05:07:45 -08:00
Gregory Schier
4fcf1df61f Move plugins to build folder 2024-02-09 05:07:34 -08:00
Gregory Schier
394beb374e gRPC Support (#20) 2024-02-09 05:01:00 -08:00
Gregory Schier
ba4d1063e3 Better message serialization 2024-02-09 05:00:48 -08:00
Gregory Schier
2bd9b436e6 Working sidebar actions for grpc 2024-02-07 00:02:02 -08:00
Gregory Schier
915a59dec4 Change hotkey handling to capture phase 2024-02-06 23:44:10 -08:00
Gregory Schier
ae2b746cb2 Fix pool management 2024-02-06 23:26:24 -08:00
Gregory Schier
b04cff153b Minor tweaks 2024-02-06 19:32:03 -08:00
Gregory Schier
bd8e71e567 gRPC schema from files! 2024-02-06 19:20:32 -08:00
Gregory Schier
562a36d616 Proto selection UI/models 2024-02-06 12:29:23 -08:00
Gregory Schier
c85a11edf1 Better reflect failure UI 2024-02-05 14:50:47 -08:00
Gregory Schier
ef7f942a8f Async connection management 2024-02-05 11:29:27 -08:00
Gregory Schier
a7f2a86d71 Refactor model emit, and recent conn dropdown 2024-02-05 10:39:47 -08:00
Gregory Schier
bf90f84d16 db to app_handle! 2024-02-04 22:52:04 -08:00
Gregory Schier
4284aa2549 Single upserted_model event 2024-02-04 21:19:15 -08:00
Gregory Schier
60773cab53 Fix DB mutex deadlock 2024-02-04 21:17:05 -08:00
Gregory Schier
e2c17873ae More messages 2024-02-04 19:08:31 -08:00
Gregory Schier
88982156ee Client streaming working 2024-02-04 17:53:15 -08:00
Gregory Schier
722c8a1c6b Bidirectional working 2024-02-04 14:10:38 -08:00
Gregory Schier
8c15274786 Messages are flowing! 2024-02-04 12:09:10 -08:00
Gregory Schier
1abba4980a Use req/conn/msg models in unary/server 2024-02-04 11:57:12 -08:00
Gregory Schier
3a340999ec Remove console log 2024-02-03 13:39:45 -08:00
Gregory Schier
b7261e77aa Grpc layout use new models 2024-02-03 13:28:31 -08:00
Gregory Schier
23431b40e9 Show gRPC requests in sidebar 2024-02-03 13:08:24 -08:00
Gregory Schier
04f31cd4a7 gRPC models and tables 2024-02-03 11:14:42 -08:00
Gregory Schier
7d82a7e74a Tiny fixes 2024-02-02 18:41:00 -08:00
Gregory Schier
d31255d987 Better formatting 2024-02-02 13:37:44 -08:00
Gregory Schier
e53693f605 A bit better handling of responses 2024-02-02 13:32:06 -08:00
Gregory Schier
67aa7b7268 Split layouts and things 2024-02-02 12:41:37 -08:00
Gregory Schier
e27ed9becc bidi hacked! 2024-02-02 01:10:54 -08:00
Gregory Schier
22d21af3c2 Implement cancel 2024-02-02 00:18:37 -08:00
Gregory Schier
67000af7f9 Better connection management 2024-02-01 20:29:32 -08:00
Gregory Schier
b84c7ba50c gRPC manager mostly working 2024-02-01 15:36:50 -08:00
Gregory Schier
a0b3f86462 Small refactor 2024-02-01 02:42:59 -08:00
Gregory Schier
6a8395660d Refactor commands and DB 2024-02-01 02:29:24 -08:00
Gregory Schier
2c041fbac6 Some minor tweaks 2024-02-01 00:48:03 -08:00
Gregory Schier
1eed0e8f22 Revert response JSON tree 2024-02-01 00:38:57 -08:00
Gregory Schier
63a0ed273d Even better styles 2024-02-01 00:36:49 -08:00
Gregory Schier
d0be5ca515 Styled it up a bit 2024-02-01 00:16:09 -08:00
Gregory Schier
b964c942d6 Merge remote-tracking branch 'origin/grpc' into grpc 2024-01-31 22:14:15 -08:00
Gregory Schier
a05fc5fd20 Hacky server streaming done 2024-01-31 22:14:08 -08:00
Gregory Schier
de183abd24 Hacky client streaming done 2024-01-31 22:13:46 -08:00
Gregory Schier
5c44df7b00 Initial frontend for gRPC UI 2024-01-30 16:43:54 -08:00
Gregory Schier
dbdce4cf9a Hooked up test call from frontend! 2024-01-29 20:50:43 -08:00
Gregory Schier
219a6b78da Fix cookie jar 2024-01-28 17:49:04 -08:00
Gregory Schier
fb11aff03f Track dialogs 2024-01-28 16:33:36 -08:00
Gregory Schier
15714ae188 Fix dropdown separator 2024-01-28 16:21:41 -08:00
Gregory Schier
ce116d032d Better format 2024-01-28 16:19:46 -08:00
Gregory Schier
6f41df6e52 More response info 2024-01-28 16:02:49 -08:00
Gregory Schier
0853d2ca95 Better BG color 2024-01-28 14:43:04 -08:00
Gregory Schier
6798331ce5 Cookie Support (#19) 2024-01-28 14:39:51 -08:00
Gregory Schier
5ffc75e0ad Add shift to hotkey dialog hotkey 2024-01-19 22:11:20 -08:00
Gregory Schier
bf92371a49 Show alert after force checking updates 2024-01-19 22:11:02 -08:00
Gregory Schier
bd3da86317 Better dialog padding 2024-01-19 22:08:32 -08:00
Gregory Schier
3db3d42246 Change env hotkey to use shift 2024-01-19 21:53:48 -08:00
Gregory Schier
de8bf3ca70 Fix URL 2px grow on focus 2024-01-19 21:49:51 -08:00
Gregory Schier
8bc131de6c Bump version 2024-01-19 13:42:02 -08:00
Gregory Schier
efce69292d Fix analytics again 2024-01-18 22:28:25 -08:00
Gregory Schier
0ccc893440 Fix dialog close button 2024-01-18 20:57:42 -08:00
Gregory Schier
1f9756c917 Fix URLBar expanded state inner buttons 2024-01-18 20:40:56 -08:00
Gregory Schier
be8f0e4521 Some analytics fixes 2024-01-18 20:23:02 -08:00
Gregory Schier
bcdf51d231 Launch analytics events, changelog, better filter styles 2024-01-18 14:42:02 -08:00
Gregory Schier
1a1553eebd Bump version 2024-01-17 14:56:47 -08:00
Gregory Schier
321c3862fe Custom HTTP method names 2024-01-17 14:52:19 -08:00
Gregory Schier
466d412e65 Workspace header tweak Windows 2024-01-17 18:48:43 -08:00
Gregory Schier
86f50b826f Fix header in fullscreen mode on Mac 2024-01-17 09:34:47 -08:00
Gregory Schier
ac1e646e68 Download response, and some fixes 2024-01-16 17:02:55 -08:00
Gregory Schier
33374eefc7 Fix editor toolbar blocking things 2024-01-15 21:44:53 -08:00
Gregory Schier
7047df4f7e Better request creation (Closes #14) 2024-01-15 21:39:27 -08:00
Gregory Schier
c8bd4d0ae0 XPath plugin 2024-01-15 21:27:47 -08:00
Gregory Schier
1e79f76701 Fix send icon 2024-01-15 15:43:55 -08:00
Gregory Schier
18852dca06 Switch to Lucide icons 2024-01-15 15:42:28 -08:00
Gregory Schier
408e7e80b7 Improve response filter UX 2024-01-15 15:19:29 -08:00
Gregory Schier
fc185de023 JSONPath filter plugins working 2024-01-15 15:06:49 -08:00
Gregory Schier
bb9d3a42f3 Move plugin stuff around 2024-01-15 14:33:51 -08:00
Gregory Schier
baf0f4291d Fix request duplication 2024-01-15 13:47:44 -08:00
Gregory Schier
536066142c Fix workspace defaults 2024-01-15 12:25:13 -08:00
Gregory Schier
04cf16497d Better settings dialog 2024-01-15 12:16:44 -08:00
Gregory Schier
feb5972090 Fix resize observer 2024-01-15 12:02:08 -08:00
Gregory Schier
77bf5a58d8 Move request-related settings to workspace 2024-01-15 11:52:36 -08:00
Gregory Schier
3539642491 Bump beta version 2024-01-14 20:30:25 -08:00
Gregory Schier
08abea6a6f fix mac decorations 2024-01-14 17:22:31 -08:00
Gregory Schier
0045b85f00 Integrated titlebar windows 2024-01-14 16:44:04 -08:00
Gregory Schier
4b34c3d101 Further titlebar tweaks 2024-01-14 12:02:44 -08:00
Gregory Schier
4af0a15d9f Better titlebar control icons 2024-01-14 11:56:21 -08:00
Gregory Schier
3a4a76c58d Basic Linux/Windows integrated titlebar 2024-01-13 23:40:32 -08:00
Gregory Schier
3086d815c1 Fix hotkey formatting 2024-01-12 22:12:01 -08:00
Gregory Schier
a48a9eab4a beta tag 2024-01-12 22:00:55 -08:00
Gregory Schier
48664c66e5 fix appearance init 2024-01-12 21:59:46 -08:00
Gregory Schier
7aee5176a9 Vendor Openssl 2024-01-12 21:03:28 -08:00
Gregory Schier
0da68ced18 Hotkeys for request switcher 2024-01-12 21:03:20 -08:00
Gregory Schier
39f7d9c113 Appearance setting and gzip/etc support 2024-01-12 13:39:08 -08:00
Gregory Schier
138943bfb6 Initial settings implementation 2024-01-11 21:13:17 -08:00
Gregory Schier
c1c9f882a6 Dropdown manages hotkeys now 2024-01-11 10:18:05 -08:00
Gregory Schier
1bcf26f656 Hotkey for keyboard shortcut help 2024-01-10 22:05:16 -08:00
Gregory Schier
7c2466da5e Bump version number 2024-01-10 16:25:55 -08:00
Gregory Schier
7dc78a1f6f Add hotkey dialog and rust-only analytics 2024-01-10 16:18:08 -08:00
Gregory Schier
88d024023b Fix beta icon 2024-01-08 17:07:42 -08:00
Gregory Schier
626aacf982 Bump version to 2024.0.0 2024-01-08 15:57:59 -08:00
Gregory Schier
d5855c45a6 Hotkey labels 2024-01-08 15:57:21 -08:00
Gregory Schier
793bff9f27 Show hotkeys on empty views 2024-01-08 15:13:44 -08:00
Gregory Schier
88ea68e72f Remove base env, fix hotkeys, and QoL improvements 2024-01-07 22:24:19 -08:00
Gregory Schier
35e40d2c55 Fix hotkeys getting stuck on cmd+tab 2024-01-07 21:32:25 -08:00
Gregory Schier
c472b83409 Always show settings dropdown 2023-11-22 09:39:30 -08:00
Gregory Schier
52c26d235c Tweak margin 2023-11-22 09:37:50 -08:00
Gregory Schier
ac54729012 Fix bottom-up dropdown positioning 2023-11-22 09:35:56 -08:00
Gregory Schier
0586034ef4 Bump version 2023-11-22 09:06:47 -08:00
Gregory Schier
91790ba708 Better linux/Windows support for hotkeys 2023-11-22 09:06:22 -08:00
Gregory Schier
d8ab6c0b50 Good hotkey support 2023-11-22 09:01:48 -08:00
Gregory Schier
b600a21a2b Reset URL bar when request changes 2023-11-21 23:26:29 -08:00
Gregory Schier
4f9d1278f7 Env dialog hotkey 2023-11-21 22:35:28 -08:00
Gregory Schier
15aa93f5f9 Remove response body and basic hotkeys 2023-11-21 22:15:01 -08:00
Gregory Schier
c7798092d8 Remove app-specific menu items 2023-11-21 19:18:40 -08:00
Gregory Schier
5560593aaa Fix macOS menu and fallback URL 2023-11-21 09:24:13 -08:00
Gregory Schier
66639e651d Hide menu on windows/linux 2023-11-21 08:17:37 -08:00
Gregory Schier
8e42d5ccdb Disable sandboxing (again) 2023-11-19 21:59:55 -08:00
Gregory Schier
5c62594087 Fix drag-drop reorder 2023-11-19 21:43:01 -08:00
Gregory Schier
26b6c48657 Postman ID generation 2023-11-19 20:54:02 -08:00
Gregory Schier
0290aba982 Bump beta.3 2023-11-19 20:46:55 -08:00
Gregory Schier
0bafc4e4f5 Postman variables + urlencoded forms 2023-11-19 20:29:24 -08:00
Gregory Schier
9a36f94279 Add back Windows/Linux builds 2023-11-19 18:22:13 -08:00
Gregory Schier
1d8e66179e Remove Tauri context menu plugin 2023-11-19 18:21:10 -08:00
Gregory Schier
fda6d16d8e Fix header padding windows/linux 2023-11-19 18:14:49 -08:00
Gregory Schier
c4737916df Some tweaks 2023-11-19 18:13:32 -08:00
Gregory Schier
919465cdbb Beta 2 2023-11-19 17:41:58 -08:00
Gregory Schier
de3730fa4f Network entitlement 2023-11-19 17:41:46 -08:00
Gregory Schier
aff26fdd46 Try sandboxing again 2023-11-19 17:06:31 -08:00
Gregory Schier
3c0edf06af Remove sandboxing 2023-11-17 09:33:16 -08:00
Gregory Schier
cb8939db88 touch 2023-11-17 08:00:04 -08:00
Gregory Schier
bf4b3213c4 Out of beta 2023-11-17 07:53:26 -08:00
Gregory Schier
633d7c52c4 Tweak 2023-11-17 07:52:03 -08:00
Gregory Schier
0401cb92aa Format GraphQL variables 2023-11-17 07:51:03 -08:00
Gregory Schier
bff6c668a0 Drag into folder (Closes #8) 2023-11-17 07:36:01 -08:00
Gregory Schier
ee87e65763 Mostly move some stuff around 2023-11-16 18:53:34 -08:00
Gregory Schier
f165a0b827 Better update logic 2023-11-14 14:28:06 -08:00
Gregory Schier
f7426dc8ce Better dropdown menu 2023-11-14 10:56:56 -08:00
Gregory Schier
6114039f7e Version to beta 2023-11-14 10:07:39 -08:00
Gregory Schier
da414debe1 Beta channel updates 2023-11-14 08:57:46 -08:00
Gregory Schier
11f5541558 Multipart form UI and fixes 2023-11-14 00:32:02 -08:00
Gregory Schier
1bc155d684 Got multipart working (text-only) 2023-11-13 23:26:11 -08:00
Gregory Schier
335231060e Fix window title moving stoplights (for now) 2023-11-13 23:25:47 -08:00
Gregory Schier
0fdf64440f Postman import form data 2023-11-13 11:48:28 -08:00
Gregory Schier
a984fb33dc Some small improvements 2023-11-13 11:44:29 -08:00
Gregory Schier
41b1ec96c9 Form urlencoded bodies! 2023-11-13 11:28:37 -08:00
Gregory Schier
df83a61d6f Url parameters done 2023-11-13 10:52:11 -08:00
Gregory Schier
d289f1fd13 Minor tweaks 2023-11-12 21:16:42 -08:00
Gregory Schier
aea4e961aa Custom updater code 2023-11-12 21:16:33 -08:00
Gregory Schier
c554b73d48 Request body is now an object 2023-11-12 11:16:12 -08:00
Gregory Schier
b519bff3d6 Some postman import improvements 2023-11-11 10:43:11 -08:00
Gregory Schier
8381104302 Sync window title (Closes #13) 2023-11-10 15:55:50 -08:00
Gregory Schier
5ef7c6a1a2 Dev app icon 2023-11-10 13:06:12 -08:00
Gregory Schier
6d7a81850c Upgrade sqlx 2023-11-10 12:28:33 -08:00
Gregory Schier
6e5d5fcb95 Clean up importers 2023-11-10 11:39:17 -08:00
Gregory Schier
004fef6729 Fix is_empty check 2023-11-10 09:12:20 -08:00
Gregory Schier
0bec5a6405 Postman importer semi-complete 2023-11-10 09:08:20 -08:00
Gregory Schier
60b091ff1c Tidy up logs and general cleanup 2023-11-09 20:33:09 -08:00
Gregory Schier
bee1a5cb2d Move error alert to hook 2023-11-09 20:02:42 -08:00
Gregory Schier
bb2d3dd5b1 Show import errors 2023-11-09 20:00:19 -08:00
Gregory Schier
bf8aad04c7 Hacky Yaak import complete! 2023-11-09 19:40:31 -08:00
Gregory Schier
4306294a72 More fallback request handing 2023-11-09 17:42:10 -08:00
Gregory Schier
10f3722fe3 Request fallback name in header 2023-11-09 17:34:57 -08:00
Gregory Schier
c1af9ca44a Better default request names 2023-11-09 17:26:04 -08:00
Gregory Schier
5b230c74f0 Better non-named requests 2023-11-09 17:17:03 -08:00
Gregory Schier
5cebb4e61a Better Yaak export 2023-11-09 17:03:29 -08:00
Gregory Schier
bd9d1e2244 Refactor previous commit 2023-11-09 16:50:57 -08:00
Gregory Schier
9bdaa05f00 Default request name to URL(ish) 2023-11-09 16:47:00 -08:00
Gregory Schier
750ad0c902 Rename initial workspace to "Yaak" 2023-11-09 13:17:21 -08:00
Gregory Schier
a9c16838e6 No analytics in dev 2023-11-09 11:46:52 -08:00
Gregory Schier
d5065ab6d9 Analytics event properties 2023-11-09 11:44:59 -08:00
Gregory Schier
9ebb3ef532 Context menu, logs in DevTools, export, tweaks 2023-11-09 09:28:01 -08:00
Gregory Schier
aeda72f13e Fix plugin (again) 2023-11-08 13:33:15 -08:00
Gregory Schier
83aa9041cb Bundle plugin in Git 2023-11-08 13:06:49 -08:00
Gregory Schier
d51913509d Move plugins back 2023-11-08 12:34:14 -08:00
Gregory Schier
5106f28ba5 Fix permissions 2023-11-08 12:02:17 -08:00
Gregory Schier
0c55c6eaab Bump version 2023-11-08 10:12:01 -08:00
Gregory Schier
b0edbd19c8 Tweak theme 2023-11-08 10:11:29 -08:00
Gregory Schier
7630db79b7 Tweak theme 2023-11-08 09:56:13 -08:00
Gregory Schier
55a7b82567 Track screen size, os, and version 2023-11-08 09:49:29 -08:00
Gregory Schier
b5cb46918a Rust analytics and a few tweaks 2023-11-08 09:13:32 -08:00
Gregory Schier
a793ece1a5 Add basic analytics 2023-11-07 09:53:59 -08:00
Gregory Schier
0f6e4b641a Cancel responses on startup 2023-11-06 13:06:15 -08:00
Gregory Schier
5ac5fab0c6 Send all in a folder 2023-11-06 10:53:39 -08:00
Gregory Schier
8030a8a235 Rework workspace header 2023-11-06 10:42:59 -08:00
Gregory Schier
d98426cad3 Fix sidebar focus max recursion 2023-11-06 10:40:02 -08:00
Gregory Schier
06034a8fc4 Save after formatting GraphQL (Closes #9) 2023-11-06 07:20:47 -08:00
Gregory Schier
1ee9f9bb51 Move plugins back to root dir 2023-11-06 07:18:53 -08:00
Gregory Schier
4b99d1405e Persist sidebar collapsed state (Closes #10) 2023-11-06 07:18:42 -08:00
Gregory Schier
8480e52195 Vite to bundle insomnia plugin 2023-11-05 22:13:22 -08:00
Gregory Schier
243e65a992 Better import conversion 2023-11-05 14:46:08 -08:00
Gregory Schier
b82304a233 Basic import of request body and bearer auth 2023-11-05 14:35:25 -08:00
Gregory Schier
f7a4ea9735 Recursive Insomnia import! 2023-11-05 13:33:23 -08:00
Gregory Schier
33d1a84ecd Remove numbers from recent requests 2023-11-04 16:19:21 -07:00
Gregory Schier
f4a071ee05 Fix drop marker indent 2023-11-04 11:16:41 -07:00
Gregory Schier
e26ba0f9d0 Folder actions 2023-11-04 10:48:18 -07:00
Gregory Schier
b4e2a12375 Folder chevron icon 2023-11-03 23:10:44 -07:00
Gregory Schier
5e7aacd31a Fix arrow navigation for nested sidebar 2023-11-03 17:29:33 -07:00
Gregory Schier
00718df49e Folder-based drag-n-drop complete! 2023-11-03 16:29:21 -07:00
Gregory Schier
bb9025ab07 Sidebar ordering 95% done! 2023-11-03 15:02:17 -07:00
Gregory Schier
867f3908ed Nested sidebar ordering almost working 2023-11-03 14:08:46 -07:00
Gregory Schier
30e1ecac39 Add folder model 2023-11-03 07:49:44 -07:00
Gregory Schier
7eb2abe9b2 Even better focus state 2023-11-02 22:23:21 -07:00
Gregory Schier
a5ac8fa035 Remove focus on env sidebar buttons 2023-11-02 22:18:13 -07:00
Gregory Schier
dd705de155 Rearrange menus 2023-11-02 21:41:35 -07:00
Gregory Schier
b15cdec701 Refactor environment edit dialog 2023-11-02 20:38:33 -07:00
Gregory Schier
a99a36b5cc Base environments fully working 2023-11-02 18:43:39 -07:00
Gregory Schier
e0b0e3d781 Initial "plugin" system with importer (#7) 2023-11-02 18:08:43 -07:00
Gregory Schier
98a4834d4f Fix cursor color in single-line mode 2023-10-31 15:10:33 -07:00
Gregory Schier
32b135dbaf Fix sending of ephemeral requests 2023-10-30 08:24:49 -07:00
Gregory Schier
0fc8d12a06 Fix GQL introspection and bearer auth templating 2023-10-30 08:07:34 -07:00
Gregory Schier
3c2bdab101 Fix button styles 2023-10-30 07:27:27 -07:00
Gregory Schier
8b5d7ae3ed Fix editor stale callbacks and recent item deletion 2023-10-30 07:07:14 -07:00
Gregory Schier
51949f4fbf Refactored some core UI 2023-10-30 06:35:52 -07:00
Gregory Schier
6013cd2329 Plugin module loading 2023-10-29 20:50:23 -07:00
Gregory Schier
eba28ade48 Bump version 2023-10-29 17:22:27 -07:00
Gregory Schier
44af1ddc8a Fix sidebar scroll 2023-10-29 17:19:03 -07:00
Gregory Schier
63c0d09df8 A bit more playing with JS runtime 2023-10-29 17:05:48 -07:00
Gregory Schier
f305633d94 Initial "Hello World" for plugins 2023-10-29 16:43:28 -07:00
Gregory Schier
13155f8591 Fix request creation 2023-10-29 12:05:05 -07:00
Gregory Schier
f2ac97aa62 Restore recent environment on workspace change
Fixes #6
2023-10-29 11:32:55 -07:00
Gregory Schier
18eb0027a1 Fix var complete and env dialog actions 2023-10-29 11:18:55 -07:00
Gregory Schier
9e2803fcfb Remove broken key/value enter/backspace logic 2023-10-29 10:45:05 -07:00
Gregory Schier
705e30b6e0 Delete key/value on backspace 2023-10-29 10:26:38 -07:00
Gregory Schier
f1260911ea Move workspace menu, better env mgmt, QoL 2023-10-29 09:45:16 -07:00
Gregory Schier
076ff63dbe Bump version 2023-10-28 23:41:58 -07:00
Gregory Schier
899092b4d2 Better listening for path changes 2023-10-28 23:41:24 -07:00
Gregory Schier
c2c3a28aab Bump version 2023-10-28 22:14:51 -07:00
Gregory Schier
25c0db502e Fixed auto-focus in prompt and env dropdown 2023-10-28 22:14:12 -07:00
Gregory Schier
6dcbe45a53 Clear selected sidebar index on drag-drop end 2023-10-28 21:47:00 -07:00
Gregory Schier
e2b46f25ff Revert debug name 2023-10-28 21:43:09 -07:00
Gregory Schier
981182be46 Fix drag-n-drop things 2023-10-28 21:42:35 -07:00
Gregory Schier
ad164ebd5e Persist window paths 2023-10-28 21:23:46 -07:00
Gregory Schier
cacdad8826 Bump version to 2023.1.0 2023-10-28 19:15:33 -07:00
Gregory Schier
77e5142a7c Update placeholders when env changes 2023-10-28 19:14:51 -07:00
Gregory Schier
613081728d Placeholder error and fix env nav 2023-10-28 19:08:31 -07:00
Gregory Schier
23e77dfec1 Recent requests/workspaces. Closes #1 2023-10-28 18:46:54 -07:00
Gregory Schier
6e273ae2a3 Fix recent requests loading on startup 2023-10-28 18:27:18 -07:00
Gregory Schier
4061094988 Add tauri window save state plugin 2023-10-28 13:14:27 -07:00
Gregory Schier
82b185e27f Fix rustfmt 2023-10-28 12:45:25 -07:00
Gregory Schier
27dc261639 Handle enabled/disabled variables and render multi 2023-10-28 11:36:40 -07:00
Gregory Schier
7e45fecf19 Remove unused Variable type 2023-10-28 11:31:45 -07:00
Gregory Schier
1a5053380b Variables under Environment, and render all props 2023-10-28 11:29:29 -07:00
Gregory Schier
408665c62d Native Codemirror cursor 2023-10-27 13:14:41 -07:00
Gregory Schier
65efee2048 Only wrap URLBar on focus and hotkey to open recent requests 2023-10-27 12:40:43 -07:00
Gregory Schier
3faa66a1fc Resizing window no longer changes sidebar visibility
Fixes #4
2023-10-27 11:21:59 -07:00
Gregory Schier
9dafe4f704 Auto-expand URL bar height 2023-10-27 10:57:07 -07:00
Gregory Schier
356eaf1713 Environment deletion and better actions menu 2023-10-26 16:18:47 -07:00
Gregory Schier
f8584f1537 Stop autocomplete from jumping around 2023-10-26 15:27:48 -07:00
Gregory Schier
6ad6cb34b0 Fix request creation from menu 2023-10-26 10:41:14 -07:00
Gregory Schier
32b27cd780 Send requests with active environment 2023-10-26 10:32:06 -07:00
Gregory Schier
0344a1e8c9 Move create request and fix slow HTML highlighting 2023-10-26 09:42:19 -07:00
Gregory Schier
0515271c12 Better project selector, Fixes #2, and a bunch more 2023-10-26 09:11:44 -07:00
Gregory Schier
5ae8d54ce0 Fixed some routing and introspection requests 2023-10-25 21:53:18 -07:00
Gregory Schier
33c406ce49 Environments in URL and better rendering 2023-10-25 11:13:00 -07:00
Gregory Schier
3b660ddbd0 Move responses dropdown to separate component 2023-10-25 07:59:10 -07:00
Gregory Schier
3132728a27 Fix dialog height 2023-10-25 00:02:51 -07:00
Gregory Schier
7063128342 Better style when no active environment 2023-10-24 23:58:12 -07:00
Gregory Schier
2187775462 Environment dropdown and actions 2023-10-24 09:17:29 -07:00
Gregory Schier
18adcd1004 Started on environment edit dialog 2023-10-23 21:00:36 -07:00
Gregory Schier
b0656d1e38 Hacky implementation of variable autocomplete 2023-10-23 10:31:21 -07:00
Gregory Schier
38e66047e0 Rendered first variable! 2023-10-22 22:30:29 -07:00
Gregory Schier
c24f049dac Updating environments! 2023-10-22 22:06:51 -07:00
Gregory Schier
53d13c8172 Update .gitignore 2023-10-22 20:40:00 -07:00
Gregory Schier
0727c6e437 Prettier and start of env editor 2023-10-22 20:38:57 -07:00
Gregory Schier
8328d20150 Environments data model 2023-10-22 18:28:56 -07:00
Gregory Schier
afe6a3bf57 Environment data model backend 2023-10-22 16:05:09 -07:00
Gregory Schier
d920632cbd Fix some eslint warnings 2023-10-22 11:02:39 -07:00
Gregory Schier
5c456fd4d5 Add APPLE_TEAM_ID 2023-10-18 14:12:08 -07:00
Gregory Schier
38c247e350 Revert artifacts things 2023-10-18 13:25:35 -07:00
Gregory Schier
0c8f72124a Bump cargo deps 2023-10-18 13:25:20 -07:00
Gregory Schier
80ed6b1525 Bump version 2023-10-18 12:14:38 -07:00
Gregory Schier
4424b3f208 Fix sidebar drag-n-drop 2023-10-18 11:58:58 -07:00
Gregory Schier
2c75abce09 Retry button on introspection errors 2023-06-12 13:20:42 -07:00
Gregory Schier
4e15eb197f Fix autocomplete doc font size 2023-05-31 21:32:48 -07:00
Gregory Schier
a7544b4f8c Persist introspection queries and also improve 2023-05-31 21:29:41 -07:00
Gregory Schier
d126aad172 Update tauri NPM 2023-05-29 12:49:50 -07:00
Gregory Schier
acc5c0de50 Fix graphql instrospection 2023-05-29 12:31:34 -07:00
Gregory Schier
3391da111d Change version 2023-04-27 16:53:39 -07:00
Gregory Schier
e37ce96956 Version 1.0.0 2023-04-27 16:47:49 -07:00
Gregory Schier
c51831c975 Fix window methods on Linux/Windows 2023-04-27 16:27:02 -07:00
Gregory Schier
180aa39de4 Cross platform window controls 2023-04-27 10:19:49 -07:00
Gregory Schier
3bd780782e Fix ubuntu build 2023-04-26 17:00:30 -07:00
Gregory Schier
f9ba2f79c2 Windows and Linux 2023-04-26 16:54:51 -07:00
Gregory Schier
d9493de2be Bump version 2023-04-26 16:47:50 -07:00
Gregory Schier
bc9a623742 Very basic CSV viewer 2023-04-22 21:53:04 +08:00
Gregory Schier
532edbf274 Truncate response files 2023-04-14 14:15:33 -07:00
Gregory Schier
1585692328 Randomly offset new windows 2023-04-14 14:05:23 -07:00
Gregory Schier
083f565b12 Fix text encoding and delete responses 2023-04-14 13:50:41 -07:00
Gregory Schier
f7f7438c9e Delete response files 2023-04-14 12:17:11 -07:00
Gregory Schier
19934a93bb Readonly editor disable tabindex 2023-04-13 22:36:11 -07:00
Gregory Schier
577cfe5bdc Fix imageview padding 2023-04-13 22:33:47 -07:00
Gregory Schier
43ac6afae1 Duration and size tags 2023-04-13 20:50:17 -07:00
Gregory Schier
8cc11703d3 Comment 2023-04-13 18:55:32 -07:00
Gregory Schier
4f7a116378 Always store response on filesystem 2023-04-13 18:52:56 -07:00
Gregory Schier
513793d9ce Support binary responses! 2023-04-13 18:48:40 -07:00
Gregory Schier
67f32b6734 Blur backdrop 2023-04-11 16:12:26 -07:00
Gregory Schier
66813d67fe Autofocus buttons 2023-04-11 14:04:23 -07:00
Gregory Schier
a38691ed53 Better opening workspaces and redirect workspace to recent request 2023-04-11 11:11:36 -07:00
Gregory Schier
deeefdcfbf Button disabled style opacity 2023-04-10 16:03:45 -07:00
Gregory Schier
db292511b1 Dropdown keys and pointer events 2023-04-10 16:02:29 -07:00
Gregory Schier
1a5334c1ce Upgrade deno core 2023-04-10 11:16:25 -07:00
Gregory Schier
11002abe39 Tweak response history 2023-04-09 23:15:51 -07:00
Gregory Schier
d922dcb062 Fixed multi-window model sync 2023-04-09 22:32:47 -07:00
Gregory Schier
6fcaa18e86 Tweak recent requests 2023-04-09 22:25:00 -07:00
Gregory Schier
7664c941dd Toggle settings 2023-04-09 22:12:16 -07:00
Gregory Schier
6f5cb528c6 Fix sidebar request focus 2023-04-09 22:03:41 -07:00
Gregory Schier
ebb78922f0 More stuff on sidebar 2023-04-09 21:52:04 -07:00
Gregory Schier
2285fe9f1c Small tweaks 2023-04-09 15:32:13 -07:00
Gregory Schier
38ba8625d8 Request history navigator 2023-04-09 15:26:54 -07:00
Gregory Schier
ab5681c7ad Enter name on create workspace 2023-04-09 12:27:02 -07:00
Gregory Schier
f66dcb9267 Rename workspace 2023-04-09 12:23:41 -07:00
Gregory Schier
1b6cfbac77 Sidebar hover transitions 2023-04-06 16:30:46 -07:00
Gregory Schier
4c27e788ea Remove some more key value usage 2023-04-06 16:26:56 -07:00
Gregory Schier
769da0b052 A bunch of tweaks 2023-04-06 16:05:25 -07:00
Gregory Schier
6b60c86300 macOS 12 2023-04-06 08:39:30 -07:00
Gregory Schier
30c1b5e8c7 Remove system tray icon 2023-04-06 08:15:40 -07:00
Gregory Schier
10af9b6f99 Minor tweaks 2023-04-04 17:21:02 -07:00
Gregory Schier
aa8c066f2d Fix some things 2023-04-04 16:56:45 -07:00
Gregory Schier
b913b74449 Editor line wrapping support (not used yet) 2023-04-04 16:40:37 -07:00
Gregory Schier
b71adce50b remove janky last location tracking 2023-04-04 16:23:08 -07:00
Gregory Schier
0fbb44c701 Fix resize cursor 2023-04-04 16:12:45 -07:00
Gregory Schier
de335e8637 Better button styles 2023-04-04 15:40:25 -07:00
Gregory Schier
2999f63a4c Bump version 2023-04-04 13:56:24 -07:00
Gregory Schier
2abc5e6f0b Some small fixes 2023-04-04 13:56:14 -07:00
Gregory Schier
639de4321e A few fixes 2023-04-04 13:31:48 -07:00
Gregory Schier
b3c461afdd Better status tags and delete request on key 2023-04-04 12:36:30 -07:00
Gregory Schier
7d154800a0 Remove expects from request sending 2023-04-04 08:14:32 -07:00
Gregory Schier
b48ed0399e Fix web view height 2023-04-04 07:51:41 -07:00
Gregory Schier
c5d6e7d74a Fix autocomplete spacing 2023-04-04 07:51:19 -07:00
Gregory Schier
e82f915363 Fix input focus border 2023-04-03 12:19:37 -07:00
Gregory Schier
3128e9ce76 Hot keys and cleanup 2023-04-03 07:59:49 -07:00
Gregory Schier
bc0e86757c Add entitlemet for v8 2023-04-02 20:23:21 -07:00
Gregory Schier
fec99916c2 Debug codesigned build 2023-04-02 19:09:14 -07:00
Gregory Schier
3b5d059b11 Disable code signing 2023-04-02 18:27:14 -07:00
Gregory Schier
c3fe2acc8a Fix tauri script command 2023-04-02 17:25:24 -07:00
Gregory Schier
4d002c412b Fix universal binary 2023-04-02 17:12:20 -07:00
Gregory Schier
46d152b5f1 Bump version 2023-04-02 15:44:41 -07:00
Gregory Schier
25fa81ebbc Fix toolchain 2023-04-02 15:44:21 -07:00
Gregory Schier
7c2de3c360 Add proper target 2023-04-02 15:42:19 -07:00
Gregory Schier
3a3b187cd0 Try universal binary 2023-04-02 15:33:13 -07:00
Gregory Schier
3226bbe083 Fix version 2023-04-02 15:25:24 -07:00
Gregory Schier
a1e4e0e6c9 Bump version 2023-04-02 14:54:41 -07:00
Gregory Schier
b3aa8b893b Notorization (hopefully) 2023-04-02 14:53:49 -07:00
Gregory Schier
f057139634 Change tabs again 2023-04-02 11:11:53 -07:00
Gregory Schier
71a2b11ab4 Better response headers 2023-04-02 10:45:41 -07:00
Gregory Schier
587254a0e7 Show response headers 2023-04-01 23:43:22 -07:00
Gregory Schier
9f4de66f3c Some more refactoring 2023-04-01 21:48:30 -07:00
Gregory Schier
b0d8908724 Refactor debounce and tauri event listeners 2023-04-01 21:39:46 -07:00
Gregory Schier
15c22d98c6 Fix dropdown and dialog key handling 2023-04-01 21:04:39 -07:00
Gregory Schier
3105ae0edc Refactor sidebar display 2023-04-01 20:58:53 -07:00
Gregory Schier
11a89f06c1 Better GraphQL schema fetching 2023-04-01 17:53:36 -07:00
Gregory Schier
9cbe24e740 More eslint fixes 2023-04-01 15:48:37 -07:00
Gregory Schier
bfbed13b8f Add React hooks eslint 2023-04-01 15:26:57 -07:00
Gregory Schier
2268de6321 Fix Tauri listeners 2023-04-01 00:02:17 -07:00
Gregory Schier
dd99aa7fcd Memo editor 2023-03-31 23:19:15 -07:00
Gregory Schier
be436bb706 Fix request duplication 2023-03-31 22:54:32 -07:00
Gregory Schier
bd48726f44 Fix tauri listeners causing too many updates 2023-03-31 22:48:34 -07:00
Gregory Schier
10bea83f98 Remove import 2023-03-31 22:42:41 -07:00
Gregory Schier
8122b4fb84 Fix 2023-03-31 22:42:26 -07:00
Gregory Schier
3ae57fb2d8 Upgrade Deno 2023-03-31 22:42:08 -07:00
Gregory Schier
6dc3eecca4 Tweak 2023-03-31 16:14:25 -07:00
Gregory Schier
9d1d732154 Fix send hotkey 2023-03-31 16:13:34 -07:00
Gregory Schier
8a117415b7 Better schema fetching 2023-03-31 16:02:09 -07:00
Gregory Schier
d36623ebc9 Finally fix the editor! 2023-03-31 15:56:35 -07:00
Gregory Schier
94a3ae3696 Fix editor blurring bug! 2023-03-31 13:53:28 -07:00
Gregory Schier
2836a28988 Better model updates 2023-03-31 13:21:02 -07:00
Gregory Schier
946d7dc89e Fix text obscuring 2023-03-30 17:22:52 -07:00
Gregory Schier
af6300f18b Button ring colors 2023-03-30 17:17:07 -07:00
Gregory Schier
905cb4b18e Remove dummy button 2023-03-30 17:12:38 -07:00
Gregory Schier
305ed09547 Confirm deletions 2023-03-30 17:09:11 -07:00
Gregory Schier
643356bad3 Dedicated event for model creation 2023-03-30 16:49:49 -07:00
Gregory Schier
e458675627 Unify text selection color 2023-03-30 16:36:24 -07:00
Gregory Schier
91e3853692 Some icon tweaks 2023-03-30 16:29:14 -07:00
Gregory Schier
5f0876a136 Fix strict mode editor blur bug 2023-03-30 10:38:33 -07:00
Gregory Schier
3a38127fb4 Better tauri listeners and stuff 2023-03-30 09:05:54 -07:00
Gregory Schier
f3b6070235 Remove updated_by, remember last location 2023-03-30 08:11:51 -07:00
Gregory Schier
5e6e78eb9e Remove hardcoded window config 2023-03-29 22:16:21 -07:00
Gregory Schier
9b66a1d1a8 Fix build 2023-03-29 22:15:55 -07:00
Gregory Schier
e954d0d7bc Remove unused import 2023-03-29 21:53:49 -07:00
Gregory Schier
dab2df7e79 Better multi-window updates 2023-03-29 21:53:20 -07:00
Gregory Schier
bc40e22008 Fixed key/value stuff 2023-03-29 14:46:36 -07:00
Gregory Schier
eef262c398 Fix bundle parts 2023-03-29 14:00:34 -07:00
Gregory Schier
8eab6e14db Fix(ish) multiwindow updates 2023-03-29 11:15:37 -07:00
Gregory Schier
ded33a110a Obscure text 2023-03-29 10:16:51 -07:00
Gregory Schier
e448a7602a Simple auth schemes 2023-03-29 09:03:38 -07:00
Gregory Schier
4c22215ca5 Good start to multi-window 2023-03-28 18:29:40 -07:00
Gregory Schier
4f501abb72 Focus traps for dialog and dropdown 2023-03-26 23:07:09 -07:00
Gregory Schier
b2dcc38982 Confirmation Dialogs 2023-03-26 12:02:20 -07:00
Gregory Schier
11b719955b Floating sidebar 2023-03-26 10:09:28 -07:00
Gregory Schier
d563ac63db Panel icons 2023-03-25 23:29:04 -07:00
Gregory Schier
6d826064c6 Update dialog 2023-03-25 21:59:18 -07:00
Gregory Schier
d30b9d6518 Optimistically-update key values 2023-03-25 21:54:00 -07:00
Gregory Schier
8da3364d0f More tweaks 2023-03-25 21:40:14 -07:00
Gregory Schier
07c372b7f5 Animate dropdown 2023-03-25 21:36:17 -07:00
Gregory Schier
7e01f38253 Animate sidebar transition 2023-03-25 21:31:52 -07:00
Gregory Schier
ba637009a7 Refactor and improve layout resizing 2023-03-25 21:16:10 -07:00
Gregory Schier
da7388e510 Even better layouts 2023-03-25 18:33:01 -07:00
Gregory Schier
3ec88fc896 Better grid layouts 2023-03-25 18:12:09 -07:00
Gregory Schier
1c9381b2bd Global layout component 2023-03-25 13:26:31 -07:00
Gregory Schier
06349b8d5b Better dropdown separator 2023-03-25 11:06:05 -07:00
Gregory Schier
6dc7dc6ad2 Fix sidebar drag 2023-03-24 08:37:52 -07:00
Gregory Schier
f981a15ec3 Upgrade TYpescript 2023-03-23 15:37:36 -07:00
Gregory Schier
8b648c0301 Fix resize 2023-03-23 07:47:58 -07:00
Gregory Schier
83ce09075b Style tweak 2023-03-21 23:59:09 -07:00
Gregory Schier
168dfb9f6b GraphQL autocomplete and duplicate request 2023-03-21 23:54:45 -07:00
Gregory Schier
9b8961c23d Tweak sidebar drag resizer 2023-03-21 19:36:32 -07:00
Gregory Schier
89bca42ee6 Minor style tweaks 2023-03-21 18:31:05 -07:00
Gregory Schier
07d2a43a17 Pull out resize bar 2023-03-21 16:53:49 -07:00
Gregory Schier
c84f2afd09 Resize titlebar and tweak things 2023-03-21 16:42:52 -07:00
Gregory Schier
df4dbaecc8 Remove icon generation from build script 2023-03-21 14:21:07 -07:00
Gregory Schier
d9bf03cefe query client cache and better body types 2023-03-21 11:38:37 -07:00
Gregory Schier
39223e8d89 Fix workspace deletion 2023-03-21 09:32:15 -07:00
Gregory Schier
67925e18b2 Use proper gray for syntax 2023-03-20 17:15:12 -07:00
Gregory Schier
89ad65513d fix import 2023-03-20 17:13:14 -07:00
Gregory Schier
90166ddfa3 Minor tweaks 2023-03-20 17:12:19 -07:00
Gregory Schier
0981b23faf Fix URL bar spacing 2023-03-20 17:01:29 -07:00
Gregory Schier
664f3b4d87 Better radio dropdown type 2023-03-20 16:54:26 -07:00
Gregory Schier
dc97b91a4e Typesafe routing and CM line height issue 2023-03-20 16:47:36 -07:00
Gregory Schier
d310272d19 Better tab dropdown handling 2023-03-20 14:14:30 -07:00
Gregory Schier
f1be3f01e1 Fix request creation priority 2023-03-20 13:56:03 -07:00
Gregory Schier
c57b6e1d73 Remove log 2023-03-20 13:49:35 -07:00
Gregory Schier
a938dc45f0 Handle "no body" case 2023-03-20 13:49:21 -07:00
Gregory Schier
bb139744a1 Small fix 2023-03-20 13:37:14 -07:00
Gregory Schier
3aa3e09552 Fix pointer window drag 2023-03-20 13:34:49 -07:00
Gregory Schier
74abfd21b8 Fix extra dropdown element 2023-03-20 13:19:23 -07:00
Gregory Schier
e703817ba2 Remove most of Radix UI 2023-03-20 13:16:58 -07:00
Gregory Schier
80dd1e457b Better Header validation 2023-03-20 01:38:05 -07:00
Gregory Schier
ea9f8d3ab2 Tweak sidebar 2023-03-20 01:30:45 -07:00
Gregory Schier
fa222bdf12 Fix pair editor container 2023-03-20 01:18:44 -07:00
Gregory Schier
45b360dabd Fix input thingy 2023-03-20 01:14:13 -07:00
Gregory Schier
5923399359 Container queries! 2023-03-20 01:08:41 -07:00
Gregory Schier
f4600f3e90 Better pair editor delete button 2023-03-20 00:30:42 -07:00
Gregory Schier
f883837685 Pair validation 2023-03-20 00:17:29 -07:00
Gregory Schier
b58bc409f0 Don't send disabled headers 2023-03-20 00:05:19 -07:00
Gregory Schier
e893e539bb Small tweak 2023-03-20 00:04:40 -07:00
Gregory Schier
90294fbb5d Pair checkboxes and fix twig indent 2023-03-20 00:03:33 -07:00
Gregory Schier
ae65f222bc Rewrote twig grammar 2023-03-19 22:12:11 -07:00
Gregory Schier
1b9813fb4c Re-order of pair editor 2023-03-19 13:28:57 -07:00
Gregory Schier
b708b5ae41 Better header editor and added completion data 2023-03-19 11:09:21 -07:00
Gregory Schier
df136fa915 A couple tweaks 2023-03-19 01:01:13 -07:00
Gregory Schier
f8329f5b8d Persist sort priority! 2023-03-19 00:48:09 -07:00
Gregory Schier
21141090de Create new workspace, and more optimizations 2023-03-18 19:36:31 -07:00
Gregory Schier
c0d9740a7d Optimized a few components 2023-03-18 18:49:01 -07:00
Gregory Schier
afcf630443 Fix sidebar drag-n-drop 2023-03-18 18:09:36 -07:00
Gregory Schier
1fe2c9826a Got drag opacity working 2023-03-18 15:06:38 -07:00
Gregory Schier
7272b80a3f Good start to drag-n-drop sidebar! 2023-03-18 14:41:07 -07:00
Gregory Schier
92114b7368 Fix mixed parser 2023-03-17 17:57:43 -07:00
Gregory Schier
f39d3e7eed Dropdown highlight 2023-03-17 17:32:24 -07:00
Gregory Schier
cbe0d27a5e Beginnings of autocomplete for headers 2023-03-17 16:51:20 -07:00
Gregory Schier
cd39699467 Flatten migrations, kvs lib, fix tabs 2023-03-17 08:36:21 -07:00
Gregory Schier
b3ea67aacf Sidebar item dropdown 2023-03-16 15:37:53 -07:00
Gregory Schier
db4ed9797c Sidebar dragging 2023-03-16 14:34:49 -07:00
Gregory Schier
1ea7d7d685 Add devtools toggle hotkey 2023-03-16 11:25:38 -07:00
Gregory Schier
2df725b57a Adjust window sizes 2023-03-16 11:16:23 -07:00
Gregory Schier
74e6648249 Store appearance in k/v 2023-03-16 11:01:30 -07:00
Gregory Schier
1026350d9c Hotkeys and view mode kvs 2023-03-16 09:24:28 -07:00
Gregory Schier
98fb87874d Some fixes 2023-03-15 23:33:46 -07:00
Gregory Schier
41fc3afdc1 Got key values working 2023-03-15 23:24:41 -07:00
Gregory Schier
83dbf46ba4 Fix editor padding 2023-03-15 17:29:35 -07:00
Gregory Schier
0b2e35bdde Minor style updates 2023-03-15 17:25:04 -07:00
Gregory Schier
d90a7331c9 Add stuff to app header 2023-03-15 16:35:19 -07:00
Gregory Schier
264e64a996 Better request delete and formatting 2023-03-15 09:41:38 -07:00
Gregory Schier
8915915c47 Fix graphql and other things 2023-03-15 09:06:56 -07:00
Gregory Schier
951ed787fa Header editor to pair editor 2023-03-15 08:09:45 -07:00
Gregory Schier
64ef6b0c22 Better header editor 2023-03-15 07:54:04 -07:00
Gregory Schier
ef18377b3c Strict mode and tweak layout padding 2023-03-14 20:19:45 -07:00
Gregory Schier
5904b6fded Add GraphQL variables editor 2023-03-14 19:56:02 -07:00
Gregory Schier
f4401e77bb GraphQL query editor transformer works! 2023-03-14 19:08:18 -07:00
Gregory Schier
efa5455a7b Add body type to request and tab dropdown 2023-03-14 11:18:56 -07:00
Gregory Schier
619c8d9e72 Improved header editor 2023-03-14 00:54:41 -07:00
Gregory Schier
bdf89ac288 Fix platform check 2023-03-14 00:15:01 -07:00
Gregory Schier
debd3c8185 Some small changes 2023-03-14 00:08:03 -07:00
Gregory Schier
f81a3ae8e7 Move stuff around 2023-03-13 23:30:14 -07:00
Gregory Schier
7d4e9894c3 Refactor hooks to be easier to use 2023-03-13 23:25:41 -07:00
Gregory Schier
4bf22d8a60 Fix header editor and scroll in general 2023-03-13 19:37:36 -07:00
Gregory Schier
8be4971a23 Lazy load routes 2023-03-13 13:56:13 -07:00
Gregory Schier
359e916b73 Back to React 2023-03-13 09:50:49 -07:00
Gregory Schier
68058f3e41 Move some stuff around 2023-03-13 09:24:38 -07:00
Gregory Schier
0c6fa3e634 Fix URL bar 2023-03-13 00:13:25 -07:00
Gregory Schier
0fa25c6335 Fix ButtonLink and edit request names 2023-03-13 00:11:23 -07:00
Gregory Schier
5684479f1d Remove old rust cache action 2023-03-12 22:48:43 -07:00
Gregory Schier
2d1603601c Better rust cache 2023-03-12 22:47:43 -07:00
Gregory Schier
f5394b2210 Start GraphQL support 2023-03-12 22:43:25 -07:00
Gregory Schier
833db5df06 Fix artifact tag 2023-03-12 21:41:15 -07:00
Gregory Schier
525ac7e980 Remove wasm stuff 2023-03-12 21:25:31 -07:00
Gregory Schier
44a747c80a Use tauri action 2023-03-12 21:13:08 -07:00
Gregory Schier
2056e7f40a Fix traffic lights thingy 2023-03-12 20:47:52 -07:00
Gregory Schier
9b6c1ad364 Cache cargo bin for "install" 2023-03-12 19:10:39 -07:00
Gregory Schier
34987bcacb Refformat 2023-03-12 19:03:27 -07:00
Gregory Schier
b62c11222a Fix artifact upload 2023-03-12 19:01:48 -07:00
Gregory Schier
b3cee3ace3 Fix dev 2023-03-12 18:39:02 -07:00
Gregory Schier
222c054c95 Split out macos deps 2023-03-12 18:36:25 -07:00
Gregory Schier
46f18a2491 Cache workflow 2023-03-12 18:28:14 -07:00
Gregory Schier
f2ca8e2753 Add wasm-pack 2023-03-12 18:19:20 -07:00
Gregory Schier
b0d243c378 Install rsw 2023-03-12 18:14:38 -07:00
Gregory Schier
6161fb86c8 Fix artifact names 2023-03-12 18:13:00 -07:00
Gregory Schier
b09cc91fe5 Fix build command 2023-03-12 18:11:24 -07:00
Gregory Schier
ef1638cbb3 Update secrets context 2023-03-12 18:07:57 -07:00
Gregory Schier
00ef8743f2 Update workflow name 2023-03-12 18:05:45 -07:00
Gregory Schier
68222659e3 Fix workflow 2023-03-12 18:05:13 -07:00
Gregory Schier
69420a4bba Start of auto updates 2023-03-12 18:04:11 -07:00
Gregory Schier
0161bbaeb1 Fix tabbing to tabs 2023-03-11 23:32:39 -08:00
Gregory Schier
948dbfe3cc Fix eslint errors 2023-03-11 23:29:25 -08:00
Gregory Schier
338ba8b189 Got tab content scrolling working 2023-03-11 22:36:13 -08:00
Gregory Schier
ca4655b441 Removed some debug stuff 2023-03-10 10:43:15 -08:00
Gregory Schier
bf37499428 Refactor editor to update better 2023-03-10 10:39:23 -08:00
Gregory Schier
0b94b57e2a Fix headers persistence and better sending 2023-03-09 13:38:17 -08:00
Gregory Schier
fc40aead98 Hook up header editor! 2023-03-09 13:07:13 -08:00
Gregory Schier
7d7f934e6a Fix 2023-03-09 10:58:27 -08:00
Gregory Schier
d5fbf4d622 Fix blur de-select speed 2023-03-09 10:57:34 -08:00
Gregory Schier
e4f6c919dc Fix Codemirror performance!! 2023-03-09 10:50:55 -08:00
Gregory Schier
4d806ff2b1 Switch to Preact!!! 2023-03-09 00:47:25 -08:00
Gregory Schier
bf8f12274f Move some things around 2023-03-08 23:20:15 -08:00
Gregory Schier
f4f438d9fe Better scrollbar color 2023-03-08 19:23:24 -08:00
Gregory Schier
2434f373be Zoom, better sizes, color picker, sidebar footer 2023-03-08 19:22:04 -08:00
Gregory Schier
2bb2061f97 Read-only editor 2023-03-08 16:53:13 -08:00
Gregory Schier
2c011a5c2a More theme tweaks 2023-03-08 16:37:20 -08:00
Gregory Schier
f66b0ccea1 Debounce autocomplete 2023-03-08 11:25:20 -08:00
Gregory Schier
665dd8447d Minor theme updates again 2023-03-08 09:43:35 -08:00
Gregory Schier
1b61ce31e6 Editor tweaks 2023-03-07 23:05:33 -08:00
Gregory Schier
ef4d960698 Remove unneeded space 2023-03-07 22:58:13 -08:00
Gregory Schier
b6d557b632 Fix small view 2023-03-07 22:55:51 -08:00
Gregory Schier
b700bd356c Minor style tweaks 2023-03-07 22:21:58 -08:00
Gregory Schier
620dd7d3ef Lots more theme stuff 2023-03-07 21:52:21 -08:00
Gregory Schier
6575121902 Start of themes 2023-03-07 11:24:38 -08:00
Gregory Schier
7c1755a0dc More subtle layout tweaks 2023-03-06 08:57:57 -08:00
Gregory Schier
8ad301a666 More layout fiddling and error page 2023-03-04 22:26:00 -08:00
Gregory Schier
ae24cd4939 More work on the layout 2023-03-04 21:51:17 -08:00
Gregory Schier
7152e1845e Try new layout and a bunch of editor fixes 2023-03-04 19:06:12 -08:00
Gregory Schier
96c1dd4081 Fix autocomplete inside dialog 2023-03-03 17:03:20 -08:00
Gregory Schier
87c7b3a663 Beginnings of Header Editor 2023-03-03 13:18:57 -08:00
Gregory Schier
c1be46a539 Fix tailwind dark selector 2023-03-03 07:54:19 -08:00
Gregory Schier
4655e0018b Fix content type in URL 2023-03-02 23:17:09 -08:00
Gregory Schier
da5ba2e3be Add Dialog component 2023-03-02 18:46:10 -08:00
Gregory Schier
aaf95f565f More colors 2023-03-02 17:56:53 -08:00
Gregory Schier
f32b984e77 Minor style tweaks 2023-03-02 16:16:41 -08:00
Gregory Schier
548aa4c7cd Improved autocompletion! 2023-03-02 11:14:51 -08:00
Gregory Schier
0ccceaac77 Rename, fix autocomplete and singleline, etc... 2023-03-02 10:42:43 -08:00
Gregory Schier
70f534f1d8 Editor placeholder 2023-03-01 14:22:10 -08:00
Gregory Schier
61fe95b300 Some minor bugs 2023-03-01 14:16:02 -08:00
Gregory Schier
915e0e8613 Fix migrations for build and iframe rendering 2023-03-01 10:31:50 -08:00
Gregory Schier
aace2580da Tweaks 2023-03-01 10:19:21 -08:00
Gregory Schier
3d36905664 Response streaming 2023-03-01 09:05:00 -08:00
Gregory Schier
0d671423da Autocomplete, and more CM stuff! 2023-02-28 22:54:54 -08:00
Gregory Schier
aebfcb9437 Some small tweaks 2023-02-28 17:25:59 -08:00
Gregory Schier
be7ef7beb1 Better editor updating 2023-02-28 12:41:03 -08:00
Gregory Schier
d77ed0c5cc URL highlighting with inline CM 2023-02-28 11:26:26 -08:00
Gregory Schier
e57e7bcec5 Implement request deletion 2023-02-27 15:42:06 -08:00
Gregory Schier
a637842ce4 Tauri events for request model updates 2023-02-27 13:28:50 -08:00
Gregory Schier
fc54ec49af Split request upsert command 2023-02-27 10:00:57 -08:00
Gregory Schier
5c43d8510a Add toggle for pretty view 2023-02-27 09:08:48 -08:00
Gregory Schier
83f84ded8d Small tweaks 2023-02-26 15:25:55 -08:00
Gregory Schier
5658da34a2 Add variable highlighting widgets 2023-02-26 15:06:14 -08:00
Gregory Schier
38e8ef6535 Dropdown scrolling 2023-02-25 23:33:07 -08:00
Gregory Schier
8c89b06238 Show response body size 2023-02-25 23:08:19 -08:00
Gregory Schier
d85c021305 A bunch more small things 2023-02-25 23:04:31 -08:00
Gregory Schier
83bb18df03 Added react-router 2023-02-25 18:04:14 -08:00
Gregory Schier
93105a3e89 Migrations and initial data stuff 2023-02-25 16:39:18 -08:00
Gregory Schier
ba3b899115 Minor tweaks 2023-02-24 17:01:48 -08:00
Gregory Schier
fcfbc1d1da Dummy requests in sidebar 2023-02-24 16:46:56 -08:00
Gregory Schier
72486b448c Codemirror initial value support 2023-02-24 16:43:47 -08:00
Gregory Schier
7dea1b7870 Send request body 2023-02-24 16:09:19 -08:00
Gregory Schier
4de2c496c9 Vendor basicSetup 2023-02-24 14:51:56 -08:00
Gregory Schier
9e1393a392 Additional methods and tweaks 2023-02-24 14:10:25 -08:00
Gregory Schier
0901690ed6 Focus states 2023-02-24 12:35:13 -08:00
Gregory Schier
95303648cc Hook up theme and clear responses 2023-02-24 12:13:30 -08:00
Gregory Schier
1dbb08c045 SQLite store in proper dir 2023-02-22 20:18:14 -08:00
Gregory Schier
00a7d9a180 Started on grid layout 2023-02-22 19:44:44 -08:00
Gregory Schier
6c549dc086 Save responses in DB 2023-02-22 18:53:44 -08:00
Gregory Schier
dc368e326a Better URL bar 2023-02-22 16:15:25 -08:00
Gregory Schier
e42188a627 Cleaner URL bar and some improvements 2023-02-22 15:58:04 -08:00
Gregory Schier
7a6a337eff Refactor classname usage 2023-02-21 18:03:57 -08:00
Gregory Schier
3907344884 Some minor tweaks 2023-02-21 17:56:48 -08:00
1209 changed files with 37683 additions and 92439 deletions

5
.eslintignore Normal file
View File

@@ -0,0 +1,5 @@
node_modules/
dist/
.prettierrc.cjs
.eslintrc.cjs
env.d.ts

37
.eslintrc.cjs Normal file
View File

@@ -0,0 +1,37 @@
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"
],
parser: "@typescript-eslint/parser",
parserOptions: {
project: ["./tsconfig.json"]
},
ignorePatterns: ["src-tauri/**/*", "plugins/**/*"],
settings: {
react: {
version: "detect"
},
"import/resolver": {
node: {
paths: ["src-web"],
extensions: [".ts", ".tsx"]
}
}
},
rules: {
"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"
}]
}
};

7
.gitattributes vendored
View File

@@ -1,7 +0,0 @@
src-tauri/vendored/**/* linguist-generated=true
src-tauri/gen/schemas/**/* linguist-generated=true
**/bindings/* linguist-generated=true
src-tauri/yaak-templates/pkg/* linguist-generated=true
# Ensure consistent line endings for test files that check exact content
src-tauri/yaak-http/tests/test.txt text eol=lf

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
# These are supported funding model platforms
github: gschier

View File

@@ -1,38 +0,0 @@
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Desktop (please complete the following information):**
- OS: [e.g. iOS]
- Browser [e.g. chrome, safari]
- Version [e.g. 22]
**Smartphone (please complete the following information):**
- Device: [e.g. iPhone6]
- OS: [e.g. iOS8.1]
- Browser [e.g. stock browser, safari]
- Version [e.g. 22]
**Additional context**
Add any other context about the problem here.

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: Bugs, Feedback, Feature Requests, and Questions
url: https://feedback.yaak.app
about: "Please report to Yaak's public feedback board. Issues will be created and linked here when applicable."

72
.github/workflows/artifacts.yml vendored Normal file
View File

@@ -0,0 +1,72 @@
name: Generate Artifacts
on:
push:
tags: [ v* ]
permissions: write-all
jobs:
build-artifacts:
name: Build
strategy:
fail-fast: false
matrix:
include:
- os: macos-latest
target: aarch64-apple-darwin
- os: macos-latest
target: x86_64-apple-darwin
- os: windows-latest
target: x86_64-pc-windows-msvc
- os: ubuntu-20.04
target: x86_64-unknown-linux-gnu
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.target }}
- name: Cache Rust
uses: actions/cache@v2
with:
path: |
~/.cargo/registry
~/.cargo/git
./src-tauri/target
key: ${{ runner.os }}-cargo-${{ hashFiles('src-tauri/Cargo.lock') }}
- uses: actions/setup-node@v3
with:
node-version: 20
cache: 'npm'
- name: install dependencies (ubuntu only)
if: matrix.os == 'ubuntu-20.04'
run: |
sudo apt-get update
sudo apt-get install -y libgtk-3-dev libwebkit2gtk-4.0-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install dependencies
run: npm ci
- name: Run tests
run: npm test
# Pin dev version to get non-default targets
# https://github.com/tauri-apps/tauri-action/issues/356
- uses: tauri-apps/tauri-action@dev
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
with:
tagName: 'v__VERSION__'
releaseName: 'Release __VERSION__'
releaseBody: 'https://yaak.app/changelog/__VERSION__'
releaseDraft: true
prerelease: false
args: '--target ${{ matrix.target }}'

View File

@@ -1,31 +0,0 @@
on:
pull_request:
push:
branches:
- main
name: Lint and Test
permissions:
contents: read
jobs:
test:
name: Lint/Test
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
workspaces: 'src-tauri'
shared-key: ci
cache-on-failure: true
- run: npm ci
- run: npm run lint
- name: Run JS Tests
run: npm test
- name: Run Rust Tests
run: cargo test --all
working-directory: src-tauri

View File

@@ -1,50 +0,0 @@
name: Claude Code
on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]
jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1
- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'
# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://code.claude.com/docs/en/cli-reference for available options
# claude_args: '--allowed-tools Bash(gh pr:*)'

View File

@@ -1,131 +0,0 @@
name: Generate Artifacts
on:
push:
tags: [ v* ]
jobs:
build-artifacts:
permissions:
contents: write
name: Build
strategy:
fail-fast: false
matrix:
include:
- platform: 'macos-latest' # for Arm-based Macs (M1 and above).
args: '--target aarch64-apple-darwin'
yaak_arch: 'arm64'
os: 'macos'
targets: 'aarch64-apple-darwin'
- platform: 'macos-latest' # for Intel-based Macs.
args: '--target x86_64-apple-darwin'
yaak_arch: 'x64'
os: 'macos'
targets: 'x86_64-apple-darwin'
- platform: 'ubuntu-22.04'
args: ''
yaak_arch: 'x64'
os: 'ubuntu'
targets: ''
- platform: 'ubuntu-22.04-arm'
args: ''
yaak_arch: 'arm64'
os: 'ubuntu'
targets: ''
- platform: 'windows-latest'
args: ''
yaak_arch: 'x64'
os: 'windows'
targets: ''
# Windows ARM64
- platform: 'windows-latest'
args: '--target aarch64-pc-windows-msvc'
yaak_arch: 'arm64'
os: 'windows'
targets: 'aarch64-pc-windows-msvc'
runs-on: ${{ matrix.platform }}
timeout-minutes: 40
steps:
- name: Checkout yaakapp/app
uses: actions/checkout@v4
- name: Setup Node
uses: actions/setup-node@v4
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.targets }}
- uses: Swatinem/rust-cache@v2
with:
workspaces: 'src-tauri'
shared-key: ci
cache-on-failure: true
- name: install dependencies (Linux only)
if: matrix.os == 'ubuntu'
run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf xdg-utils
- name: Install Protoc for plugin-runtime
uses: arduino/setup-protoc@v3
with:
repo-token: ${{ secrets.GITHUB_TOKEN }}
- name: Install trusted-signing-cli (Windows only)
if: matrix.os == 'windows'
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$dir = "$env:USERPROFILE\trusted-signing"
New-Item -ItemType Directory -Force -Path $dir | Out-Null
$url = "https://github.com/Levminer/trusted-signing-cli/releases/download/0.8.0/trusted-signing-cli.exe"
$exe = Join-Path $dir "trusted-signing-cli.exe"
Invoke-WebRequest -Uri $url -OutFile $exe
echo $dir >> $env:GITHUB_PATH
& $exe --version
- run: npm ci
- run: npm run lint
- name: Run JS Tests
run: npm test
- name: Run Rust Tests
run: cargo test --all
working-directory: src-tauri
- name: Set version
run: npm run replace-version
env:
YAAK_VERSION: ${{ github.ref_name }}
- uses: tauri-apps/tauri-action@v0
env:
YAAK_TARGET_ARCH: ${{ matrix.yaak_arch }}
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }}
TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }}
# Apple signing stuff
APPLE_CERTIFICATE: ${{ matrix.os == 'macos' && secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ matrix.os == 'macos' && secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_ID: ${{ matrix.os == 'macos' && secrets.APPLE_ID }}
APPLE_PASSWORD: ${{ matrix.os == 'macos' && secrets.APPLE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ matrix.os == 'macos' && secrets.APPLE_SIGNING_IDENTITY }}
APPLE_TEAM_ID: ${{ matrix.os == 'macos' && secrets.APPLE_TEAM_ID }}
# Windows signing stuff
AZURE_CLIENT_ID: ${{ matrix.os == 'windows' && secrets.AZURE_CLIENT_ID }}
AZURE_CLIENT_SECRET: ${{ matrix.os == 'windows' && secrets.AZURE_CLIENT_SECRET }}
AZURE_TENANT_ID: ${{ matrix.os == 'windows' && secrets.AZURE_TENANT_ID }}
with:
tagName: 'v__VERSION__'
releaseName: 'Release __VERSION__'
releaseBody: '[Changelog __VERSION__](https://yaak.app/blog/__VERSION__)'
releaseDraft: true
prerelease: true
args: '${{ matrix.args }} --config ./src-tauri/tauri.release.conf.json'

View File

@@ -1,44 +0,0 @@
name: Generate Sponsors README
on:
workflow_dispatch:
schedule:
- cron: 30 15 * * 0-6
permissions:
contents: write
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Checkout 🛎️
uses: actions/checkout@v2
- name: Generate Sponsors
uses: JamesIves/github-sponsors-readme-action@v1
with:
token: ${{ secrets.SPONSORS_PAT }}
file: 'README.md'
maximum: 1999
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="50px" alt="User avatar: {{{ login }}}" /></a>&nbsp;&nbsp;'
active-only: false
include-private: true
marker: 'sponsors-base'
- name: Generate Sponsors
uses: JamesIves/github-sponsors-readme-action@v1
with:
token: ${{ secrets.SPONSORS_PAT }}
file: 'README.md'
minimum: 2000
template: '<a href="https://github.com/{{{ login }}}"><img src="{{{ avatarUrl }}}" width="80px" alt="User avatar: {{{ login }}}" /></a>&nbsp;&nbsp;'
active-only: false
include-private: true
marker: 'sponsors-premium'
# ⚠️ Note: You can use any deployment step here to automatically push the README
# changes back to your branch.
- name: Commit Changes
uses: JamesIves/github-pages-deploy-action@v4
with:
branch: main
force: false
folder: '.'

10
.gitignore vendored
View File

@@ -15,8 +15,6 @@ dist-ssr
# Editor directories and files
.vscode/*
!.vscode/extensions.json
!.vscode/settings.json
!.vscode/launch.json
.idea
.DS_Store
*.suo
@@ -25,14 +23,6 @@ dist-ssr
*.sln
*.sw?
.eslintcache
out
*.sqlite
*.sqlite-*
.cargo
.tmp
tmp
.zed
codebook.toml

1
.husky/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
_

4
.husky/pre-commit Executable file
View File

@@ -0,0 +1,4 @@
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"
npx lint-staged

4
.prettierignore Normal file
View File

@@ -0,0 +1,4 @@
node_modules/
dist/
out/
.prettierrc.cjs

8
.prettierrc.cjs Normal file
View File

@@ -0,0 +1,8 @@
module.exports = {
"trailingComma": "all",
"tabWidth": 2,
"semi": true,
"singleQuote": true,
"printWidth": 100,
"bracketSpacing": true
}

View File

@@ -0,0 +1,20 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Build Desktop" type="ShConfigurationType">
<option name="SCRIPT_TEXT" value="npm run tauri build -- --target universal-apple-darwin" />
<option name="INDEPENDENT_SCRIPT_PATH" value="true" />
<option name="SCRIPT_PATH" value="" />
<option name="SCRIPT_OPTIONS" value="" />
<option name="INDEPENDENT_SCRIPT_WORKING_DIRECTORY" value="true" />
<option name="SCRIPT_WORKING_DIRECTORY" value="$PROJECT_DIR$" />
<option name="INDEPENDENT_INTERPRETER_PATH" value="true" />
<option name="INTERPRETER_PATH" value="/bin/zsh" />
<option name="INTERPRETER_OPTIONS" value="" />
<option name="EXECUTE_IN_TERMINAL" value="true" />
<option name="EXECUTE_SCRIPT_FILE" value="false" />
<envs>
<env name="TAURI_KEY_PASSWORD" value="fishhook-upstream-wash-assured" />
<env name="TAURI_PRIVATE_KEY" value="dW50cnVzdGVkIGNvbW1lbnQ6IHJzaWduIGVuY3J5cHRlZCBzZWNyZXQga2V5ClJXUlRZMEl5OGxWaytTa3dIa2xXVUltQzRGUXIzd2lYQ2NpV0ZhQURSbWJWZ1NrK0tnY0FBQkFBQUFBQUFBQUFBQUlBQUFBQUV2M1VKdVRyVHpHSzhQdGc2ZVFtOVNsMU5tNEVSN280cFNrbXhncW9tdjNXaFJZUTJqUzQ5Q01zWTJWRVhaY1pGNHNjR1NFR3JmcWFRN09NdWdGMXpZVXhzejR4V3lDV1JpZHlnbW5LNS9vMFFtRlZjbUl4YjZSNzhlMmk3ait5SExYcG5QZUkxOFE9Cg==" />
</envs>
<method v="2" />
</configuration>
</component>

13
.run/Dev Desktop.run.xml Normal file
View File

@@ -0,0 +1,13 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Dev Desktop" type="js.build_tools.npm">
<package-json value="$PROJECT_DIR$/package.json" />
<command value="run" />
<scripts>
<script value="start" />
</scripts>
<node-interpreter value="project" />
<envs>
</envs>
<method v="2" />
</configuration>
</component>

5
.sqllsrc.json Normal file
View File

@@ -0,0 +1,5 @@
{
"name": "yaak-dev",
"adapter": "sqlite3",
"filename": "src-tauri/db.sqlite"
}

1
.tauriignore Normal file
View File

@@ -0,0 +1 @@
plugins

View File

@@ -1,3 +0,0 @@
{
"recommendations": ["biomejs.biome", "rust-lang.rust-analyzer", "bradlc.vscode-tailwindcss"]
}

26
.vscode/launch.json vendored
View File

@@ -1,26 +0,0 @@
{
"version": "0.2.0",
"configurations": [
{
"type": "node",
"request": "launch",
"name": "Dev App",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "start"]
},
{
"type": "node",
"request": "launch",
"name": "Build App",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "start"]
},
{
"type": "node",
"request": "launch",
"name": "Bootstrap",
"runtimeExecutable": "npm",
"runtimeArgs": ["run", "bootstrap"]
}
]
}

View File

@@ -1,6 +0,0 @@
{
"editor.defaultFormatter": "biomejs.biome",
"editor.formatOnSave": true,
"biome.enabled": true,
"biome.lint.format.enable": true
}

View File

@@ -1,88 +0,0 @@
# Developer Setup
Yaak is a combined Node.js and Rust monorepo. It is a [Tauri](https://tauri.app) project, so
uses Rust and HTML/CSS/JS for the main application but there is also a plugin system powered
by a Node.js sidecar that communicates to the app over gRPC.
Because of the moving parts, there are a few setup steps required before development can
begin.
## Prerequisites
Make sure you have the following tools installed:
- [Node.js](https://nodejs.org/en/download/package-manager)
- [Rust](https://www.rust-lang.org/tools/install)
Check the installations with the following commands:
```shell
node -v
npm -v
rustc --version
```
Install the NPM dependencies:
```shell
npm install
```
Run the `bootstrap` command to do some initial setup:
```shell
npm run bootstrap
```
## Run the App
After bootstrapping, start the app in development mode:
```shell
npm start
```
## SQLite Migrations
New migrations can be created from the `src-tauri/` directory:
```shell
npm run migration
```
Rerun the app to apply the migrations.
_Note: For safety, development builds use a separate database location from production builds._
## Lezer Grammar Generation
```sh
# Example
lezer-generator components/core/Editor/<LANG>/<LANG>.grammar > components/core/Editor/<LANG>/<LANG>.ts
```
## Linting & Formatting
This repo uses Biome for linting and formatting (replacing ESLint + Prettier).
- Lint the entire repo:
```sh
npm run lint
```
- Auto-fix lint issues where possible:
```sh
npm run lint:fix
```
- Format code:
```sh
npm run format
```
Notes:
- Many workspace packages also expose the same scripts (`lint`, `lint:fix`, and `format`).
- TypeScript type-checking still runs separately via `tsc --noEmit` in relevant packages.

21
LICENSE
View File

@@ -1,21 +0,0 @@
MIT License
Copyright (c) 2024 Yaak
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

13
Makefile Normal file
View File

@@ -0,0 +1,13 @@
.PHONY: sqlx-prepare, dev, migrate, build
sqlx-prepare:
cd src-tauri && cargo sqlx prepare --database-url 'sqlite://db.sqlite'
dev:
npm run tauri-dev
migrate:
cd src-tauri && cargo sqlx migrate run --database-url 'sqlite://db.sqlite?mode=rw'
build:
./node_modules/.bin/tauri build

View File

@@ -1,70 +1,16 @@
<p align="center">
<a href="https://github.com/JamesIves/github-sponsors-readme-action">
<img width="200px" src="https://github.com/mountain-loop/yaak/raw/main/src-tauri/icons/icon.png">
</a>
</p>
# Yaak Network Toolkit
<h1 align="center">
💫 Yaak ➟ Desktop API Client 💫
</h1>
The most fun you'll ever have working with APIs.
<p align="center">
A fast, privacy-first API client for REST, GraphQL, SSE, WebSocket, and gRPC built with Tauri, Rust, and React.
</p>
<p align="center">
Development is funded by community-purchased <a href="https://yaak.app/pricing">licenses</a>. You can also <a href="https://github.com/sponsors/gschier">become a sponsor</a> to have your logo appear below. 💖
</p>
<br>
## Common Commands
```sh
# Start dev app
npm run tauri-dev
<p align="center">
<!-- sponsors-premium --><a href="https://github.com/MVST-Solutions"><img src="https:&#x2F;&#x2F;github.com&#x2F;MVST-Solutions.png" width="80px" alt="User avatar: MVST-Solutions" /></a>&nbsp;&nbsp;<a href="https://github.com/dharsanb"><img src="https:&#x2F;&#x2F;github.com&#x2F;dharsanb.png" width="80px" alt="User avatar: dharsanb" /></a>&nbsp;&nbsp;<a href="https://github.com/railwayapp"><img src="https:&#x2F;&#x2F;github.com&#x2F;railwayapp.png" width="80px" alt="User avatar: railwayapp" /></a>&nbsp;&nbsp;<a href="https://github.com/caseyamcl"><img src="https:&#x2F;&#x2F;github.com&#x2F;caseyamcl.png" width="80px" alt="User avatar: caseyamcl" /></a>&nbsp;&nbsp;<a href="https://github.com/bytebase"><img src="https:&#x2F;&#x2F;github.com&#x2F;bytebase.png" width="80px" alt="User avatar: bytebase" /></a>&nbsp;&nbsp;<a href="https://github.com/"><img src="https:&#x2F;&#x2F;raw.githubusercontent.com&#x2F;JamesIves&#x2F;github-sponsors-readme-action&#x2F;dev&#x2F;.github&#x2F;assets&#x2F;placeholder.png" width="80px" alt="User avatar: " /></a>&nbsp;&nbsp;<!-- sponsors-premium -->
</p>
<p align="center">
<!-- sponsors-base --><a href="https://github.com/seanwash"><img src="https:&#x2F;&#x2F;github.com&#x2F;seanwash.png" width="50px" alt="User avatar: seanwash" /></a>&nbsp;&nbsp;<a href="https://github.com/jerath"><img src="https:&#x2F;&#x2F;github.com&#x2F;jerath.png" width="50px" alt="User avatar: jerath" /></a>&nbsp;&nbsp;<a href="https://github.com/itsa-sh"><img src="https:&#x2F;&#x2F;github.com&#x2F;itsa-sh.png" width="50px" alt="User avatar: itsa-sh" /></a>&nbsp;&nbsp;<a href="https://github.com/dmmulroy"><img src="https:&#x2F;&#x2F;github.com&#x2F;dmmulroy.png" width="50px" alt="User avatar: dmmulroy" /></a>&nbsp;&nbsp;<a href="https://github.com/timcole"><img src="https:&#x2F;&#x2F;github.com&#x2F;timcole.png" width="50px" alt="User avatar: timcole" /></a>&nbsp;&nbsp;<a href="https://github.com/VLZH"><img src="https:&#x2F;&#x2F;github.com&#x2F;VLZH.png" width="50px" alt="User avatar: VLZH" /></a>&nbsp;&nbsp;<a href="https://github.com/terasaka2k"><img src="https:&#x2F;&#x2F;github.com&#x2F;terasaka2k.png" width="50px" alt="User avatar: terasaka2k" /></a>&nbsp;&nbsp;<a href="https://github.com/andriyor"><img src="https:&#x2F;&#x2F;github.com&#x2F;andriyor.png" width="50px" alt="User avatar: andriyor" /></a>&nbsp;&nbsp;<a href="https://github.com/majudhu"><img src="https:&#x2F;&#x2F;github.com&#x2F;majudhu.png" width="50px" alt="User avatar: majudhu" /></a>&nbsp;&nbsp;<a href="https://github.com/axelrindle"><img src="https:&#x2F;&#x2F;github.com&#x2F;axelrindle.png" width="50px" alt="User avatar: axelrindle" /></a>&nbsp;&nbsp;<a href="https://github.com/jirizverina"><img src="https:&#x2F;&#x2F;github.com&#x2F;jirizverina.png" width="50px" alt="User avatar: jirizverina" /></a>&nbsp;&nbsp;<a href="https://github.com/chip-well"><img src="https:&#x2F;&#x2F;github.com&#x2F;chip-well.png" width="50px" alt="User avatar: chip-well" /></a>&nbsp;&nbsp;<a href="https://github.com/GRAYAH"><img src="https:&#x2F;&#x2F;github.com&#x2F;GRAYAH.png" width="50px" alt="User avatar: GRAYAH" /></a>&nbsp;&nbsp;<!-- sponsors-base -->
</p>
![Yaak API Client](https://yaak.app/static/screenshot.png)
## Features
Yaak is an offline-first API client designed to stay out of your way while giving you everything you need when you need it.
Built with [Tauri](https://tauri.app), Rust, and React, its fast, lightweight, and private. No telemetry, no VC funding, and no cloud lock-in.
### 🌐 Work with any API
- Import collections from Postman, Insomnia, OpenAPI, Swagger, or Curl.
- Send requests via REST, GraphQL, gRPC, WebSocket, or Server-Sent Events.
- Filter and inspect responses with JSONPath or XPath.
### 🔐 Stay secure
- Use OAuth 2.0, JWT, Basic Auth, or custom plugins for authentication.
- Secure sensitive values with encrypted secrets.
- Store secrets in your OS keychain.
### ☁️ Organize & collaborate
- Group requests into workspaces and nested folders.
- Use environment variables to switch between dev, staging, and prod.
- Mirror workspaces to your filesystem for versioning in Git or syncing with Dropbox.
### 🧩 Extend & customize
- Insert dynamic values like UUIDs or timestamps with template tags.
- Pick from built-in themes or build your own.
- Create plugins to extend authentication, template tags, or the UI.
## Contribution Policy
Yaak is open source but only accepting contributions for bug fixes. To get started,
visit [`DEVELOPMENT.md`](DEVELOPMENT.md) for tips on setting up your environment.
## Useful Resources
- [Feedback and Bug Reports](https://feedback.yaak.app)
- [Documentation](https://feedback.yaak.app/help)
- [Yaak vs Postman](https://yaak.app/alternatives/postman)
- [Yaak vs Bruno](https://yaak.app/alternatives/bruno)
- [Yaak vs Insomnia](https://yaak.app/alternatives/insomnia)
# Migration commands
cd src-tauri
cargo sqlx migrate add ${MIGRATION_NAME}
cargo sqlx migrate run --database-url 'sqlite://db.sqlite?mode=rw'
cargo sqlx prepare --database-url 'sqlite://db.sqlite'
```

View File

@@ -1,51 +0,0 @@
{
"$schema": "https://biomejs.dev/schemas/2.3.7/schema.json",
"linter": {
"enabled": true,
"rules": {
"recommended": true,
"a11y": {
"useKeyWithClickEvents": "off"
}
}
},
"formatter": {
"enabled": true,
"indentStyle": "space",
"indentWidth": 2,
"lineWidth": 100,
"bracketSpacing": true
},
"css": {
"parser": {
"tailwindDirectives": true
},
"linter": {
"enabled": false
}
},
"javascript": {
"formatter": {
"quoteStyle": "single",
"jsxQuoteStyle": "double",
"trailingCommas": "all",
"semicolons": "always"
}
},
"files": {
"includes": [
"**",
"!**/node_modules",
"!**/dist",
"!**/build",
"!scripts",
"!packages/plugin-runtime",
"!packages/plugin-runtime-types",
"!src-tauri",
"!src-web/tailwind.config.cjs",
"!src-web/postcss.config.cjs",
"!src-web/vite.config.ts",
"!src-web/routeTree.gen.ts"
]
}
}

BIN
design/Icons.afdesign Normal file
View File

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 31 KiB

After

Width:  |  Height:  |  Size: 31 KiB

View File

Before

Width:  |  Height:  |  Size: 356 KiB

After

Width:  |  Height:  |  Size: 356 KiB

BIN
design/logo.afdesign Normal file
View File

Binary file not shown.

View File

@@ -1,34 +1,31 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Yaak App</title>
<!-- <script src="http://localhost:8097"></script>-->
<!-- <script src="http://localhost:8097"></script>-->
<!-- Certain elements like webview (and maybe <select>?) will use background
color depending on document background color-->
<style>
html, body {
background-color: white;
}
@media (prefers-color-scheme: dark) {
html, body {
background-color: #1b1a29;
background-color: white;
}
@media (prefers-color-scheme: dark) {
html, body {
background-color: black;
}
}
}
</style>
</head>
<body class="text-base">
<body>
<div id="root"></div>
<div id="cm-portal" class="cm-portal"></div>
<div id="react-portal"></div>
<div id="radix-portal" class="cm-portal"></div>
<script type="module" src="/theme.ts"></script>
<script type="module" src="/font-size.ts"></script>
<script type="module" src="/font.ts"></script>
<script type="module" src="/main.tsx"></script>
<script type="module" src="/src-web/main.tsx"></script>
</body>
</html>

18639
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -2,98 +2,106 @@
"name": "yaak-app",
"private": true,
"version": "0.0.0",
"repository": {
"type": "git",
"url": "git+https://github.com/mountain-loop/yaak.git"
},
"workspaces": [
"packages/common-lib",
"packages/plugin-runtime",
"packages/plugin-runtime-types",
"plugins/action-copy-curl",
"plugins/action-copy-grpcurl",
"plugins/auth-apikey",
"plugins/auth-aws",
"plugins/auth-basic",
"plugins/auth-bearer",
"plugins/auth-jwt",
"plugins/auth-ntlm",
"plugins/auth-oauth2",
"plugins/auth-oauth1",
"plugins/filter-jsonpath",
"plugins/filter-xpath",
"plugins/importer-curl",
"plugins/importer-insomnia",
"plugins/importer-openapi",
"plugins/importer-postman",
"plugins/importer-postman-environment",
"plugins/importer-yaak",
"plugins/template-function-1password",
"plugins/template-function-cookie",
"plugins/template-function-ctx",
"plugins/template-function-encode",
"plugins/template-function-fs",
"plugins/template-function-hash",
"plugins/template-function-json",
"plugins/template-function-prompt",
"plugins/template-function-random",
"plugins/template-function-regex",
"plugins/template-function-timestamp",
"plugins/template-function-uuid",
"plugins/template-function-xml",
"plugins/template-function-request",
"plugins/template-function-response",
"plugins/themes-yaak",
"src-tauri",
"src-tauri/yaak-crypto",
"src-tauri/yaak-fonts",
"src-tauri/yaak-git",
"src-tauri/yaak-license",
"src-tauri/yaak-mac-window",
"src-tauri/yaak-models",
"src-tauri/yaak-plugins",
"src-tauri/yaak-sse",
"src-tauri/yaak-sync",
"src-tauri/yaak-templates",
"src-tauri/yaak-ws",
"src-web"
],
"type": "module",
"scripts": {
"start": "npm run app-dev",
"app-build": "tauri build",
"app-dev": "tauri dev --no-watch --config ./src-tauri/tauri.development.conf.json",
"migration": "node scripts/create-migration.cjs",
"build": "npm run --workspaces --if-present build",
"build-plugins": "npm run --workspaces --if-present build",
"test": "npm run --workspaces --if-present test",
"icons": "run-p icons:*",
"icons:dev": "tauri icon src-tauri/icons/icon-dev.png --output src-tauri/icons/dev",
"icons:release": "tauri icon src-tauri/icons/icon.png --output src-tauri/icons/release",
"bootstrap": "run-s bootstrap:*",
"bootstrap:install-wasm-pack": "node scripts/install-wasm-pack.cjs",
"bootstrap:build": "npm run build",
"bootstrap:vendor": "npm run vendor",
"vendor": "run-p vendor:*",
"vendor:vendor-plugins": "node scripts/vendor-plugins.cjs",
"vendor:vendor-protoc": "node scripts/vendor-protoc.cjs",
"vendor:vendor-node": "node scripts/vendor-node.cjs",
"lint": "run-p lint:*",
"lint:biome": "biome lint",
"lint:extra": "npm run --workspaces --if-present lint",
"format": "biome format --write .",
"replace-version": "node scripts/replace-version.cjs",
"start": "npm run build:plugins && npm run tauri-dev",
"tauri-dev": "tauri dev --no-watch --config ./src-tauri/tauri-dev.conf.json",
"tauri-build": "tauri build",
"tauri": "tauri",
"tauri-before-build": "npm run bootstrap",
"tauri-before-dev": "workspaces-run --parallel -- npm run --workspaces --if-present dev"
"build": "npm run build:frontend",
"dev": "vite dev",
"lint": "tsc && eslint . --ext .ts,.tsx",
"build:icon:release": "tauri icon design/icon.png --output ./src-tauri/icons/release",
"build:icon:dev": "tauri icon design/icon-dev.png --output ./src-tauri/icons/dev",
"build:frontend": "vite build",
"build:plugins": "run-p build:plugin:*",
"build:plugin:importer-insomnia": "cd plugins/importer-insomnia && vite build --emptyOutDir",
"build:plugin:importer-postman": "cd plugins/importer-postman && vite build --emptyOutDir",
"build:plugin:importer-yaak": "cd plugins/importer-yaak && vite build --emptyOutDir",
"build:plugin:filter-jsonpath": "cd plugins/filter-jsonpath && vite build --emptyOutDir",
"build:plugin:filter-xpath": "cd plugins/filter-xpath && vite build --emptyOutDir",
"test": "vitest",
"coverage": "vitest run --coverage",
"prepare": "husky install"
},
"dependencies": {
"@codemirror/commands": "^6.2.1",
"@codemirror/lang-javascript": "^6.1.4",
"@codemirror/lang-json": "^6.0.1",
"@codemirror/lang-xml": "^6.0.2",
"@codemirror/language": "^6.6.0",
"@codemirror/search": "^6.2.3",
"@lezer/generator": "^1.2.2",
"@lezer/highlight": "^1.1.3",
"@lezer/lr": "^1.3.3",
"@react-hook/resize-observer": "^1.2.6",
"@tailwindcss/container-queries": "^0.1.0",
"@tanstack/query-sync-storage-persister": "^4.27.1",
"@tanstack/react-query": "^4.28.0",
"@tanstack/react-query-devtools": "^4.28.0",
"@tanstack/react-query-persist-client": "^4.28.0",
"@tauri-apps/api": "^1.5.3",
"buffer": "^6.0.3",
"classnames": "^2.3.2",
"cm6-graphql": "^0.0.9",
"codemirror": "^6.0.1",
"codemirror-json-schema": "^0.6.1",
"date-fns": "^3.3.1",
"focus-trap-react": "^10.1.1",
"format-graphql": "^1.4.0",
"framer-motion": "^9.0.4",
"lucide-react": "^0.309.0",
"mime": "^4.0.1",
"papaparse": "^5.4.1",
"parse-color": "^1.0.0",
"react": "^18.2.0",
"react-dnd": "^16.0.1",
"react-dnd-html5-backend": "^16.0.1",
"react-dom": "^18.2.0",
"react-helmet-async": "^1.3.0",
"react-router-dom": "^6.8.1",
"react-use": "^17.4.0",
"slugify": "^1.6.6",
"tauri-plugin-log-api": "github:tauri-apps/tauri-plugin-log#v1",
"uuid": "^9.0.0",
"xml-formatter": "^3.6.2"
},
"devDependencies": {
"@biomejs/biome": "^2.3.7",
"@tauri-apps/cli": "^2.9.6",
"@yaakapp/cli": "^0.3.4",
"nodejs-file-downloader": "^4.13.0",
"@tailwindcss/nesting": "^0.0.0-insiders.565cd3e",
"@tauri-apps/cli": "^1.5.10",
"@types/node": "^18.7.10",
"@types/papaparse": "^5.3.7",
"@types/parse-color": "^1.0.1",
"@types/parse-json": "^4.0.0",
"@types/react": "^18.0.31",
"@types/react-dom": "^18.0.11",
"@types/uuid": "^9.0.1",
"@typescript-eslint/eslint-plugin": "^7.0.2",
"@typescript-eslint/parser": "^7.0.2",
"@vitejs/plugin-react": "^4.2.1",
"autoprefixer": "^10.4.13",
"eslint": "^8.34.0",
"eslint-config-prettier": "^8.6.0",
"eslint-plugin-import": "^2.27.5",
"eslint-plugin-jsx-a11y": "^6.7.1",
"eslint-plugin-react": "^7.32.2",
"eslint-plugin-react-hooks": "^4.6.0",
"husky": "^8.0.3",
"lint-staged": "^15.0.2",
"npm-run-all": "^4.1.5",
"typescript": "^5.8.3",
"vitest": "^3.2.4",
"workspaces-run": "^1.0.2"
"postcss": "^8.4.21",
"postcss-nesting": "^11.2.1",
"prettier": "^2.8.4",
"react-devtools": "^4.27.2",
"tailwindcss": "^3.2.7",
"typescript": "^5.3.3",
"vite": "^5.1.1",
"vite-plugin-svgr": "^4.2.0",
"vite-plugin-top-level-await": "^1.4.1",
"vitest": "^1.3.0"
},
"lint-staged": {
"*.{ts,tsx}": "eslint --cache --fix",
"*.{js,css,md}": "prettier --write"
}
}

View File

@@ -1,20 +0,0 @@
export function formatSize(bytes: number): string {
let num: number;
let unit: string;
if (bytes > 1000 * 1000 * 1000) {
num = bytes / 1000 / 1000 / 1000;
unit = 'GB';
} else if (bytes > 1000 * 1000) {
num = bytes / 1000 / 1000;
unit = 'MB';
} else if (bytes > 1000) {
num = bytes / 1000;
unit = 'KB';
} else {
num = bytes;
unit = 'B';
}
return `${Math.round(num * 10) / 10} ${unit}`;
}

View File

@@ -1,3 +0,0 @@
export * from './debounce';
export * from './formatSize';
export * from './templateFunction';

View File

@@ -1,6 +0,0 @@
{
"name": "@yaakapp-internal/lib",
"private": true,
"version": "1.0.0",
"main": "index.ts"
}

View File

@@ -1,49 +0,0 @@
import type {
CallTemplateFunctionArgs,
JsonPrimitive,
TemplateFunctionArg,
} from '@yaakapp-internal/plugins';
export function validateTemplateFunctionArgs(
fnName: string,
args: TemplateFunctionArg[],
values: CallTemplateFunctionArgs['values'],
): string | null {
for (const arg of args) {
if ('inputs' in arg && arg.inputs) {
// Recurse down
const err = validateTemplateFunctionArgs(fnName, arg.inputs, values);
if (err) return err;
}
if (!('name' in arg)) continue;
if (arg.optional) continue;
if (arg.defaultValue != null) continue;
if (arg.hidden) continue;
if (values[arg.name] != null) continue;
return `Missing required argument "${arg.label || arg.name}" for template function ${fnName}()`;
}
return null;
}
/** Recursively apply form input defaults to a set of values */
export function applyFormInputDefaults(
inputs: TemplateFunctionArg[],
values: { [p: string]: JsonPrimitive | undefined },
) {
let newValues: { [p: string]: JsonPrimitive | undefined } = { ...values };
for (const input of inputs) {
if ('defaultValue' in input && values[input.name] === undefined) {
newValues[input.name] = input.defaultValue;
}
if (input.type === 'checkbox' && values[input.name] === undefined) {
newValues[input.name] = false;
}
// Recurse down to all child inputs
if ('inputs' in input) {
newValues = applyFormInputDefaults(input.inputs ?? [], newValues);
}
}
return newValues;
}

View File

@@ -1,2 +0,0 @@
lib
node_modules

View File

@@ -1,28 +0,0 @@
# Yaak Plugin API
Yaak is a desktop [API client](https://yaak.app/blog/yet-another-api-client) for
interacting with REST, GraphQL, Server Sent Events (SSE), WebSocket, and gRPC APIs. It's
built using Tauri, Rust, and ReactJS.
Plugins can be created in TypeScript, which are executed alongside Yaak in a NodeJS
runtime. This package contains the TypeScript type definitions required to make building
Yaak plugins a breeze.
## Quick Start
The easiest way to get started is by generating a plugin with the Yaak CLI:
```shell
npx @yaakapp/cli generate
```
For more details on creating plugins, check out
the [Quick Start Guide](https://feedback.yaak.app/help/articles/6911763-plugins-quick-start)
## Installation
If you prefer starting from scratch, manually install the types package:
```shell
npm install -D @yaakapp/api
```

View File

@@ -1,47 +0,0 @@
{
"name": "@yaakapp/api",
"version": "0.1.17",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@yaakapp/api",
"version": "0.1.17",
"dependencies": {
"@types/node": "^22.5.4"
},
"devDependencies": {
"typescript": "^5.6.2"
}
},
"node_modules/@types/node": {
"version": "22.5.4",
"resolved": "https://registry.npmjs.org/@types/node/-/node-22.5.4.tgz",
"integrity": "sha512-FDuKUJQm/ju9fT/SeX/6+gBzoPzlVCzfzmGkwKvRHQVxi4BntVbyIwf6a4Xn62mrvndLiml6z/UBXIdEVjQLXg==",
"license": "MIT",
"dependencies": {
"undici-types": "~6.19.2"
}
},
"node_modules/typescript": {
"version": "5.6.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz",
"integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "6.19.8",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.19.8.tgz",
"integrity": "sha512-ve2KP6f/JnbPBFyobGHuerC9g1FYGn/F8n1LWTwNxCEzd6IfqTwUQcNXgEtmmQ6DlRrC1hrSrBnCZPokRrDHjw==",
"license": "MIT"
}
}
}

View File

@@ -1,39 +0,0 @@
{
"name": "@yaakapp/api",
"version": "0.7.1",
"keywords": [
"api-client",
"insomnia-alternative",
"bruno-alternative",
"postman-alternative"
],
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak"
},
"bugs": {
"url": "https://feedback.yaak.app"
},
"homepage": "https://yaak.app",
"main": "lib/index.js",
"typings": "./lib/index.d.ts",
"files": [
"lib/**/*"
],
"scripts": {
"bootstrap": "npm run build",
"build": "run-s build:copy-types build:tsc",
"build:tsc": "tsc",
"build:copy-types": "run-p build:copy-types:*",
"build:copy-types:root": "cpy --flat ../../src-tauri/yaak-plugins/bindings/*.ts ./src/bindings",
"build:copy-types:next": "cpy --flat ../../src-tauri/yaak-plugins/bindings/serde_json/*.ts ./src/bindings/serde_json",
"publish": "npm publish",
"prepublishOnly": "npm run build"
},
"dependencies": {
"@types/node": "^24.0.13"
},
"devDependencies": {
"cpy-cli": "^5.0.0"
}
}

View File

@@ -1,8 +0,0 @@
// 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";
export type PluginNameVersion = { name: string, version: string, };
export type PluginSearchResponse = { plugins: Array<PluginVersion>, };
export type PluginUpdatesResponse = { plugins: Array<PluginNameVersion>, };

View File

File diff suppressed because one or more lines are too long

View File

@@ -1,25 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type Environment = { model: "environment", id: string, workspaceId: string, createdAt: string, updatedAt: string, name: string, public: boolean, parentModel: string, parentId: string | null, variables: Array<EnvironmentVariable>, color: string | null, sortPriority: number, };
export type EnvironmentVariable = { enabled?: boolean, name: string, value: string, id?: string, };
export type Folder = { model: "folder", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, description: string, headers: Array<HttpRequestHeader>, name: string, sortPriority: number, };
export type GrpcRequest = { model: "grpc_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authenticationType: string | null, authentication: Record<string, any>, description: string, message: string, metadata: Array<HttpRequestHeader>, method: string | null, name: string, service: string | null, sortPriority: number, url: string, };
export type HttpRequest = { model: "http_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, body: Record<string, any>, bodyType: string | null, description: string, headers: Array<HttpRequestHeader>, method: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
export type HttpRequestHeader = { enabled?: boolean, name: string, value: string, id?: string, };
export type HttpResponse = { model: "http_response", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, bodyPath: string | null, contentLength: number | null, contentLengthCompressed: number | null, elapsed: number, elapsedHeaders: number, error: string | null, headers: Array<HttpResponseHeader>, remoteAddr: string | null, requestContentLength: number | null, requestHeaders: Array<HttpResponseHeader>, status: number, statusReason: string | null, state: HttpResponseState, url: string, version: string | null, };
export type HttpResponseHeader = { name: string, value: string, };
export type HttpResponseState = "initialized" | "connected" | "closed";
export type HttpUrlParameter = { enabled?: boolean, name: string, value: string, id?: string, };
export type WebsocketRequest = { model: "websocket_request", id: string, createdAt: string, updatedAt: string, workspaceId: string, folderId: string | null, authentication: Record<string, any>, authenticationType: string | null, description: string, headers: Array<HttpRequestHeader>, message: string, name: string, sortPriority: number, url: string, urlParameters: Array<HttpUrlParameter>, };
export type Workspace = { model: "workspace", id: string, createdAt: string, updatedAt: string, authentication: Record<string, any>, authenticationType: string | null, description: string, headers: Array<HttpRequestHeader>, name: string, encryptionKeyChallenge: string | null, settingValidateCertificates: boolean, settingFollowRedirects: boolean, settingRequestTimeout: number, };

View File

@@ -1,5 +0,0 @@
// 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, };

View File

@@ -1,3 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type JsonValue = number | string | boolean | Array<JsonValue> | { [key in string]?: JsonValue } | null;

View File

@@ -1,2 +0,0 @@
export type AtLeast<T, K extends keyof T> = Partial<T> & Pick<T, K>;
export type MaybePromise<T> = Promise<T> | T;

View File

@@ -1,9 +0,0 @@
export type * from './plugins';
export type * from './themes';
export * from './bindings/gen_models';
export * from './bindings/gen_events';
// Some extras for utility
export type { PartialImportResources } from './plugins/ImporterPlugin';

View File

@@ -1,44 +0,0 @@
import {
CallHttpAuthenticationActionArgs,
CallHttpAuthenticationRequest,
CallHttpAuthenticationResponse,
FormInput,
GetHttpAuthenticationSummaryResponse,
HttpAuthenticationAction,
} from '../bindings/gen_events';
import { MaybePromise } from '../helpers';
import { Context } from './Context';
type AddDynamicMethod<T> = {
dynamic?: (
ctx: Context,
args: CallHttpAuthenticationActionArgs,
) => MaybePromise<Partial<T> | null | undefined>;
};
type AddDynamic<T> = T extends any
? T extends { inputs?: FormInput[] }
? Omit<T, 'inputs'> & {
inputs: Array<AddDynamic<FormInput>>;
dynamic?: (
ctx: Context,
args: CallHttpAuthenticationActionArgs,
) => MaybePromise<
Partial<Omit<T, 'inputs'> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
>;
}
: T & AddDynamicMethod<T>
: never;
export type DynamicAuthenticationArg = AddDynamic<FormInput>;
export type AuthenticationPlugin = GetHttpAuthenticationSummaryResponse & {
args: DynamicAuthenticationArg[];
onApply(
ctx: Context,
args: CallHttpAuthenticationRequest,
): MaybePromise<CallHttpAuthenticationResponse>;
actions?: (HttpAuthenticationAction & {
onSelect(ctx: Context, args: CallHttpAuthenticationActionArgs): Promise<void> | void;
})[];
};

View File

@@ -1,78 +0,0 @@
import type {
FindHttpResponsesRequest,
FindHttpResponsesResponse,
GetCookieValueRequest,
GetCookieValueResponse,
GetHttpRequestByIdRequest,
GetHttpRequestByIdResponse,
ListCookieNamesResponse,
ListFoldersRequest,
ListFoldersResponse,
ListHttpRequestsRequest,
ListHttpRequestsResponse,
OpenWindowRequest,
PromptTextRequest,
PromptTextResponse,
RenderGrpcRequestRequest,
RenderGrpcRequestResponse,
RenderHttpRequestRequest,
RenderHttpRequestResponse,
SendHttpRequestRequest,
SendHttpRequestResponse,
ShowToastRequest,
TemplateRenderRequest,
} from '../bindings/gen_events.ts';
import type { JsonValue } from '../bindings/serde_json/JsonValue';
export interface Context {
clipboard: {
copyText(text: string): Promise<void>;
};
toast: {
show(args: ShowToastRequest): Promise<void>;
};
prompt: {
text(args: PromptTextRequest): Promise<PromptTextResponse['value']>;
};
store: {
set<T>(key: string, value: T): Promise<void>;
get<T>(key: string): Promise<T | undefined>;
delete(key: string): Promise<boolean>;
};
window: {
requestId(): Promise<string | null>;
workspaceId(): Promise<string | null>;
environmentId(): Promise<string | null>;
openUrl(
args: OpenWindowRequest & {
onNavigate?: (args: { url: string }) => void;
onClose?: () => void;
},
): Promise<{ close: () => void }>;
};
cookies: {
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']>;
render(args: RenderHttpRequestRequest): Promise<RenderHttpRequestResponse['httpRequest']>;
list(args?: ListHttpRequestsRequest): Promise<ListHttpRequestsResponse['httpRequests']>;
};
folder: {
list(args?: ListFoldersRequest): Promise<ListFoldersResponse['folders']>;
};
httpResponse: {
find(args: FindHttpResponsesRequest): Promise<FindHttpResponsesResponse['httpResponses']>;
};
templates: {
render<T extends JsonValue>(args: TemplateRenderRequest & { data: T }): Promise<T>;
};
plugin: {
reload(): void;
};
}

View File

@@ -1,11 +0,0 @@
import { FilterResponse } from '../bindings/gen_events';
import type { Context } from './Context';
export type FilterPlugin = {
name: string;
description?: string;
onFilter(
ctx: Context,
args: { payload: string; filter: string; mimeType: string },
): Promise<FilterResponse> | FilterResponse;
};

View File

@@ -1,6 +0,0 @@
import type { CallFolderActionArgs, FolderAction } from '../bindings/gen_events';
import type { Context } from './Context';
export type FolderActionPlugin = FolderAction & {
onSelect(ctx: Context, args: CallFolderActionArgs): Promise<void> | void;
};

View File

@@ -1,6 +0,0 @@
import { CallGrpcRequestActionArgs, GrpcRequestAction } from '../bindings/gen_events';
import type { Context } from './Context';
export type GrpcRequestActionPlugin = GrpcRequestAction & {
onSelect(ctx: Context, args: CallGrpcRequestActionArgs): Promise<void> | void;
};

View File

@@ -1,6 +0,0 @@
import type { CallHttpRequestActionArgs, HttpRequestAction } from '../bindings/gen_events';
import type { Context } from './Context';
export type HttpRequestActionPlugin = HttpRequestAction & {
onSelect(ctx: Context, args: CallHttpRequestActionArgs): Promise<void> | void;
};

View File

@@ -1,28 +0,0 @@
import { ImportResources } from '../bindings/gen_events';
import { AtLeast, MaybePromise } from '../helpers';
import type { Context } from './Context';
type RootFields = 'name' | 'id' | 'model';
type CommonFields = RootFields | 'workspaceId';
export type PartialImportResources = {
workspaces: Array<AtLeast<ImportResources['workspaces'][0], RootFields>>;
environments: Array<AtLeast<ImportResources['environments'][0], CommonFields>>;
folders: Array<AtLeast<ImportResources['folders'][0], CommonFields>>;
httpRequests: Array<AtLeast<ImportResources['httpRequests'][0], CommonFields>>;
grpcRequests: Array<AtLeast<ImportResources['grpcRequests'][0], CommonFields>>;
websocketRequests: Array<AtLeast<ImportResources['websocketRequests'][0], CommonFields>>;
};
export type ImportPluginResponse = null | {
resources: PartialImportResources;
};
export type ImporterPlugin = {
name: string;
description?: string;
onImport(
ctx: Context,
args: { text: string },
): MaybePromise<ImportPluginResponse | null | undefined>;
};

View File

@@ -1,31 +0,0 @@
import { CallTemplateFunctionArgs, FormInput, TemplateFunction } from '../bindings/gen_events';
import { MaybePromise } from '../helpers';
import { Context } from './Context';
type AddDynamicMethod<T> = {
dynamic?: (
ctx: Context,
args: CallTemplateFunctionArgs,
) => MaybePromise<Partial<T> | null | undefined>;
};
type AddDynamic<T> = T extends any
? T extends { inputs?: FormInput[] }
? Omit<T, 'inputs'> & {
inputs: Array<AddDynamic<FormInput>>;
dynamic?: (
ctx: Context,
args: CallTemplateFunctionArgs,
) => MaybePromise<
Partial<Omit<T, 'inputs'> & { inputs: Array<AddDynamic<FormInput>> }> | null | undefined
>;
}
: T & AddDynamicMethod<T>
: never;
export type DynamicTemplateFunctionArg = AddDynamic<FormInput>;
export type TemplateFunctionPlugin = Omit<TemplateFunction, 'args'> & {
args: DynamicTemplateFunctionArg[];
onRender(ctx: Context, args: CallTemplateFunctionArgs): Promise<string | null>;
};

View File

@@ -1,3 +0,0 @@
import { Theme } from '../bindings/gen_events';
export type ThemePlugin = Theme;

View File

@@ -1,6 +0,0 @@
import type { CallWebsocketRequestActionArgs, WebsocketRequestAction } from '../bindings/gen_events';
import type { Context } from './Context';
export type WebsocketRequestActionPlugin = WebsocketRequestAction & {
onSelect(ctx: Context, args: CallWebsocketRequestActionArgs): Promise<void> | void;
};

View File

@@ -1,6 +0,0 @@
import type { CallWorkspaceActionArgs, WorkspaceAction } from '../bindings/gen_events';
import type { Context } from './Context';
export type WorkspaceActionPlugin = WorkspaceAction & {
onSelect(ctx: Context, args: CallWorkspaceActionArgs): Promise<void> | void;
};

View File

@@ -1,37 +0,0 @@
import { AuthenticationPlugin } from './AuthenticationPlugin';
import type { Context } from './Context';
import type { FilterPlugin } from './FilterPlugin';
import { GrpcRequestActionPlugin } from './GrpcRequestActionPlugin';
import type { HttpRequestActionPlugin } from './HttpRequestActionPlugin';
import type { WebsocketRequestActionPlugin } from './WebSocketRequestActionPlugin';
import type { WorkspaceActionPlugin } from './WorkspaceActionPlugin';
import type { FolderActionPlugin } from './FolderActionPlugin';
import type { ImporterPlugin } from './ImporterPlugin';
import type { TemplateFunctionPlugin } from './TemplateFunctionPlugin';
import type { ThemePlugin } from './ThemePlugin';
export type { Context };
export type { DynamicTemplateFunctionArg } from './TemplateFunctionPlugin';
export type { DynamicAuthenticationArg } from './AuthenticationPlugin';
export type { TemplateFunctionPlugin };
export type { WorkspaceActionPlugin } from './WorkspaceActionPlugin';
export type { FolderActionPlugin } from './FolderActionPlugin';
/**
* The global structure of a Yaak plugin
*/
export type PluginDefinition = {
init?: (ctx: Context) => void | Promise<void>;
dispose?: () => void | Promise<void>;
importer?: ImporterPlugin;
themes?: ThemePlugin[];
filter?: FilterPlugin;
authentication?: AuthenticationPlugin;
httpRequestActions?: HttpRequestActionPlugin[];
websocketRequestActions?: WebsocketRequestActionPlugin[];
workspaceActions?: WorkspaceActionPlugin[];
folderActions?: FolderActionPlugin[];
grpcRequestActions?: GrpcRequestActionPlugin[];
templateFunctions?: TemplateFunctionPlugin[];
};

View File

@@ -1,44 +0,0 @@
export type Colors = {
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 Index = Colors & {
id: string;
name: string;
components?: Partial<{
dialog: Partial<Colors>;
menu: Partial<Colors>;
toast: Partial<Colors>;
sidebar: Partial<Colors>;
responsePane: Partial<Colors>;
appHeader: Partial<Colors>;
button: Partial<Colors>;
banner: Partial<Colors>;
placeholder: Partial<Colors>;
urlBar: Partial<Colors>;
editor: Partial<Colors>;
input: Partial<Colors>;
}>;
};

View File

@@ -1,20 +0,0 @@
{
"compilerOptions": {
"module": "node16",
"target": "es6",
"lib": [
"es2021",
"dom"
],
"declaration": true,
"declarationDir": "./lib",
"outDir": "./lib",
"strict": true,
"types": [
"node"
]
},
"files": [
"src/index.ts"
]
}

View File

@@ -1,10 +0,0 @@
{
"name": "@yaakapp-internal/plugin-runtime",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "@yaakapp-internal/plugin-runtime"
}
}
}

View File

@@ -1,14 +0,0 @@
{
"name": "@yaakapp-internal/plugin-runtime",
"scripts": {
"bootstrap": "npm run build",
"build": "run-p build:*",
"build:main": "esbuild src/index.ts --bundle --platform=node --outfile=../../src-tauri/vendored/plugin-runtime/index.cjs"
},
"dependencies": {
"ws": "^8.18.0"
},
"devDependencies": {
"@types/ws": "^8.5.13"
}
}

View File

@@ -1,19 +0,0 @@
import type { InternalEvent } from '@yaakapp/api';
export class EventChannel {
#listeners = new Set<(event: InternalEvent) => void>();
emit(e: InternalEvent) {
for (const l of this.#listeners) {
l(e);
}
}
listen(cb: (e: InternalEvent) => void) {
this.#listeners.add(cb);
}
unlisten(cb: (e: InternalEvent) => void) {
this.#listeners.delete(cb);
}
}

View File

@@ -1,26 +0,0 @@
import { PluginContext } from '@yaakapp-internal/plugins';
import type { BootRequest, InternalEvent } from '@yaakapp/api';
import type { EventChannel } from './EventChannel';
import { PluginInstance, PluginWorkerData } from './PluginInstance';
export class PluginHandle {
#instance: PluginInstance;
constructor(
pluginRefId: string,
context: PluginContext,
bootRequest: BootRequest,
pluginToAppEvents: EventChannel,
) {
const workerData: PluginWorkerData = { pluginRefId, context, bootRequest };
this.#instance = new PluginInstance(workerData, pluginToAppEvents);
}
sendToWorker(event: InternalEvent) {
this.#instance.postMessage(event);
}
async terminate() {
await this.#instance.terminate();
}
}

View File

@@ -1,800 +0,0 @@
import { applyFormInputDefaults, validateTemplateFunctionArgs } from '@yaakapp-internal/lib/templateFunction';
import {
BootRequest,
DeleteKeyValueResponse,
FindHttpResponsesResponse,
GetCookieValueRequest,
GetCookieValueResponse,
GetHttpRequestByIdResponse,
GetKeyValueResponse,
GrpcRequestAction,
HttpAuthenticationAction,
HttpRequestAction,
InternalEvent,
InternalEventPayload,
ListCookieNamesResponse,
PluginContext,
PromptTextResponse,
RenderGrpcRequestResponse,
RenderHttpRequestResponse,
SendHttpRequestResponse,
TemplateFunction,
TemplateRenderResponse,
WindowInfoResponse,
} from '@yaakapp-internal/plugins';
import { Context, PluginDefinition } from '@yaakapp/api';
import console from 'node:console';
import { type Stats, statSync, watch } from 'node:fs';
import path from 'node:path';
import { applyDynamicFormInput } from './common';
import { EventChannel } from './EventChannel';
import { migrateTemplateFunctionSelectOptions } from './migrations';
export interface PluginWorkerData {
bootRequest: BootRequest;
pluginRefId: string;
context: PluginContext;
}
export class PluginInstance {
#workerData: PluginWorkerData;
#mod: PluginDefinition;
#pluginToAppEvents: EventChannel;
#appToPluginEvents: EventChannel;
constructor(workerData: PluginWorkerData, pluginEvents: EventChannel) {
this.#workerData = workerData;
this.#pluginToAppEvents = pluginEvents;
this.#appToPluginEvents = new EventChannel();
// Forward incoming events to onMessage()
this.#appToPluginEvents.listen(async (event) => {
await this.#onMessage(event);
});
this.#mod = {} as any;
const fileChangeCallback = async () => {
await this.#mod?.dispose?.();
this.#importModule();
await this.#mod?.init?.(this.#newCtx(workerData.context));
return this.#sendPayload(
workerData.context,
{
type: 'reload_response',
silent: false,
},
null,
);
};
if (this.#workerData.bootRequest.watch) {
watchFile(this.#pathMod(), fileChangeCallback);
watchFile(this.#pathPkg(), fileChangeCallback);
}
this.#importModule();
}
postMessage(event: InternalEvent) {
this.#appToPluginEvents.emit(event);
}
async terminate() {
await this.#mod?.dispose?.();
this.#unimportModule();
}
async #onMessage(event: InternalEvent) {
const ctx = this.#newCtx(event.context);
const { context, payload, id: replyId } = event;
try {
if (payload.type === 'boot_request') {
await this.#mod?.init?.(ctx);
this.#sendPayload(context, { type: 'boot_response' }, replyId);
return;
}
if (payload.type === 'terminate_request') {
const payload: InternalEventPayload = {
type: 'terminate_response',
};
await this.terminate();
this.#sendPayload(context, payload, replyId);
return;
}
if (
payload.type === 'import_request' &&
typeof this.#mod?.importer?.onImport === 'function'
) {
const reply = await this.#mod.importer.onImport(ctx, {
text: payload.content,
});
if (reply != null) {
const replyPayload: InternalEventPayload = {
type: 'import_response',
// deno-lint-ignore no-explicit-any
resources: reply.resources as any,
};
this.#sendPayload(context, replyPayload, replyId);
return;
} else {
// Send back an empty reply (below)
}
}
if (payload.type === 'filter_request' && typeof this.#mod?.filter?.onFilter === 'function') {
const reply = await this.#mod.filter.onFilter(ctx, {
filter: payload.filter,
payload: payload.content,
mimeType: payload.type,
});
this.#sendPayload(context, { type: 'filter_response', ...reply }, replyId);
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(context, replyPayload, replyId);
return;
}
if (
payload.type === 'get_http_request_actions_request' &&
Array.isArray(this.#mod?.httpRequestActions)
) {
const reply: HttpRequestAction[] = this.#mod.httpRequestActions.map((a) => ({
...a,
// Add everything except onSelect
onSelect: undefined,
}));
const replyPayload: InternalEventPayload = {
type: 'get_http_request_actions_response',
pluginRefId: this.#workerData.pluginRefId,
actions: reply,
};
this.#sendPayload(context, replyPayload, replyId);
return;
}
if (
payload.type === 'get_websocket_request_actions_request' &&
Array.isArray(this.#mod?.websocketRequestActions)
) {
const reply = this.#mod.websocketRequestActions.map((a) => ({
...a,
onSelect: undefined,
}));
const replyPayload: InternalEventPayload = {
type: 'get_websocket_request_actions_response',
pluginRefId: this.#workerData.pluginRefId,
actions: reply,
};
this.#sendPayload(context, replyPayload, replyId);
return;
}
if (
payload.type === 'get_workspace_actions_request' &&
Array.isArray(this.#mod?.workspaceActions)
) {
const reply = this.#mod.workspaceActions.map((a) => ({
...a,
onSelect: undefined,
}));
const replyPayload: InternalEventPayload = {
type: 'get_workspace_actions_response',
pluginRefId: this.#workerData.pluginRefId,
actions: reply,
};
this.#sendPayload(context, replyPayload, replyId);
return;
}
if (
payload.type === 'get_folder_actions_request' &&
Array.isArray(this.#mod?.folderActions)
) {
const reply = this.#mod.folderActions.map((a) => ({
...a,
onSelect: undefined,
}));
const replyPayload: InternalEventPayload = {
type: 'get_folder_actions_response',
pluginRefId: this.#workerData.pluginRefId,
actions: reply,
};
this.#sendPayload(context, replyPayload, replyId);
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(context, replyPayload, replyId);
return;
}
if (
payload.type === 'get_template_function_summary_request' &&
Array.isArray(this.#mod?.templateFunctions)
) {
const functions: TemplateFunction[] = this.#mod.templateFunctions.map(
(templateFunction) => {
return {
...migrateTemplateFunctionSelectOptions(templateFunction),
// Add everything except render
onRender: undefined,
};
},
);
const replyPayload: InternalEventPayload = {
type: 'get_template_function_summary_response',
pluginRefId: this.#workerData.pluginRefId,
functions,
};
this.#sendPayload(context, replyPayload, replyId);
return;
}
if (
payload.type === 'get_template_function_config_request' &&
Array.isArray(this.#mod?.templateFunctions)
) {
let templateFunction = this.#mod.templateFunctions.find((f) => f.name === payload.name);
if (templateFunction == null) {
this.#sendEmpty(context, replyId);
return;
}
const fn = {
...migrateTemplateFunctionSelectOptions(templateFunction),
onRender: undefined,
};
payload.values = applyFormInputDefaults(fn.args, payload.values);
const p = { ...payload, purpose: 'preview' } as const;
const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, p);
const replyPayload: InternalEventPayload = {
type: 'get_template_function_config_response',
pluginRefId: this.#workerData.pluginRefId,
function: { ...fn, args: resolvedArgs },
};
this.#sendPayload(context, replyPayload, replyId);
return;
}
if (payload.type === 'get_http_authentication_summary_request' && this.#mod?.authentication) {
const replyPayload: InternalEventPayload = {
type: 'get_http_authentication_summary_response',
...this.#mod.authentication,
};
this.#sendPayload(context, replyPayload, replyId);
return;
}
if (payload.type === 'get_http_authentication_config_request' && this.#mod?.authentication) {
const { args, actions } = this.#mod.authentication;
payload.values = applyFormInputDefaults(args, payload.values);
const resolvedArgs = await applyDynamicFormInput(ctx, args, payload);
const resolvedActions: HttpAuthenticationAction[] = [];
for (const { onSelect, ...action } of actions ?? []) {
resolvedActions.push(action);
}
const replyPayload: InternalEventPayload = {
type: 'get_http_authentication_config_response',
args: resolvedArgs,
actions: resolvedActions,
pluginRefId: this.#workerData.pluginRefId,
};
this.#sendPayload(context, replyPayload, replyId);
return;
}
if (payload.type === 'call_http_authentication_request' && this.#mod?.authentication) {
const auth = this.#mod.authentication;
if (typeof auth?.onApply === 'function') {
auth.args = await applyDynamicFormInput(ctx, auth.args, payload);
payload.values = applyFormInputDefaults(auth.args, payload.values);
this.#sendPayload(
context,
{
type: 'call_http_authentication_response',
...(await auth.onApply(ctx, payload)),
},
replyId,
);
return;
}
}
if (
payload.type === 'call_http_authentication_action_request' &&
this.#mod.authentication != null
) {
const action = this.#mod.authentication.actions?.[payload.index];
if (typeof action?.onSelect === 'function') {
await action.onSelect(ctx, payload.args);
this.#sendEmpty(context, replyId);
return;
}
}
if (
payload.type === 'call_http_request_action_request' &&
Array.isArray(this.#mod.httpRequestActions)
) {
const action = this.#mod.httpRequestActions[payload.index];
if (typeof action?.onSelect === 'function') {
await action.onSelect(ctx, payload.args);
this.#sendEmpty(context, replyId);
return;
}
}
if (
payload.type === 'call_websocket_request_action_request' &&
Array.isArray(this.#mod.websocketRequestActions)
) {
const action = this.#mod.websocketRequestActions[payload.index];
if (typeof action?.onSelect === 'function') {
await action.onSelect(ctx, payload.args);
this.#sendEmpty(context, replyId);
return;
}
}
if (
payload.type === 'call_workspace_action_request' &&
Array.isArray(this.#mod.workspaceActions)
) {
const action = this.#mod.workspaceActions[payload.index];
if (typeof action?.onSelect === 'function') {
await action.onSelect(ctx, payload.args);
this.#sendEmpty(context, replyId);
return;
}
}
if (
payload.type === 'call_folder_action_request' &&
Array.isArray(this.#mod.folderActions)
) {
const action = this.#mod.folderActions[payload.index];
if (typeof action?.onSelect === 'function') {
await action.onSelect(ctx, payload.args);
this.#sendEmpty(context, replyId);
return;
}
}
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(context, replyId);
return;
}
}
if (
payload.type === 'call_template_function_request' &&
Array.isArray(this.#mod?.templateFunctions)
) {
const fn = this.#mod.templateFunctions.find((a) => a.name === payload.name);
if (
payload.args.purpose === 'preview' &&
(fn?.previewType === 'click' || fn?.previewType === 'none')
) {
// Send empty render response
this.#sendPayload(
context,
{
type: 'call_template_function_response',
value: null,
error: 'Live preview disabled for this function',
},
replyId,
);
} else if (typeof fn?.onRender === 'function') {
const resolvedArgs = await applyDynamicFormInput(ctx, fn.args, payload.args);
const values = applyFormInputDefaults(resolvedArgs, payload.args.values);
const error = validateTemplateFunctionArgs(fn.name, resolvedArgs, values);
if (error && payload.args.purpose !== 'preview') {
this.#sendPayload(
context,
{ type: 'call_template_function_response', value: null, error },
replyId,
);
return;
}
try {
const result = await fn.onRender(ctx, { ...payload.args, values });
this.#sendPayload(
context,
{ type: 'call_template_function_response', value: result ?? null },
replyId,
);
} catch (err) {
this.#sendPayload(
context,
{
type: 'call_template_function_response',
value: null,
error: `${err}`.replace(/^Error:\s*/g, ''),
},
replyId,
);
}
return;
}
}
} catch (err) {
const error = `${err}`.replace(/^Error:\s*/g, '');
console.log('Plugin call threw exception', payload.type, '→', error);
this.#sendPayload(context, { type: 'error_response', error }, replyId);
return;
}
// No matches, so send back an empty response so the caller doesn't block forever
this.#sendEmpty(context, replyId);
}
#pathMod() {
return path.posix.join(this.#workerData.bootRequest.dir, 'build', 'index.js');
}
#pathPkg() {
return path.join(this.#workerData.bootRequest.dir, 'package.json');
}
#unimportModule() {
const id = require.resolve(this.#pathMod());
delete require.cache[id];
}
#importModule() {
const id = require.resolve(this.#pathMod());
delete require.cache[id];
this.#mod = require(id).plugin;
}
#buildEventToSend(
context: PluginContext,
payload: InternalEventPayload,
replyId: string | null = null,
): InternalEvent {
return {
pluginRefId: this.#workerData.pluginRefId,
pluginName: path.basename(this.#workerData.bootRequest.dir),
id: genId(),
replyId,
payload,
context,
};
}
#sendPayload(
context: PluginContext,
payload: InternalEventPayload,
replyId: string | null,
): string {
const event = this.#buildEventToSend(context, payload, replyId);
this.#sendEvent(event);
return event.id;
}
#sendEvent(event: InternalEvent) {
// if (event.payload.type !== 'empty_response') {
// console.log('Sending event to app', this.#pkg.name, event.id, event.payload.type);
// }
this.#pluginToAppEvents.emit(event);
}
#sendEmpty(context: PluginContext, replyId: string | null = null): string {
return this.#sendPayload(context, { type: 'empty_response' }, replyId);
}
#sendForReply<T extends Omit<InternalEventPayload, 'type'>>(
context: PluginContext,
payload: InternalEventPayload,
): Promise<T> {
// 1. Build event to send
const eventToSend = this.#buildEventToSend(context, payload, null);
// 2. Spawn listener in background
const promise = new Promise<T>((resolve) => {
const cb = (event: InternalEvent) => {
if (event.replyId === eventToSend.id) {
this.#appToPluginEvents.unlisten(cb); // Unlisten, now that we're done
const { type: _, ...payload } = event.payload;
resolve(payload as T);
}
};
this.#appToPluginEvents.listen(cb);
});
// 3. Send the event after we start listening (to prevent race)
this.#sendEvent(eventToSend);
// 4. Return the listener promise
return promise as unknown as Promise<T>;
}
#sendAndListenForEvents(
context: PluginContext,
payload: InternalEventPayload,
onEvent: (event: InternalEventPayload) => void,
): void {
// 1. Build event to send
const eventToSend = this.#buildEventToSend(context, payload, null);
// 2. Listen for replies in the background
this.#appToPluginEvents.listen((event: InternalEvent) => {
if (event.replyId === eventToSend.id) {
onEvent(event.payload);
}
});
// 3. Send the event after we start listening (to prevent race)
this.#sendEvent(eventToSend);
}
#newCtx(context: PluginContext): Context {
const _windowInfo = async () => {
if (context.label == null) {
throw new Error("Can't get window context without an active window");
}
const payload: InternalEventPayload = {
type: 'window_info_request',
label: context.label,
};
return this.#sendForReply<WindowInfoResponse>(context, payload);
};
return {
clipboard: {
copyText: async (text) => {
await this.#sendForReply(context, {
type: 'copy_text_request',
text,
});
},
},
toast: {
show: async (args) => {
await this.#sendForReply(context, {
type: 'show_toast_request',
// Handle default here because null/undefined both convert to None in Rust translation
timeout: args.timeout === undefined ? 5000 : args.timeout,
...args,
});
},
},
window: {
requestId: async () => {
return (await _windowInfo()).requestId;
},
async workspaceId(): Promise<string | null> {
return (await _windowInfo()).workspaceId;
},
async environmentId(): Promise<string | null> {
return (await _windowInfo()).environmentId;
},
openUrl: async ({ onNavigate, onClose, ...args }) => {
args.label = args.label || `${Math.random()}`;
const payload: InternalEventPayload = { type: 'open_window_request', ...args };
const onEvent = (event: InternalEventPayload) => {
if (event.type === 'window_navigate_event') {
onNavigate?.(event);
} else if (event.type === 'window_close_event') {
onClose?.();
}
};
this.#sendAndListenForEvents(context, payload, onEvent);
return {
close: () => {
const closePayload: InternalEventPayload = {
type: 'close_window_request',
label: args.label,
};
this.#sendPayload(context, closePayload, null);
},
};
},
},
prompt: {
text: async (args) => {
const reply: PromptTextResponse = await this.#sendForReply(context, {
type: 'prompt_text_request',
...args,
});
return reply.value;
},
},
httpResponse: {
find: async (args) => {
const payload = {
type: 'find_http_responses_request',
...args,
} as const;
const { httpResponses } = await this.#sendForReply<FindHttpResponsesResponse>(
context,
payload,
);
return httpResponses;
},
},
grpcRequest: {
render: async (args) => {
const payload = {
type: 'render_grpc_request_request',
...args,
} as const;
const { grpcRequest } = await this.#sendForReply<RenderGrpcRequestResponse>(
context,
payload,
);
return grpcRequest;
},
},
httpRequest: {
getById: async (args) => {
const payload = {
type: 'get_http_request_by_id_request',
...args,
} as const;
const { httpRequest } = await this.#sendForReply<GetHttpRequestByIdResponse>(
context,
payload,
);
return httpRequest;
},
send: async (args) => {
const payload = {
type: 'send_http_request_request',
...args,
} as const;
const { httpResponse } = await this.#sendForReply<SendHttpRequestResponse>(
context,
payload,
);
return httpResponse;
},
render: async (args) => {
const payload = {
type: 'render_http_request_request',
...args,
} as const;
const { httpRequest } = await this.#sendForReply<RenderHttpRequestResponse>(
context,
payload,
);
return httpRequest;
},
list: async (args?: { folderId?: string }) => {
const payload = {
type: 'list_http_requests_request',
folderId: args?.folderId,
} as any;
const { httpRequests } = await this.#sendForReply<any>(context, payload);
return httpRequests as any[];
},
},
folder: {
list: async () => {
const payload = {
type: 'list_folders_request',
} as any;
const { folders } = await this.#sendForReply<any>(context, payload);
return folders as any[];
},
},
cookies: {
getValue: async (args: GetCookieValueRequest) => {
const payload = {
type: 'get_cookie_value_request',
...args,
} as const;
const { value } = await this.#sendForReply<GetCookieValueResponse>(context, payload);
return value;
},
listNames: async () => {
const payload = { type: 'list_cookie_names_request' } as const;
const { names } = await this.#sendForReply<ListCookieNamesResponse>(context, payload);
return names;
},
},
templates: {
/**
* Invoke Yaak's template engine to render a value. If the value is a nested type
* (eg. object), it will be recursively rendered.
*/
render: async (args) => {
const payload = { type: 'template_render_request', ...args } as const;
const result = await this.#sendForReply<TemplateRenderResponse>(context, payload);
return result.data as any;
},
},
store: {
get: async <T>(key: string) => {
const payload = { type: 'get_key_value_request', key } as const;
const result = await this.#sendForReply<GetKeyValueResponse>(context, payload);
return result.value ? (JSON.parse(result.value) as T) : undefined;
},
set: async <T>(key: string, value: T) => {
const valueStr = JSON.stringify(value);
const payload: InternalEventPayload = {
type: 'set_key_value_request',
key,
value: valueStr,
};
await this.#sendForReply<GetKeyValueResponse>(context, payload);
},
delete: async (key: string) => {
const payload = { type: 'delete_key_value_request', key } as const;
const result = await this.#sendForReply<DeleteKeyValueResponse>(context, payload);
return result.deleted;
},
},
plugin: {
reload: () => {
this.#sendPayload(context, { type: 'reload_response', silent: true }, null);
},
},
};
}
}
function genId(len = 5): string {
const alphabet = '01234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
let id = '';
for (let i = 0; i < len; i++) {
id += alphabet[Math.floor(Math.random() * alphabet.length)];
}
return id;
}
const watchedFiles: Record<string, Stats | null> = {};
/**
* 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.
*/
function watchFile(filepath: string, cb: () => void) {
watch(filepath, () => {
const stat = statSync(filepath, { throwIfNoEntry: false });
if (stat == null || stat.mtimeMs !== watchedFiles[filepath]?.mtimeMs) {
watchedFiles[filepath] = stat ?? null;
cb();
}
});
}

View File

@@ -1,37 +0,0 @@
import { CallHttpAuthenticationActionArgs, CallTemplateFunctionArgs } from '@yaakapp-internal/plugins';
import { Context, DynamicAuthenticationArg, DynamicTemplateFunctionArg } from '@yaakapp/api';
export async function applyDynamicFormInput(
ctx: Context,
args: DynamicTemplateFunctionArg[],
callArgs: CallTemplateFunctionArgs,
): Promise<DynamicTemplateFunctionArg[]>;
export async function applyDynamicFormInput(
ctx: Context,
args: DynamicAuthenticationArg[],
callArgs: CallHttpAuthenticationActionArgs,
): Promise<DynamicAuthenticationArg[]>;
export async function applyDynamicFormInput(
ctx: Context,
args: (DynamicTemplateFunctionArg | DynamicAuthenticationArg)[],
callArgs: CallTemplateFunctionArgs | CallHttpAuthenticationActionArgs,
): Promise<(DynamicTemplateFunctionArg | DynamicAuthenticationArg)[]> {
const resolvedArgs: any[] = [];
for (const { dynamic, ...arg } of args) {
const newArg: any = {
...arg,
...(typeof dynamic === 'function' ? await dynamic(ctx, callArgs as any) : undefined),
};
if ('inputs' in newArg && Array.isArray(newArg.inputs)) {
try {
newArg.inputs = await applyDynamicFormInput(ctx, newArg.inputs, callArgs as any);
} catch (e) {
console.error('Failed to apply dynamic form input', e);
}
}
resolvedArgs.push(newArg);
}
return resolvedArgs;
}

View File

@@ -1,64 +0,0 @@
import type { InternalEvent } from '@yaakapp/api';
import { EventChannel } from './EventChannel';
import { PluginHandle } from './PluginHandle';
import WebSocket from 'ws';
const port = process.env.PORT;
if (!port) {
throw new Error('Plugin runtime missing PORT')
}
const host = process.env.HOST;
if (!host) {
throw new Error('Plugin runtime missing HOST')
}
const pluginToAppEvents = new EventChannel();
const plugins: Record<string, PluginHandle> = {};
const ws = new WebSocket(`ws://${host}:${port}`);
ws.on('message', async (e: Buffer) => {
try {
await handleIncoming(e.toString());
} catch (err) {
console.log('Failed to handle incoming plugin event', err);
}
});
ws.on('open', () => console.log('Plugin runtime connected to websocket'));
ws.on('error', (err: any) => console.error('Plugin runtime websocket error', err));
ws.on('close', (code: number) => console.log('Plugin runtime websocket closed', code));
// Listen for incoming events from plugins
pluginToAppEvents.listen((e) => {
const eventStr = JSON.stringify(e);
ws.send(eventStr);
});
async function handleIncoming(msg: string) {
const pluginEvent: InternalEvent = JSON.parse(msg);
// Handle special event to bootstrap plugin
if (pluginEvent.payload.type === 'boot_request') {
const plugin = new PluginHandle(pluginEvent.pluginRefId, pluginEvent.context, pluginEvent.payload, pluginToAppEvents);
plugins[pluginEvent.pluginRefId] = plugin;
}
// Once booted, forward all events to the plugin worker
const plugin = plugins[pluginEvent.pluginRefId];
if (!plugin) {
console.warn('Failed to get plugin for event by', pluginEvent.pluginRefId);
return;
}
if (pluginEvent.payload.type === 'terminate_request') {
await plugin.terminate();
console.log('Terminated plugin worker', pluginEvent.pluginRefId);
delete plugins[pluginEvent.pluginRefId];
}
plugin.sendToWorker(pluginEvent);
}
process.on('unhandledRejection', (reason, promise) => {
console.error('Unhandled Rejection at:', promise, 'reason:', reason);
});

View File

@@ -1,37 +0,0 @@
import process from "node:process";
export function interceptStdout(
intercept: (text: string) => string,
) {
const old_stdout_write = process.stdout.write;
const old_stderr_write = process.stderr.write;
process.stdout.write = (function (write) {
return function (text: string) {
arguments[0] = interceptor(text, intercept);
// deno-lint-ignore no-explicit-any
write.apply(process.stdout, arguments as any);
return true;
};
})(process.stdout.write);
process.stderr.write = (function (write) {
return function (text: string) {
arguments[0] = interceptor(text, intercept);
// deno-lint-ignore no-explicit-any
write.apply(process.stderr, arguments as any);
return true;
};
})(process.stderr.write);
// puts back to original
return function unhook() {
process.stdout.write = old_stdout_write;
process.stderr.write = old_stderr_write;
};
}
function interceptor(text: string, fn: (text: string) => string) {
return fn(text).replace(/\n$/, "") +
(fn(text) && /\n$/.test(text) ? "\n" : "");
}

View File

@@ -1,17 +0,0 @@
import type { TemplateFunctionPlugin } from '@yaakapp/api';
export function migrateTemplateFunctionSelectOptions(
f: TemplateFunctionPlugin,
): TemplateFunctionPlugin {
const migratedArgs = f.args.map((a) => {
if (a.type === 'select') {
a.options = a.options.map((o) => ({
...o,
label: o.label || (o as any).name,
}));
}
return a;
});
return { ...f, args: migratedArgs };
}

View File

@@ -1,150 +0,0 @@
import { CallTemplateFunctionArgs } from '@yaakapp-internal/plugins';
import { Context, DynamicTemplateFunctionArg } from '@yaakapp/api';
import { describe, expect, test } from 'vitest';
import { applyDynamicFormInput, applyFormInputDefaults } from '../src/common';
describe('applyFormInputDefaults', () => {
test('Works with top-level select', () => {
const args: DynamicTemplateFunctionArg[] = [
{
type: 'select',
name: 'test',
options: [{ label: 'Option 1', value: 'one' }],
defaultValue: 'one',
},
];
expect(applyFormInputDefaults(args, {})).toEqual({
test: 'one',
});
});
test('Works with existing value', () => {
const args: DynamicTemplateFunctionArg[] = [
{
type: 'select',
name: 'test',
options: [{ label: 'Option 1', value: 'one' }],
defaultValue: 'one',
},
];
expect(applyFormInputDefaults(args, { test: 'explicit' })).toEqual({
test: 'explicit',
});
});
test('Works with recursive select', () => {
const args: DynamicTemplateFunctionArg[] = [
{ type: 'text', name: 'dummy', defaultValue: 'top' },
{
type: 'accordion',
label: 'Test',
inputs: [
{ type: 'text', name: 'name', defaultValue: 'hello' },
{
type: 'select',
name: 'test',
options: [{ label: 'Option 1', value: 'one' }],
defaultValue: 'one',
},
],
},
];
expect(applyFormInputDefaults(args, {})).toEqual({
dummy: 'top',
test: 'one',
name: 'hello',
});
});
test('Works with dynamic options', () => {
const args: DynamicTemplateFunctionArg[] = [
{
type: 'select',
name: 'test',
defaultValue: 'one',
options: [],
dynamic() {
return { options: [{ label: 'Option 1', value: 'one' }] };
},
},
];
expect(applyFormInputDefaults(args, {})).toEqual({
test: 'one',
});
expect(applyFormInputDefaults(args, {})).toEqual({
test: 'one',
});
});
});
describe('applyDynamicFormInput', () => {
test('Works with plain input', async () => {
const ctx = {} as Context;
const args: DynamicTemplateFunctionArg[] = [
{ type: 'text', name: 'name' },
{ type: 'checkbox', name: 'checked' },
];
const callArgs: CallTemplateFunctionArgs = {
values: {},
purpose: 'preview',
};
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
{ type: 'text', name: 'name' },
{ type: 'checkbox', name: 'checked' },
]);
});
test('Works with dynamic input', async () => {
const ctx = {} as Context;
const args: DynamicTemplateFunctionArg[] = [
{
type: 'text',
name: 'name',
async dynamic(_ctx, _args) {
return { hidden: true };
},
},
];
const callArgs: CallTemplateFunctionArgs = {
values: {},
purpose: 'preview',
};
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
{ type: 'text', name: 'name', hidden: true },
]);
});
test('Works with recursive dynamic input', async () => {
const ctx = {} as Context;
const callArgs: CallTemplateFunctionArgs = {
values: { hello: 'world' },
purpose: 'preview',
};
const args: DynamicTemplateFunctionArg[] = [
{
type: 'banner',
inputs: [
{
type: 'text',
name: 'name',
async dynamic(_ctx, args) {
return { hidden: args.values.hello === 'world' };
},
},
],
},
];
expect(await applyDynamicFormInput(ctx, args, callArgs)).toEqual([
{
type: 'banner',
inputs: [
{
type: 'text',
name: 'name',
hidden: true,
},
],
},
]);
});
});

View File

@@ -1,25 +0,0 @@
{
"compilerOptions": {
"module": "node16",
"strict": true,
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"target": "es2021",
"lib": ["es2021"],
"noImplicitAny": false,
"moduleResolution": "node16",
"resolveJsonModule": true,
"sourceMap": true,
"outDir": "build",
"baseUrl": ".",
"paths": {
"*": [
"node_modules/*",
"src/types/*"
]
}
},
"include": [
"src"
]
}

1
plugins/.gitignore vendored
View File

@@ -1 +0,0 @@
*/build

View File

@@ -1,68 +0,0 @@
# Copy as cUrl
A request action plugin for Yaak that converts HTTP requests into [curl](https://curl.se)
commands, making it easy to share, debug, and execute requests outside Yaak.
![Screenshot of context menu](screenshot.png)
## Overview
This plugin adds a 'Copy as Curl' action to HTTP requests, converting any request into its
equivalent curl command. This is useful for debugging, sharing requests with team members,
and executing requests in terminal environments where `curl` is available.
## How It Works
The plugin analyzes the given HTTP request and generates a properly formatted curl command
that includes:
- HTTP method (GET, POST, PUT, DELETE, etc.)
- Request URL with query parameters
- Headers (including authentication headers)
- Request body (for POST, PUT, PATCH requests)
- Authentication credentials
## Usage
1. Configure an HTTP request as usual in Yaak
2. Right-click on the request in the sidebar
3. Select 'Copy as Curl'
4. The command is copied to your clipboard
5. Share or execute the command
## Generated Curl Examples
### Simple GET Request
```bash
curl -X GET 'https://api.example.com/users' \
--header 'Accept: application/json'
```
### POST Request with JSON Data
```bash
curl -X POST 'https://api.example.com/users' \
--header 'Content-Type: application/json' \
--header 'Accept: application/json' \
--data '{
"name": "John Doe",
"email": "john@example.com"
}'
```
### Request with Multi-part Form Data
```bash
curl -X POST 'yaak.app' \
--header 'Content-Type: multipart/form-data' \
--form 'hello=world' \
--form file=@/path/to/file.json
```
### Request with Authentication
```bash
curl -X GET 'https://api.example.com/protected' \
--user 'username:password'
```

View File

@@ -1,17 +0,0 @@
{
"name": "@yaak/action-copy-curl",
"displayName": "Copy as Curl",
"description": "Copy request as a curl command",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins/action-copy-curl"
},
"private": true,
"version": "0.1.0",
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev",
"test": "vitest --run tests"
}
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 496 KiB

View File

@@ -1,173 +0,0 @@
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: 'Command copied to clipboard',
icon: 'copy',
color: 'success',
});
},
},
],
};
export async function convertToCurl(request: Partial<HttpRequest>) {
const xs = ['curl'];
// Add method and URL all on first line
if (request.method) xs.push('-X', request.method);
// Build final URL with parameters (compatible with old curl)
let finalUrl = request.url || '';
const urlParams = (request.urlParameters ?? []).filter(onlyEnabled);
if (urlParams.length > 0) {
// Build url
const [base, hash] = finalUrl.split('#');
const separator = base?.includes('?') ? '&' : '?';
const queryString = urlParams
.map((p) => `${encodeURIComponent(p.name)}=${encodeURIComponent(p.value)}`)
.join('&');
finalUrl = base + separator + queryString + (hash ? `#${hash}` : '');
}
// Add API key authentication
if (request.authenticationType === 'apikey') {
if (request.authentication?.location === 'query') {
const sep = finalUrl.includes('?') ? '&' : '?';
finalUrl = [
finalUrl,
sep,
encodeURIComponent(request.authentication?.key ?? 'token'),
'=',
encodeURIComponent(request.authentication?.value ?? ''),
].join('');
} else {
request.headers = request.headers ?? [];
request.headers.push({
name: request.authentication?.key ?? 'X-Api-Key',
value: request.authentication?.value ?? '',
});
}
}
xs.push(quote(finalUrl));
xs.push(NEWLINE);
// Add headers
for (const h of (request.headers ?? []).filter(onlyEnabled)) {
xs.push('--header', quote(`${h.name}: ${h.value}`));
xs.push(NEWLINE);
}
// Add form params
const type = request.bodyType ?? 'none';
if (
(type === 'multipart/form-data' || type === 'application/x-www-form-urlencoded') &&
Array.isArray(request.body?.form)
) {
const flag = request.bodyType === 'multipart/form-data' ? '--form' : '--data';
for (const p of (request.body?.form ?? []).filter(onlyEnabled)) {
if (p.file) {
let v = `${p.name}=@${p.file}`;
v += p.contentType ? `;type=${p.contentType}` : '';
xs.push(flag, v);
} else {
xs.push(flag, quote(`${p.name}=${p.value}`));
}
xs.push(NEWLINE);
}
} else if (type === 'graphql' && typeof request.body?.query === 'string') {
const body = {
query: request.body.query || '',
variables: maybeParseJSON(request.body.variables, undefined),
};
xs.push('--data', quote(JSON.stringify(body)));
xs.push(NEWLINE);
} else if (type !== 'none' && typeof request.body?.text === 'string') {
xs.push('--data', quote(request.body.text));
xs.push(NEWLINE);
}
// Add basic/digest authentication
if (request.authentication?.disabled !== true) {
if (request.authenticationType === 'basic' || request.authenticationType === 'digest') {
if (request.authenticationType === 'digest') xs.push('--digest');
xs.push(
'--user',
quote(
`${request.authentication?.username ?? ''}:${request.authentication?.password ?? ''}`,
),
);
xs.push(NEWLINE);
}
// Add bearer authentication
if (request.authenticationType === 'bearer') {
const value =
`${request.authentication?.prefix ?? 'Bearer'} ${request.authentication?.token ?? ''}`.trim();
xs.push('--header', quote(`Authorization: ${value}`));
xs.push(NEWLINE);
}
if (request.authenticationType === 'auth-aws-sig-v4') {
xs.push(
'--aws-sigv4',
[
'aws',
'amz',
request.authentication?.region ?? '',
request.authentication?.service ?? '',
].join(':'),
);
xs.push(NEWLINE);
xs.push(
'--user',
quote(
`${request.authentication?.accessKeyId ?? ''}:${request.authentication?.secretAccessKey ?? ''}`,
),
);
if (request.authentication?.sessionToken) {
xs.push(NEWLINE);
xs.push('--header', quote(`X-Amz-Security-Token: ${request.authentication.sessionToken}`));
}
xs.push(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 maybeParseJSON<T>(v: string, fallback: T) {
try {
return JSON.parse(v);
} catch {
return fallback;
}
}

View File

@@ -1,414 +0,0 @@
import { describe, expect, test } from 'vitest';
import { convertToCurl } from '../src';
describe('exporter-curl', () => {
test('Exports GET with params', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
urlParameters: [
{ name: 'a', value: 'aaa' },
{ name: 'b', value: 'bbb', enabled: true },
{ name: 'c', value: 'ccc', enabled: false },
],
}),
).toEqual([`curl 'https://yaak.app?a=aaa&b=bbb'`].join(' \\n '));
});
test('Exports GET with params and hash', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app/path#section',
urlParameters: [
{ name: 'a', value: 'aaa' },
{ name: 'b', value: 'bbb', enabled: true },
{ name: 'c', value: 'ccc', enabled: false },
],
}),
).toEqual([`curl 'https://yaak.app/path?a=aaa&b=bbb#section'`].join(' \\n '));
});
test('Exports POST with url form data', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
method: 'POST',
bodyType: 'application/x-www-form-urlencoded',
body: {
form: [
{ name: 'a', value: 'aaa' },
{ name: 'b', value: 'bbb', enabled: true },
{ name: 'c', value: 'ccc', enabled: false },
],
},
}),
).toEqual(
[`curl -X POST 'https://yaak.app'`, `--data 'a=aaa'`, `--data 'b=bbb'`].join(' \\\n '),
);
});
test('Exports POST with GraphQL data', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
method: 'POST',
bodyType: 'graphql',
body: {
query: '{foo,bar}',
variables: '{"a": "aaa", "b": "bbb"}',
},
}),
).toEqual(
[
`curl -X POST 'https://yaak.app'`,
`--data '{"query":"{foo,bar}","variables":{"a":"aaa","b":"bbb"}}'`,
].join(' \\\n '),
);
});
test('Exports POST with GraphQL data no variables', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
method: 'POST',
bodyType: 'graphql',
body: {
query: '{foo,bar}',
},
}),
).toEqual(
[`curl -X POST 'https://yaak.app'`, `--data '{"query":"{foo,bar}"}'`].join(' \\\n '),
);
});
test('Exports PUT with multipart form', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
method: 'PUT',
bodyType: 'multipart/form-data',
body: {
form: [
{ name: 'a', value: 'aaa' },
{ name: 'b', value: 'bbb', enabled: true },
{ name: 'c', value: 'ccc', enabled: false },
{ name: 'f', file: '/foo/bar.png', contentType: 'image/png' },
],
},
}),
).toEqual(
[
`curl -X PUT 'https://yaak.app'`,
`--form 'a=aaa'`,
`--form 'b=bbb'`,
'--form f=@/foo/bar.png;type=image/png',
].join(' \\\n '),
);
});
test('Exports JSON body', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
method: 'POST',
bodyType: 'application/json',
body: {
text: `{"foo":"bar's"}`,
},
headers: [{ name: 'Content-Type', value: 'application/json' }],
}),
).toEqual(
[
`curl -X POST 'https://yaak.app'`,
`--header 'Content-Type: application/json'`,
`--data '{"foo":"bar\\'s"}'`,
].join(' \\\n '),
);
});
test('Exports multi-line JSON body', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
method: 'POST',
bodyType: 'application/json',
body: {
text: `{"foo":"bar",\n"baz":"qux"}`,
},
headers: [{ name: 'Content-Type', value: 'application/json' }],
}),
).toEqual(
[
`curl -X POST 'https://yaak.app'`,
`--header 'Content-Type: application/json'`,
`--data '{"foo":"bar",\n"baz":"qux"}'`,
].join(' \\\n '),
);
});
test('Exports headers', async () => {
expect(
await convertToCurl({
headers: [
{ name: 'a', value: 'aaa' },
{ name: 'b', value: 'bbb', enabled: true },
{ name: 'c', value: 'ccc', enabled: false },
],
}),
).toEqual([`curl ''`, `--header 'a: aaa'`, `--header 'b: bbb'`].join(' \\\n '));
});
test('Basic auth', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'basic',
authentication: {
username: 'user',
password: 'pass',
},
}),
).toEqual([`curl 'https://yaak.app'`, `--user 'user:pass'`].join(' \\\n '));
});
test('Basic auth disabled', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'basic',
authentication: {
disabled: true,
username: 'user',
password: 'pass',
},
}),
).toEqual([`curl 'https://yaak.app'`].join(' \\\n '));
});
test('Broken basic auth', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'basic',
authentication: {},
}),
).toEqual([`curl 'https://yaak.app'`, `--user ':'`].join(' \\\n '));
});
test('Digest auth', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'digest',
authentication: {
username: 'user',
password: 'pass',
},
}),
).toEqual([`curl 'https://yaak.app'`, `--digest --user 'user:pass'`].join(' \\\n '));
});
test('Bearer auth', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'bearer',
authentication: {
token: 'tok',
},
}),
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer tok'`].join(' \\\n '));
});
test('Bearer auth with custom prefix', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'bearer',
authentication: {
token: 'abc123',
prefix: 'Token',
},
}),
).toEqual(
[`curl 'https://yaak.app'`, `--header 'Authorization: Token abc123'`].join(' \\\n '),
);
});
test('Bearer auth with empty prefix', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'bearer',
authentication: {
token: 'xyz789',
prefix: '',
},
}),
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: xyz789'`].join(' \\\n '));
});
test('Broken bearer auth', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'bearer',
authentication: {
username: 'user',
password: 'pass',
},
}),
).toEqual([`curl 'https://yaak.app'`, `--header 'Authorization: Bearer'`].join(' \\\n '));
});
test('AWS v4 auth', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'auth-aws-sig-v4',
authentication: {
accessKeyId: 'ak',
secretAccessKey: 'sk',
sessionToken: '',
region: 'us-east-1',
service: 's3',
},
}),
).toEqual(
[`curl 'https://yaak.app'`, '--aws-sigv4 aws:amz:us-east-1:s3', `--user 'ak:sk'`].join(
' \\\n ',
),
);
});
test('AWS v4 auth with session', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'auth-aws-sig-v4',
authentication: {
accessKeyId: 'ak',
secretAccessKey: 'sk',
sessionToken: 'st',
region: 'us-east-1',
service: 's3',
},
}),
).toEqual(
[
`curl 'https://yaak.app'`,
'--aws-sigv4 aws:amz:us-east-1:s3',
`--user 'ak:sk'`,
`--header 'X-Amz-Security-Token: st'`,
].join(' \\\n '),
);
});
test('API key auth header', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'apikey',
authentication: {
location: 'header',
key: 'X-Header',
value: 'my-token',
},
}),
).toEqual([`curl 'https://yaak.app'`, `--header 'X-Header: my-token'`].join(' \\\n '));
});
test('API key auth header query', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app?hi=there',
urlParameters: [{ name: 'param', value: 'hi' }],
authenticationType: 'apikey',
authentication: {
location: 'query',
key: 'foo',
value: 'bar',
},
}),
).toEqual([`curl 'https://yaak.app?hi=there&param=hi&foo=bar'`].join(' \\\n '));
});
test('API key auth header query with params', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
urlParameters: [{ name: 'param', value: 'hi' }],
authenticationType: 'apikey',
authentication: {
location: 'query',
key: 'foo',
value: 'bar',
},
}),
).toEqual([`curl 'https://yaak.app?param=hi&foo=bar'`].join(' \\\n '));
});
test('API key auth header default', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'apikey',
authentication: {
location: 'header',
},
}),
).toEqual([`curl 'https://yaak.app'`, `--header 'X-Api-Key: '`].join(' \\\n '));
});
test('API key auth query', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
authenticationType: 'apikey',
authentication: {
location: 'query',
key: 'foo',
value: 'bar-baz',
},
}),
).toEqual([`curl 'https://yaak.app?foo=bar-baz'`].join(' \\\n '));
});
test('API key auth query with existing', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app?foo=bar&baz=qux',
authenticationType: 'apikey',
authentication: {
location: 'query',
key: 'hi',
value: 'there',
},
}),
).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&hi=there'`].join(' \\\n '));
});
test('API key auth query default', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app?foo=bar&baz=qux',
authenticationType: 'apikey',
authentication: {
location: 'query',
},
}),
).toEqual([`curl 'https://yaak.app?foo=bar&baz=qux&token='`].join(' \\\n '));
});
test('Stale body data', async () => {
expect(
await convertToCurl({
url: 'https://yaak.app',
bodyType: 'none',
body: {
text: 'ignore me',
},
}),
).toEqual([`curl 'https://yaak.app'`].join(' \\\n '));
});
});

View File

@@ -1,3 +0,0 @@
{
"extends": "../../tsconfig.json"
}

View File

@@ -1,76 +0,0 @@
# Copy as gRPCurl
An HTTP request action plugin that converts gRPC requests
into [gRPCurl](https://github.com/fullstorydev/grpcurl) commands, enabling easy sharing,
debugging, and execution of gRPC calls outside Yaak.
![Screenshot of context menu](screenshot.png)
## Overview
This plugin adds a "Copy as gRPCurl" action to gRPC requests, converting any gRPC request
into its equivalent executable command. This is useful for debugging gRPC services,
sharing requests with team members, or executing gRPC calls in terminal environments where
`grpcurl` is available.
## How It Works
The plugin analyzes your gRPC request configuration and generates a properly formatted
`grpcurl` command that includes:
- gRPC service and method names
- Server address and port
- Request message data (JSON format)
- Metadata (headers)
- Authentication credentials
- Protocol buffer definitions
## Usage
1. Configure a gRPC request as usual in Yaak
2. Right-click on the request sidebar item
3. Select "Copy as gRPCurl" from the available actions
4. The command is copied to your clipboard
5. Share or execute the command
## Generated gRPCurl Examples
### Simple Unary Call
```bash
grpcurl -plaintext \
-d '{"name": "John Doe"}' \
localhost:9090 \
user.UserService/GetUser
```
### Call with Metadata
```bash
grpcurl -plaintext \
-H "authorization: Bearer my-token" \
-H "x-api-version: v1" \
-d '{"user_id": "12345"}' \
api.example.com:443 \
user.UserService/GetUserProfile
```
### Call with TLS
```bash
grpcurl \
-d '{"query": "search term"}' \
secure-api.example.com:443 \
search.SearchService/Search
```
### Call with Proto Files
```bash
grpcurl -import-path /path/to/protos \
-proto /other/path/to/user.proto \
-d '{"email": "user@example.com"}' \
localhost:9090 \
user.UserService/CreateUser
```

View File

@@ -1,17 +0,0 @@
{
"name": "@yaak/action-copy-grpcurl",
"displayName": "Copy as gRPCurl",
"description": "Copy gRPC request as a grpcurl command",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins/action-copy-grpcurl"
},
"private": true,
"version": "0.1.0",
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev",
"test": "vitest --run tests"
}
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 492 KiB

View File

@@ -1,155 +0,0 @@
import path from 'node:path';
import type { GrpcRequest, PluginDefinition } from '@yaakapp/api';
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.posix.join(f, '..'));
inferredIncludes.add(path.posix.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.authentication?.disabled !== true) {
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);
} else if (request.authenticationType === 'apikey') {
if (request.authentication?.location === 'query') {
const sep = request.url?.includes('?') ? '&' : '?';
request.url = [
request.url,
sep,
encodeURIComponent(request.authentication?.key ?? 'token'),
'=',
encodeURIComponent(request.authentication?.value ?? ''),
].join('');
} else {
xs.push(
'-H',
quote(
`${request.authentication?.key ?? 'X-Api-Key'}: ${request.authentication?.value ?? ''}`,
),
);
}
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);
xs.push(NEWLINE);
}
// 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;
}
}

View File

@@ -1,159 +0,0 @@
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('Basic auth', async () => {
expect(
await convert(
{
url: 'https://yaak.app',
authenticationType: 'basic',
authentication: {
username: 'user',
password: 'pass',
},
},
[],
),
).toEqual([`grpcurl -H 'Authorization: Basic dXNlcjpwYXNz'`, 'yaak.app'].join(' \\\n '));
});
test('API key auth', async () => {
expect(
await convert(
{
url: 'https://yaak.app',
authenticationType: 'apikey',
authentication: {
key: 'X-Token',
value: 'tok',
},
},
[],
),
).toEqual([`grpcurl -H 'X-Token: tok'`, 'yaak.app'].join(' \\\n '));
});
test('API key auth', async () => {
expect(
await convert(
{
url: 'https://yaak.app',
authenticationType: 'apikey',
authentication: {
location: 'query',
key: 'token',
value: 'tok 1',
},
},
[],
),
).toEqual(['grpcurl', 'yaak.app?token=tok%201'].join(' \\\n '));
});
test('Single proto file', async () => {
expect(await convert({ url: 'https://yaak.app' }, ['/foo/bar/baz.proto'])).toEqual(
[
`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 '),
);
});
});

View File

@@ -1,3 +0,0 @@
{
"extends": "../../tsconfig.json"
}

View File

@@ -1,16 +0,0 @@
{
"name": "@yaak/auth-apikey",
"displayName": "API Key Authentication",
"description": "Authenticate requests using an API key",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins/auth-apikey"
},
"private": true,
"version": "0.1.0",
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev"
}
}

View File

@@ -1,54 +0,0 @@
import type { PluginDefinition } from '@yaakapp/api';
export const plugin: PluginDefinition = {
authentication: {
name: 'apikey',
label: 'API Key',
shortLabel: 'API Key',
args: [
{
type: 'select',
name: 'location',
label: 'Behavior',
defaultValue: 'header',
options: [
{ label: 'Insert Header', value: 'header' },
{ label: 'Append Query Parameter', value: 'query' },
],
},
{
type: 'text',
name: 'key',
label: 'Key',
dynamic: (_ctx, { values }) => {
return values.location === 'query'
? {
label: 'Parameter Name',
description: 'The name of the query parameter to add to the request',
}
: {
label: 'Header Name',
description: 'The name of the header to add to the request',
};
},
},
{
type: 'text',
name: 'value',
label: 'API Key',
optional: true,
password: true,
},
],
async onApply(_ctx, { values }) {
const key = String(values.key ?? '');
const value = String(values.value ?? '');
const location = String(values.location);
if (location === 'query') {
return { setQueryParameters: [{ name: key, value }] };
}
return { setHeaders: [{ name: key, value }] };
},
},
};

View File

@@ -1,3 +0,0 @@
{
"extends": "../../tsconfig.json"
}

View File

@@ -1,49 +0,0 @@
# AWS Signature Version 4 Auth
A plugin for authenticating AWS-compatible requests using the
[AWS Signature Version 4 signing process](https://docs.aws.amazon.com/general/latest/gr/signature-version-4.html).
This enables secure, signed requests to AWS services (or any S3-compatible APIs like
Cloudflare R2).
![Screenshot of AWS SigV4 UI](screenshot.png)
## Overview
This plugin provides AWS Signature authentication for API requests in Yaak. SigV4 is used
by nearly all AWS APIs to verify the authenticity and integrity of requests using
cryptographic signatures.
With this plugin, you can securely sign requests to AWS services such as S3, STS, Lambda,
API Gateway, DynamoDB, and more. You can also authenticate against S3-compatible services
like **Cloudflare R2**, **MinIO**, or **Wasabi**.
## How AWS Signature Version 4 Works
SigV4 signs requests by creating a hash of key request components (method, URL, headers,
and optionally the payload) using your AWS credentials. The resulting HMAC signature is
added in the `Authorization` header along with credential scope metadata.
Example header:
```
Authorization: AWS4-HMAC-SHA256 Credential=AKIA…/20251011/us-east-1/s3/aws4_request, SignedHeaders=host;x-amz-date, Signature=abcdef123456…
```
Each request must include a timestamp (`X-Amz-Date`) and may include a session token if
using temporary credentials.
## Configuration
The plugin presents the following fields:
- **Access Key ID** Your AWS access key identifier
- **Secret Access Key** The secret associated with the access key
- **Session Token** *(optional)* Used for temporary or assumed-role credentials (treated as secret)
- **Region** AWS region (e.g., `us-east-1`)
- **Service** AWS service identifier (e.g., `sts`, `s3`, `execute-api`)
## Usage
1. Configure a request, folder, or workspace to use **AWS SigV4 Authentication**
2. Enter your AWS credentials and target service/region
3. The plugin will automatically sign outgoing requests with valid SigV4 headers

View File

@@ -1,22 +0,0 @@
{
"name": "@yaak/auth-aws",
"displayName": "AWS SigV4",
"description": "Authenticate requests using AWS SigV4 signing",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins/auth-aws"
},
"private": true,
"version": "0.1.0",
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev"
},
"dependencies": {
"aws4": "^1.13.2"
},
"devDependencies": {
"@types/aws4": "^1.11.6"
}
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 790 KiB

View File

@@ -1,88 +0,0 @@
import { URL } from 'node:url';
import type { PluginDefinition } from '@yaakapp/api';
import type { CallHttpAuthenticationResponse } from '@yaakapp-internal/plugins';
import type { Request } from 'aws4';
import aws4 from 'aws4';
export const plugin: PluginDefinition = {
authentication: {
name: 'awsv4',
label: 'AWS Signature',
shortLabel: 'AWS v4',
args: [
{ name: 'accessKeyId', label: 'Access Key ID', type: 'text', password: true },
{
name: 'secretAccessKey',
label: 'Secret Access Key',
type: 'text',
password: true,
},
{
name: 'service',
label: 'Service Name',
type: 'text',
defaultValue: 'sts',
placeholder: 'sts',
description: 'The service that is receiving the request (sts, s3, sqs, ...)',
},
{
name: 'region',
label: 'Region',
type: 'text',
placeholder: 'us-east-1',
description: 'The region that is receiving the request (defaults to us-east-1)',
optional: true,
},
{
name: 'sessionToken',
label: 'Session Token',
type: 'text',
password: true,
optional: true,
description: 'Only required if you are using temporary credentials',
},
],
onApply(_ctx, { values, ...args }): CallHttpAuthenticationResponse {
const accessKeyId = String(values.accessKeyId || '');
const secretAccessKey = String(values.secretAccessKey || '');
const sessionToken = String(values.sessionToken || '') || undefined;
const url = new URL(args.url);
const headers: NonNullable<Request['headers']> = {};
for (const headerName of ['content-type', 'host', 'x-amz-date', 'x-amz-security-token']) {
const v = args.headers.find((h) => h.name.toLowerCase() === headerName);
if (v != null) {
headers[headerName] = v.value;
}
}
const signature = aws4.sign(
{
host: url.host,
method: args.method,
path: url.pathname + (url.search || ''),
service: String(values.service || 'sts'),
region: values.region ? String(values.region) : undefined,
headers,
doNotEncodePath: true,
},
{
accessKeyId,
secretAccessKey,
sessionToken,
},
);
if (signature.headers == null) {
return {};
}
return {
setHeaders: Object.entries(signature.headers)
.filter(([name]) => name !== 'content-type') // Don't add this because we already have it
.map(([name, value]) => ({ name, value: String(value || '') })),
};
},
},
};

View File

@@ -1,3 +0,0 @@
{
"extends": "../../tsconfig.json"
}

View File

@@ -1,44 +0,0 @@
# Basic Authentication
A simple Basic Authentication plugin that implements HTTP Basic Auth according
to [RFC 7617](https://datatracker.ietf.org/doc/html/rfc7617), enabling secure
authentication with username and password credentials.
![Screenshot of basic auth UI](screenshot.png)
## Overview
This plugin provides HTTP Basic Authentication support for API requests in Yaak. Basic
Auth is one of the most widely supported authentication methods, making it ideal for APIs
that require simple username/password authentication without the complexity of OAuth
flows.
## How Basic Authentication Works
Basic Authentication encodes your username and password credentials using Base64 encoding
and sends them in the `Authorization` header with each request. The format is:
```
Authorization: Basic <base64-encoded-credentials>
```
Where `<base64-encoded-credentials>` is the Base64 encoding of `username:password`.
## Configuration
The plugin presents two fields:
- **Username**: Username or user identifier
- **Password**: Password or authentication token
## Usage
1. Configure the request, folder, or workspace to use Basic Authentication
2. Enter your username and password in the authentication configuration
3. The plugin will automatically add the proper `Authorization` header to your requests
## Troubleshooting
- **401 Unauthorized**: Verify your username and password are correct
- **403 Forbidden**: Check if your account has the necessary permissions
- **Connection Issues**: Ensure you're using HTTPS for secure transmission

View File

@@ -1,16 +0,0 @@
{
"name": "@yaak/auth-basic",
"displayName": "Basic Authentication",
"description": "Authenticate requests using Basic Auth",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins/auth-basic"
},
"private": true,
"version": "0.1.0",
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev"
}
}

View File

Binary file not shown.

Before

Width:  |  Height:  |  Size: 289 KiB

View File

@@ -1,29 +0,0 @@
import type { PluginDefinition } from '@yaakapp/api';
export const plugin: PluginDefinition = {
authentication: {
name: 'basic',
label: 'Basic Auth',
shortLabel: 'Basic',
args: [
{
type: 'text',
name: 'username',
label: 'Username',
optional: true,
},
{
type: 'text',
name: 'password',
label: 'Password',
optional: true,
password: true,
},
],
async onApply(_ctx, { values }) {
const { username, password } = values;
const value = `Basic ${Buffer.from(`${username}:${password}`).toString('base64')}`;
return { setHeaders: [{ name: 'Authorization', value }] };
},
},
};

Some files were not shown because too many files have changed in this diff Show More