Compare commits

..

857 Commits

Author SHA1 Message Date
Gregory Schier
8aeeaa2e09 Bump version 2024-06-12 00:23:50 -07:00
Gregory Schier
57f01d249e Entitlement for v8/Deno 2024-06-12 00:23:32 -07:00
Gregory Schier
6c5a914db6 Variables in pair editor 2024-06-11 12:33:43 -07:00
Gregory Schier
155e51aa74 Put toast on top 2024-06-11 12:04:46 -07:00
Gregory Schier
012a984456 Fix bulk edit label 2024-06-11 09:02:11 -07:00
Gregory Schier
25800202f2 Bump version 2024-06-11 08:35:29 -07:00
Gregory Schier
a058064f1f Adjust fuzzy threshold 2024-06-10 23:25:57 -07:00
Gregory Schier
9f40804532 Fix window drag above cmd+k 2024-06-10 23:24:03 -07:00
Gregory Schier
26cc467858 Fix delete request in sidebar 2024-06-10 23:17:12 -07:00
Gregory Schier
be1cf7bf65 Don't arrow-nav to hidden dropdown items 2024-06-10 23:16:37 -07:00
Gregory Schier
ea4f104ca7 Bulk edit environments 2024-06-10 23:16:21 -07:00
Gregory Schier
32a28a3170 Fix hotkey react keys 2024-06-10 22:29:06 -07:00
Gregory Schier
6215914212 Cmd Palette Improvements (#50)
- Fuzzy matching
- Show hotkeys
- Add actions
2024-06-10 21:37:41 -07:00
Gregory Schier
a2dbd7f849 Download Active Response (#49)
This PR prompts you to save un-previewable file types and adds an option
to save to the response history.
2024-06-10 16:36:09 -07:00
Gregory Schier
5bb9815f4b Remove jump to request hotkey 2024-06-10 09:00:26 -07:00
Gregory Schier
7cd8ac3b21 Response viewer for PDF (#48)
This PR adds a response viewer for PDF files using `react-pdf`
2024-06-10 08:57:08 -07:00
Gregory Schier
456d3aaf52 Don't focus sidebar on cmd+0 2024-06-09 08:36:12 -07:00
Gregory Schier
113743f7cf Try fix CI 2024-06-09 08:09:47 -07:00
Gregory Schier
01a4d6f4ac Bump version 2024-06-09 07:56:44 -07:00
Gregory Schier
ff5cfe744e Remove CI again 2024-06-09 07:56:09 -07:00
Gregory Schier
29c4b51f54 Add it back 2024-06-09 07:23:28 -07:00
Gregory Schier
2f74bf8db8 Remove CI in release stage 2024-06-09 07:22:49 -07:00
Gregory Schier
b30d784d06 Remove GH Action step names 2024-06-09 07:16:53 -07:00
Gregory Schier
ac0adaf3d8 Back to other 2024-06-09 07:14:07 -07:00
Gregory Schier
c246d3a748 Fix action step 2024-06-09 07:10:44 -07:00
Gregory Schier
ae40728c1e Rust cache release 2024-06-09 07:09:46 -07:00
Gregory Schier
0430ec883b Cmd jump to request 2024-06-09 07:03:16 -07:00
Gregory Schier
eba6f33536 Only CI on PR 2024-06-09 06:47:47 -07:00
Gregory Schier
dae2873376 Fix Rust lint 2024-06-09 06:43:53 -07:00
Gregory Schier
cb2f56d9a1 Remove slow udeps from CI 2024-06-08 21:03:17 -07:00
Gregory Schier
7d82aa70a4 Rust CI to release 2024-06-08 20:51:03 -07:00
Gregory Schier
adea234987 Try again 2024-06-08 20:47:48 -07:00
Gregory Schier
37d0b487b8 Remove cache 2024-06-08 20:47:18 -07:00
Gregory Schier
d507f8c99f Try removing git db 2024-06-08 20:44:58 -07:00
Gregory Schier
60406ac83f Cargo check 2024-06-08 20:36:56 -07:00
Gregory Schier
8fe6f3a335 Remove Cargo fmt 2024-06-08 20:34:24 -07:00
Gregory Schier
69e027c302 Fix tests 2024-06-08 20:30:52 -07:00
Gregory Schier
4232bdd298 Cargo format 2024-06-08 20:28:45 -07:00
Gregory Schier
ef1c5da027 Set max dropdown width 2024-06-08 19:55:25 -07:00
Gregory Schier
e250326868 Bump version 2024-06-08 19:46:32 -07:00
Gregory Schier
125f503cfa Upgrade reqwest and add ALPN for http/2 2024-06-08 19:40:35 -07:00
Gregory Schier
8f086425fe Fix curl exporter 2024-06-08 19:40:11 -07:00
Gregory Schier
ae2da73873 Fix cmd+k filtering 2024-06-07 22:52:23 -07:00
Gregory Schier
3cd6688ffb Remove delete hotkey for request 2024-06-07 22:52:10 -07:00
Gregory Schier
8538da8879 Short method tags and hide active in cmd+k 2024-06-07 22:39:11 -07:00
Gregory Schier
b0e4ece278 Add command palette (#46)
This PR finished the initial PoC command palette. It currently only
supports switching between requests and workspaces, but can easily be
extended for more.
2024-06-07 21:59:57 -07:00
Gregory Schier
5e058af03e Bulk editor (#45)
Bulk editor for all pair editors except multipart/form-data
2024-06-07 13:42:08 -07:00
Gregory Schier
5108bc92f3 Fix rose pine and method tags 2024-06-07 12:01:31 -07:00
Gregory Schier
3c5fdcb18d Generate things 2024-06-07 11:58:08 -07:00
Gregory Schier
4672de4a47 Remove tauri-plugin-deep-link 2024-06-07 11:57:44 -07:00
Gregory Schier
239f6da141 Update CI script 2024-06-07 11:01:21 -07:00
Gregory Schier
ec148d1736 Fix workflow 2024-06-07 10:54:50 -07:00
Gregory Schier
392b549646 Deno plugins (#42)
Switch from BoaJS to Deno core
2024-06-07 10:47:41 -07:00
Gregory Schier
993d4dc65d Open workspace pref (#44)
Ability to remember workspace window opening selection.
2024-06-07 09:04:53 -07:00
Gregory Schier
e326405f4f Templating (#43)
Add new `templating` crate with custom parser/renderer for dealing with
variables
2024-06-07 08:39:12 -07:00
Gregory Schier
a83e094f00 Fix copy-as-curl variables 2024-06-05 06:53:38 -07:00
Gregory Schier
e683a2cb2a Bump version 2024-06-04 23:53:45 -07:00
Gregory Schier
1f231c2722 Only release on tag again 2024-06-04 23:53:31 -07:00
Gregory Schier
0fc2575ef6 Fix default workspace settings on initial Workspace 2024-06-04 17:06:40 -07:00
Gregory Schier
44c718e6bc Better analytics error log 2024-06-04 16:27:22 -07:00
Gregory Schier
495df847ab Merge branch 'beta' into master 2024-06-04 15:01:55 -07:00
Gregory Schier
5dbb9852f3 Bump version 2024-06-04 15:00:34 -07:00
Gregory Schier
82d3304c38 Hopefully fix AppImage build not finding migrations. I think it was because the productName had a capital Yaak in it. 2024-06-04 14:59:40 -07:00
Gregory Schier
5e2218fd64 Remove Windows plugin 2024-06-04 02:56:52 -07:00
Gregory Schier
5b12fad173 2024.5.0-beta.2 (#38) 2024-06-03 14:03:25 -07:00
Gregory Schier
8d6f23eacb Bump version 2024-06-03 14:00:49 -07:00
Gregory Schier
75a09859bc Move Postman to first plugin 2024-06-03 14:00:40 -07:00
Gregory Schier
dc47c4ceba Preserve JSON/XPath filter (Closes #22) 2024-06-03 13:49:51 -07:00
Gregory Schier
8ae0290aed Don't send request on completion (Fixes #27) 2024-06-03 07:49:13 -07:00
Gregory Schier
f01d1e704b Don't always change request method. Closes #29 2024-06-03 07:26:40 -07:00
Gregory Schier
3f72996e64 Tweak settings labels 2024-06-02 23:54:00 -07:00
Gregory Schier
fe862517fb Fix dropdown icon color 2024-06-02 23:50:26 -07:00
Gregory Schier
b39335dc4f Don't call is_fullscreen so much 2024-06-02 23:38:56 -07:00
Gregory Schier
8f3bdb5039 Fix autocomplete for environment editor names 2024-06-02 17:52:51 -07:00
Gregory Schier
b47ec01f9c <Select> uses custom component on Windows 2024-06-02 16:57:23 -07:00
Gregory Schier
36728d1d1f Tweak Moonlight theme 2024-06-02 15:48:27 -07:00
Gregory Schier
c3f0351445 Fix font size 2024-06-02 11:53:41 -07:00
Gregory Schier
9a5364187c Better invalid URL errors 2024-06-02 11:53:35 -07:00
Gregory Schier
a9db14994f Import content-type from Postman multi-part form 2024-06-02 11:53:25 -07:00
Gregory Schier
2383e8468f Also release on beta branch 2024-05-31 10:37:21 -07:00
Gregory Schier
a79d485b6a Bump version 2024-05-31 09:46:57 -07:00
Gregory Schier
7eb931d689 Fix multipart files 2024-05-31 09:32:16 -07:00
Gregory Schier
995cd2aa7b Fade in window contents (Layout) 2024-05-31 09:16:52 -07:00
Gregory Schier
1ce50e0c1b Window border on Linux 2024-05-31 08:43:35 -07:00
Gregory Schier
bce3d26a1a Try tweaking settings/theme for Windows. Setting the theme didn't work. 2024-05-30 23:38:49 -07:00
Gregory Schier
d9680ad0fa Ignore stoplights when centering settings title 2024-05-30 12:33:59 -07:00
Gregory Schier
e2e026e1ff Tweak appearance settings 2024-05-30 12:32:12 -07:00
Gregory Schier
16739d9a37 Dracula theme 2024-05-30 11:56:57 -07:00
Gregory Schier
524a4f2275 Tweak 2024-05-30 11:45:40 -07:00
Gregory Schier
ba66883dc2 Theme analytics and Moonlight 2024-05-30 11:00:50 -07:00
Gregory Schier
2caa735a2e Tweak settings for release 2024-05-30 10:28:59 -07:00
Gregory Schier
90637fda6b Unlisten to window event and fix hotkeys 2024-05-30 00:29:01 -07:00
Gregory Schier
2cef46b46a Window title working again 2024-05-30 00:11:55 -07:00
Gregory Schier
14b3abf76c Fix window stoplights 2024-05-29 22:59:34 -07:00
Gregory Schier
8cd3961f87 Custom font sizes and better zoom 2024-05-29 12:10:01 -07:00
Gregory Schier
5eb2e2b5a2 Merge branch 'refs/heads/release'
# Conflicts:
#	src-tauri/src/updates.rs
2024-05-26 09:11:29 -07:00
Gregory Schier
5dd897e042 Initial handler for yaak:// protocol 2024-05-26 08:45:15 -07:00
Gregory Schier
9c77ec296d Better theme export 2024-05-24 18:54:30 -07:00
Gregory Schier
696e72323b More theme stuff 2024-05-24 17:36:48 -07:00
Gregory Schier
3e8c01f436 Tweak themes 2024-05-23 10:16:29 -07:00
Gregory Schier
80fc4dec09 Hotdog stand 2024-05-23 10:14:07 -07:00
Gregory Schier
671885fc8c More themes! 2024-05-23 09:40:11 -07:00
Gregory Schier
002b61f0d7 Set window title on Mac 2024-05-23 09:40:00 -07:00
Gregory Schier
d32b462bd9 More tweaks 2024-05-22 23:44:47 -07:00
Gregory Schier
5c8b47288a Tweak Yaak default themes 2024-05-22 23:28:53 -07:00
Gregory Schier
8e662e6feb A bunch more theme stuff 2024-05-22 23:14:53 -07:00
Gregory Schier
83aaeb94f6 Theme system refactor (#31) 2024-05-21 17:56:06 -07:00
Gregory Schier
8606940dee Move is_dev check for updates 2024-05-16 10:28:25 -07:00
Gregory Schier
60e469a1c9 Fix release (#30) 2024-05-14 15:35:53 -07:00
Gregory Schier
57d548743f Merge remote-tracking branch 'refs/remotes/origin/release'
# Conflicts:
#	src-tauri/tauri.conf.json
2024-05-14 15:35:33 -07:00
Gregory Schier
dab7ee2492 Bump version 2024-05-14 15:34:34 -07:00
Gregory Schier
9360fd7e43 Oops 2024-05-14 15:33:27 -07:00
Gregory Schier
7b4bc53c0f Release 2024.4.1 (#28) 2024-05-14 15:01:55 -07:00
Gregory Schier
0f7969d10a Fix maximize permission 2024-05-14 14:54:09 -07:00
Gregory Schier
6b373b5985 Fix autocomplete 2024-05-14 14:47:33 -07:00
Gregory Schier
c0068a1561 Add num launches to notification call 2024-05-14 14:25:10 -07:00
Gregory Schier
fad5e03f8b Fix window clicking on Linux 2024-05-14 12:19:19 -07:00
Gregory Schier
a22fc68764 Slight refactor 2024-05-14 08:44:15 -07:00
Gregory Schier
59a442e8c0 Build plugin 2024-05-14 08:28:20 -07:00
Gregory Schier
35e8155ecf Improve Curl imports 2024-05-14 08:28:01 -07:00
Gregory Schier
8b9a5cb5fb Bump version for release 2024-05-14 07:50:01 -07:00
Gregory Schier
29f9825f04 Fix upgrade cancel 2024-05-14 07:08:57 -07:00
Gregory Schier
ad8a6beacf Toast after data export 2024-05-14 00:36:15 -07:00
Gregory Schier
194ac78814 Fix selection of HTTP Request on create dropdown hotkey 2024-05-14 00:17:33 -07:00
Gregory Schier
0542f1ebea Fix curl export with multi-line body 2024-05-14 00:05:54 -07:00
Gregory Schier
8de917ea5e Autocomplete URLs of other requests 2024-05-13 23:54:52 -07:00
Gregory Schier
0b494bbfbf Fixed asset:// loading and tweak curl stuff 2024-05-13 23:20:30 -07:00
Gregory Schier
7e74f71c79 Bump version 2024-05-13 16:52:32 -07:00
Gregory Schier
bd7fd676a5 Better notifications 2024-05-13 16:52:20 -07:00
Gregory Schier
cb1c6a4d8c Improve copy-as-curl 2024-05-13 11:30:10 -07:00
Gregory Schier
b640f0c357 Refactor UpdateMode 2024-05-13 07:28:45 -07:00
Gregory Schier
967590c7ff Hide large GRPC messages by default 2024-05-13 07:19:26 -07:00
Gregory Schier
321d6ec9d7 Bump version 2024-05-12 12:11:14 -07:00
Gregory Schier
79b4fd7829 Add curl banner to import dialog 2024-05-10 13:36:30 -07:00
Gregory Schier
52eb4d338f Change curl import to post-toast 2024-05-10 13:06:40 -07:00
Gregory Schier
4b8b48e92f Toast component and use for copy-as-curl 2024-05-10 12:37:04 -07:00
Gregory Schier
e3f45b58e5 Insomnia YAML and loading state on import 2024-05-10 09:46:20 -07:00
Gregory Schier
a5f1922446 Some fixes after upgrading react-query 2024-05-10 09:19:29 -07:00
Gregory Schier
22e7b15a63 Update deps 2024-05-10 08:52:06 -07:00
Gregory Schier
c6bb2a73f8 Fix paste handler in Editor.tsx 2024-05-09 23:17:43 -07:00
Gregory Schier
bde961f95d Fix release targets 2024-05-09 15:38:27 -07:00
Gregory Schier
c4867b3f68 Don't commit .cargo 2024-05-09 10:17:35 -07:00
Gregory Schier
1c36e9a2f0 Fix args 2024-05-09 09:35:39 -07:00
Gregory Schier
1c82d8a738 Update GH action 2024-05-09 09:33:47 -07:00
Gregory Schier
a2df591102 Try fix linux 2024-05-09 08:37:18 -07:00
Gregory Schier
c950ee0fb8 Fix env var in GH Action 2024-05-09 08:29:45 -07:00
Gregory Schier
0ce5a9fcce Fix curl export tests 2024-05-09 08:18:06 -07:00
Gregory Schier
bd1970a805 Default .app/.dev/etc domains to https protos 2024-05-09 08:16:06 -07:00
Gregory Schier
3aaa4b1050 Bump version for beta 2024-05-09 07:52:36 -07:00
Gregory Schier
31d2426846 Fix GRPC event.emit permissions 2024-05-09 07:45:00 -07:00
Gregory Schier
25c4e4edaf Import from Curl 2024-05-09 07:31:52 -07:00
Gregory Schier
1a5bf53b02 Package lock 2024-05-08 15:37:53 -07:00
Gregory Schier
e2d1b62044 Slight refactor to copy-as-curl 2024-05-08 00:28:40 -07:00
Gregory Schier
6186104d11 Add rename request to context menu
Closes #21
2024-05-08 00:08:18 -07:00
Gregory Schier
f1e97eaea4 Copy as curl 2024-05-08 00:00:50 -07:00
Gregory Schier
bb0e58bf8b Curl import (#24) 2024-05-07 21:57:03 -07:00
Gregory Schier
dc802fa055 Upgrade to Tauri 2.0 (#23) 2024-05-04 14:14:19 -07:00
Gregory Schier
3c8e19367f Fix horizontal scroll on GraphQL variables editor 2024-04-18 10:53:36 -07:00
Gregory Schier
a89887eab8 Oops 2024-04-02 10:11:37 +02:00
Gregory Schier
7d3f0ad549 Postman text body import 2024-04-02 10:10:16 +02:00
Gregory Schier
5608db1334 Bump version 2024-04-01 08:48:26 +02:00
Gregory Schier
10443b3c02 Separate floating sidebar hidden state 2024-03-22 10:43:10 -07:00
Gregory Schier
7d272f3cd6 Filtering for cmd palette 2024-03-22 10:42:45 -07:00
Gregory Schier
d60a62ab24 Space between var placeholders and code fold cursor 2024-03-22 10:42:35 -07:00
Gregory Schier
2b0f73b7c5 Fix Postman variable import 2024-03-22 10:40:51 -07:00
Gregory Schier
0915ea8585 Adjust highlight color 2024-03-22 10:37:45 -07:00
Gregory Schier
ed9a0b8ac7 Some scrolling tweaks 2024-03-20 17:27:47 -07:00
Gregory Schier
7437f39ed3 Fix URL bar buttons in expanded state 2024-03-20 16:17:05 -07:00
Gregory Schier
27f80cd97b Bump version 2024-03-20 16:05:14 -07:00
Gregory Schier
04cf688a9b Fix dialog height 2024-03-20 16:05:01 -07:00
Gregory Schier
178af308ce Remove openOnHotKeyAction in favor of putting hotkey on the trigger button= 2024-03-20 15:56:39 -07:00
Gregory Schier
d40732a910 Use SQLite connect options 2024-03-20 13:33:11 -07:00
Gregory Schier
d8a579b7b3 Pass workspace id to import 2024-03-20 07:30:59 -07:00
Gregory Schier
bf8ef40708 Handle import errors 2024-03-20 07:27:12 -07:00
Gregory Schier
41060d5d43 Postman bearer, global auth, global vars 2024-03-20 07:26:46 -07:00
Gregory Schier
ab6cef064c Implement select for command palette 2024-03-19 17:24:57 -07:00
Gregory Schier
829d10d7b9 Tweak checkbox and autocomplete styles 2024-03-19 17:08:06 -07:00
Gregory Schier
57e1f641a7 Fix sidebar border 2024-03-19 16:44:37 -07:00
Gregory Schier
7df965e74b Export multiple workspaces 2024-03-19 13:43:33 -07:00
Gregory Schier
0e1153fdfd Fix settings query store and analytics 2024-03-19 10:23:21 -07:00
Gregory Schier
9797bc1830 Start of command palette 2024-03-18 17:09:01 -07:00
Gregory Schier
9d00eb98d2 useRequests hook 2024-03-18 13:49:36 -07:00
Gregory Schier
88f6e882c6 Fix editor key events 2024-03-18 13:40:15 -07:00
Gregory Schier
230a773909 Simple tests for Postman and Yaak importers 2024-03-18 13:40:00 -07:00
Gregory Schier
e6da1afa82 Set filename on Multipart part 2024-03-18 13:24:27 -07:00
Gregory Schier
a70d9e57be Fix GraphQL editor large variables quirk 2024-03-18 13:10:55 -07:00
Gregory Schier
8afe0c0755 Fix postman import and import Insomnia gRPC 2024-03-18 08:18:04 -07:00
Gregory Schier
e47c2513a8 Better padding 2024-03-16 13:59:06 -07:00
Gregory Schier
276bcfceeb Remove debug log 2024-03-16 12:50:27 -07:00
Gregory Schier
6fd1b35a50 Custom content-type for multipart items 2024-03-16 12:49:17 -07:00
Gregory Schier
bc33244549 Content menu on entire sidebar 2024-03-16 10:47:10 -07:00
Gregory Schier
33c982b288 Obfuscate environment variables 2024-03-16 10:42:46 -07:00
Gregory Schier
98493a1167 Fix light mode text selection 2024-03-16 09:48:55 -07:00
Gregory Schier
a8c10f9601 Stubbed out global commands helper 2024-03-16 09:46:11 -07:00
Gregory Schier
1051f84bbf Fix deleting workspace staying on deleted workspace path 2024-03-15 13:07:02 -07:00
Gregory Schier
eea137e677 Bump version 2024-03-10 18:15:00 -07:00
Gregory Schier
27cf4e925f Fix recent workspaces 2024-03-10 17:42:25 -07:00
Gregory Schier
6b239c4e3f Fix adding header if not exist 2024-03-10 17:10:16 -07:00
Gregory Schier
4c7ee5ef80 Remove completion debug blur thing 2024-03-10 16:46:18 -07:00
Gregory Schier
3ddb79899e Adjust detected content-type header 2024-03-10 16:26:06 -07:00
Gregory Schier
b14595a3e2 Clickable links in response viewer 2024-03-10 13:41:44 -07:00
Gregory Schier
82c94369ae Better error handling for file not found 2024-03-10 11:02:32 -07:00
Gregory Schier
7e77d2e9a8 Cancel file selection sets to undefined 2024-03-10 10:57:49 -07:00
Gregory Schier
ce5ce76b21 Binary file uploads and missing workspace empty state 2024-03-10 10:56:38 -07:00
Gregory Schier
27d6b30cf9 Fix GraphQL Header backend 2024-02-28 13:38:22 -08:00
Gregory Schier
9285dd5d70 Fix GraphQL content type on creation, and placeholder 2024-02-28 13:04:17 -08:00
Gregory Schier
977234808e Include default protoc includes 2024-02-28 09:45:11 -08:00
Gregory Schier
e1ccd327f5 Bump version 2024-02-28 08:51:34 -08:00
Gregory Schier
a0e3e71a4b Fix dropdown arrow keys 2024-02-28 08:51:08 -08:00
Gregory Schier
2d4037da37 Track GRPC 2024-02-28 07:32:05 -08:00
Gregory Schier
d21608bb82 Analytics ID 2024-02-28 07:27:19 -08:00
Gregory Schier
f16239de73 Analytics ID 2024-02-28 07:26:02 -08:00
Gregory Schier
d00f34c240 Remove Escape from hotkeys 2024-02-27 18:58:41 -08:00
Gregory Schier
fd77437f59 Fix sidebar focus 2024-02-27 10:33:20 -08:00
Gregory Schier
f9cd8d24a1 Fix gap in dropdown menu items 2024-02-27 10:27:04 -08:00
Gregory Schier
c0a9f79834 Methods in recent dropdown 2024-02-27 10:20:35 -08:00
Gregory Schier
ca63c101a1 Many hotkey improvements 2024-02-27 10:10:38 -08:00
Gregory Schier
dbd0b46ef2 Info logs in build 2024-02-26 17:27:08 -08:00
Gregory Schier
89793ebe2f Introspection tweak 2024-02-26 17:24:44 -08:00
Gregory Schier
e1ffc387ea Tweak editor find/replace 2024-02-26 17:17:37 -08:00
Gregory Schier
1efd0852ef Fix find/replace CM styling 2024-02-26 17:07:09 -08:00
Gregory Schier
59d532ed4d Fix lint errors 2024-02-26 07:43:08 -08:00
Gregory Schier
f034cda7cd Autocomplete icons and transfer proto files on duplicate 2024-02-26 07:39:53 -08:00
Gregory Schier
5c1cf1e57d Prompt folder name on create 2024-02-26 07:14:27 -08:00
Gregory Schier
780960f5de Better creation from folder menu 2024-02-26 07:09:59 -08:00
Gregory Schier
aada95e9da Auto-fill link to changelog in release script 2024-02-25 18:42:04 -08:00
Gregory Schier
89b5003cce Protoc sidecar 2024-02-25 17:43:29 -08:00
Gregory Schier
3559333461 Fix deletion in sidebar 2024-02-25 12:56:57 -08:00
Gregory Schier
fbb6cab567 Fix create dropdown hotkey 2024-02-24 22:02:04 -08:00
Gregory Schier
30fca29c6d Try changing macOS version 2024-02-24 21:25:58 -08:00
Gregory Schier
ec1e521b39 Bump some things 2024-02-24 21:23:02 -08:00
Gregory Schier
a8998cd696 Fix response pane height 2024-02-24 19:31:59 -08:00
Gregory Schier
df178baed6 Don't build plugins 2024-02-24 18:58:51 -08:00
Gregory Schier
e2f75371e6 Remove npm ci plugins 2024-02-24 18:28:07 -08:00
Gregory Schier
9f25ab39c6 Check in built plugins again 2024-02-24 18:27:36 -08:00
Gregory Schier
937df6a024 Add pkg locks 2024-02-24 18:19:16 -08:00
Gregory Schier
7deb7182a8 Install plugins in CI 2024-02-24 16:29:22 -08:00
Gregory Schier
82b14c56be Bump version 2024-02-24 16:22:55 -08:00
Gregory Schier
58b98734c9 Got json-schema autocomplete working again 2024-02-24 16:22:22 -08:00
Gregory Schier
c2f58f328c Proto files off model 2024-02-24 14:16:58 -08:00
Gregory Schier
36eabe7528 Better gRPC status on error 2024-02-24 12:41:43 -08:00
Gregory Schier
8347093e8b More analytics, and cancel requests 2024-02-24 11:30:07 -08:00
Gregory Schier
75282cb43a Hook up empty state buttons for first-launch experience 2024-02-23 16:34:19 -08:00
Gregory Schier
3a253a758d gRPC in import/export 2024-02-23 16:16:13 -08:00
Gregory Schier
f2955c26c1 Everything in messages now 2024-02-22 19:51:30 -08:00
Gregory Schier
e6c0317b37 Format XML responses 2024-02-22 01:00:02 -08:00
Gregory Schier
0cfb218b07 Refactor into grpc events 2024-02-22 00:49:22 -08:00
Gregory Schier
8ef103fbde Fix split layout placeholder 2024-02-18 09:12:44 -08:00
Gregory Schier
ec5cdfb025 Add other body type 2024-02-18 08:59:14 -08:00
Gregory Schier
7f4d082c17 Add metadata and squash migrations 2024-02-18 08:35:31 -08:00
Gregory Schier
e10011ef34 Better environment edit dialog 2024-02-18 07:44:53 -08:00
Gregory Schier
1eb6999c37 Allow editing base environment 2024-02-18 00:14:47 -08:00
Gregory Schier
35596916bf Render gRPC message body 2024-02-18 00:14:26 -08:00
Gregory Schier
d61b22dd87 gRPC authentication 2024-02-17 23:47:28 -08:00
Gregory Schier
5565a9db9a Even better dropdown filtering 2024-02-17 22:27:01 -08:00
Gregory Schier
b803655306 Started gRPC tabs 2024-02-17 22:15:44 -08:00
Gregory Schier
20df2bf13a Better dropdown filtering 2024-02-17 22:03:42 -08:00
Gregory Schier
e8fab85ce5 Prevent dragging folders into itself 2024-02-17 15:32:15 -08:00
Gregory Schier
ce730f3dbe Fix deps 2024-02-17 15:20:13 -08:00
Gregory Schier
2328973de5 Upgrade Vite deps and fix windows DnD 2024-02-17 14:57:12 -08:00
Gregory Schier
37dea50c91 Fix postman importer TS ref 2024-02-17 11:05:57 -08:00
Gregory Schier
40dfc8b30a Add more info to settings 2024-02-17 11:04:19 -08:00
Gregory Schier
72971bb9ec Adjust placeholder error colors 2024-02-15 21:15:18 -08:00
Gregory Schier
f835599502 Tweak tab padding 2024-02-15 16:38:14 -08:00
Gregory Schier
0caa4f8099 Responsive (scroll) workspace header 2024-02-15 16:30:08 -08:00
Gregory Schier
71d3c9acd1 Better button highlight border 2024-02-15 15:55:32 -08:00
Gregory Schier
239ffa174a Refactor recentRequest/Env/Workspace 2024-02-15 15:14:18 -08:00
Gregory Schier
eae79ab14b Better sidebar collapse, debuonce container uqeries, fix recent requests 2024-02-15 15:07:15 -08:00
Gregory Schier
ef7912615a Better recent work/env/req logic 2024-02-13 17:21:54 -08:00
Gregory Schier
7f20b67380 Fix active environment on workspace change 2024-02-13 16:42:07 -08:00
Gregory Schier
504337c178 Active environment in query param 2024-02-13 16:32:31 -08:00
Gregory Schier
d6b4f06ac8 Better variable placeholder styles 2024-02-13 16:32:17 -08:00
Gregory Schier
28cbf5474d Show GQL for graphql requests in sidebar 2024-02-13 16:32:00 -08:00
Gregory Schier
d797b84d4e Better highlight on JSON tree 2024-02-13 16:31:38 -08:00
Gregory Schier
720745857d Cmd+n to open dropdown 2024-02-11 14:17:09 -08:00
Gregory Schier
a74ea1aeda Tab-to-indent in editor 2024-02-11 14:16:36 -08:00
Gregory Schier
643f5e7f26 Make editor variables more prominent 2024-02-11 14:16:25 -08:00
Gregory Schier
a208b934e4 Fix dropdown open index 2024-02-11 14:16:11 -08:00
Gregory Schier
5892774082 Catch URL error when URL = "{{HOST}}" 2024-02-11 09:04:27 -08:00
Gregory Schier
13bfc1c3bd Grap gRPC status codes 2024-02-11 08:52:12 -08:00
Gregory Schier
00289734c7 Use basemsg 2024-02-11 08:29:57 -08:00
Gregory Schier
09c7c2cb91 Combine grpc handlers, fix duplicate 2024-02-10 10:41:45 -08:00
Gregory Schier
bbe62abd20 Fix response emit and sidebar flex 2024-02-09 16:16:02 -08:00
Gregory Schier
cae9a4fd36 Sidebar methods and fix model hooks 2024-02-09 16:09:24 -08:00
Gregory Schier
e7ee4a8867 Fix editor selection/cursor and lint errors 2024-02-09 14:32:58 -08:00
Gregory Schier
b020574c88 Remove built plugins from source control 2024-02-09 05:09:37 -08:00
Gregory Schier
192e1da5b6 Merge branch 'grpc' 2024-02-09 05:07:45 -08:00
Gregory Schier
0640079e59 Move plugins to build folder 2024-02-09 05:07:34 -08:00
Gregory Schier
a9d99aa17f gRPC Support (#20) 2024-02-09 05:01:00 -08:00
Gregory Schier
b4667e1f88 Better message serialization 2024-02-09 05:00:48 -08:00
Gregory Schier
7bc26fd448 Working sidebar actions for grpc 2024-02-07 00:02:02 -08:00
Gregory Schier
94a9a5d5d5 Change hotkey handling to capture phase 2024-02-06 23:44:10 -08:00
Gregory Schier
bcfa2c411f Fix pool management 2024-02-06 23:26:24 -08:00
Gregory Schier
1e3d43dbae Minor tweaks 2024-02-06 19:32:03 -08:00
Gregory Schier
c6b5e4d5df gRPC schema from files! 2024-02-06 19:20:32 -08:00
Gregory Schier
1293870e11 Proto selection UI/models 2024-02-06 12:29:23 -08:00
Gregory Schier
8309c19167 Better reflect failure UI 2024-02-05 14:50:47 -08:00
Gregory Schier
63a381c55a Async connection management 2024-02-05 11:29:27 -08:00
Gregory Schier
d891f891b7 Refactor model emit, and recent conn dropdown 2024-02-05 10:39:47 -08:00
Gregory Schier
0b12a6b318 db to app_handle! 2024-02-04 22:52:04 -08:00
Gregory Schier
0144ab05a0 Single upserted_model event 2024-02-04 21:19:15 -08:00
Gregory Schier
1f71d4372f Fix DB mutex deadlock 2024-02-04 21:17:05 -08:00
Gregory Schier
5ed1ea07ef More messages 2024-02-04 19:08:31 -08:00
Gregory Schier
4d2b101278 Client streaming working 2024-02-04 17:53:15 -08:00
Gregory Schier
1dfdadde98 Bidirectional working 2024-02-04 14:10:38 -08:00
Gregory Schier
d19729869e Messages are flowing! 2024-02-04 12:09:10 -08:00
Gregory Schier
5f782ad109 Use req/conn/msg models in unary/server 2024-02-04 11:57:12 -08:00
Gregory Schier
27dbdc9b5a Remove console log 2024-02-03 13:39:45 -08:00
Gregory Schier
4892863dd7 Grpc layout use new models 2024-02-03 13:28:31 -08:00
Gregory Schier
dc077209cc Show gRPC requests in sidebar 2024-02-03 13:08:24 -08:00
Gregory Schier
d8d5344d21 gRPC models and tables 2024-02-03 11:14:42 -08:00
Gregory Schier
d148a8384d Tiny fixes 2024-02-02 18:41:00 -08:00
Gregory Schier
6884e9428b Better formatting 2024-02-02 13:37:44 -08:00
Gregory Schier
25ebccfcd7 A bit better handling of responses 2024-02-02 13:32:06 -08:00
Gregory Schier
7adb0cbb50 Split layouts and things 2024-02-02 12:41:37 -08:00
Gregory Schier
50866abda4 bidi hacked! 2024-02-02 01:10:54 -08:00
Gregory Schier
c83d904cf0 Implement cancel 2024-02-02 00:18:37 -08:00
Gregory Schier
160447f8f6 Better connection management 2024-02-01 20:29:32 -08:00
Gregory Schier
184b13cc2a gRPC manager mostly working 2024-02-01 15:36:50 -08:00
Gregory Schier
8fa965e055 Small refactor 2024-02-01 02:42:59 -08:00
Gregory Schier
1dd0b69079 Refactor commands and DB 2024-02-01 02:29:24 -08:00
Gregory Schier
be8dd107e3 Some minor tweaks 2024-02-01 00:48:03 -08:00
Gregory Schier
8f139f10ef Revert response JSON tree 2024-02-01 00:38:57 -08:00
Gregory Schier
8b0823984b Even better styles 2024-02-01 00:36:49 -08:00
Gregory Schier
d82d2229d4 Styled it up a bit 2024-02-01 00:16:09 -08:00
Gregory Schier
4be1bc17f3 Merge remote-tracking branch 'origin/grpc' into grpc 2024-01-31 22:14:15 -08:00
Gregory Schier
e6af0c6009 Hacky server streaming done 2024-01-31 22:14:08 -08:00
Gregory Schier
e5d10bd72b Hacky client streaming done 2024-01-31 22:13:46 -08:00
Gregory Schier
9426885bb8 Initial frontend for gRPC UI 2024-01-30 16:43:54 -08:00
Gregory Schier
89e5d4f235 Hooked up test call from frontend! 2024-01-29 20:50:43 -08:00
Gregory Schier
eecb3fbc7f Fix cookie jar 2024-01-28 17:49:04 -08:00
Gregory Schier
244f1319b4 Track dialogs 2024-01-28 16:33:36 -08:00
Gregory Schier
6a2b76e760 Fix dropdown separator 2024-01-28 16:21:41 -08:00
Gregory Schier
bc0278fce9 Better format 2024-01-28 16:19:46 -08:00
Gregory Schier
959841fb22 More response info 2024-01-28 16:02:49 -08:00
Gregory Schier
fbc878dbe5 Better BG color 2024-01-28 14:43:04 -08:00
Gregory Schier
7d183c6580 Cookie Support (#19) 2024-01-28 14:39:51 -08:00
Gregory Schier
0555420ad9 Add shift to hotkey dialog hotkey 2024-01-19 22:11:20 -08:00
Gregory Schier
ae25561c7e Show alert after force checking updates 2024-01-19 22:11:02 -08:00
Gregory Schier
92e2b2b8f9 Better dialog padding 2024-01-19 22:08:32 -08:00
Gregory Schier
2a4a830fb7 Change env hotkey to use shift 2024-01-19 21:53:48 -08:00
Gregory Schier
0ad4c7cd7e Fix URL 2px grow on focus 2024-01-19 21:49:51 -08:00
Gregory Schier
41413d52ad Bump version 2024-01-19 13:42:02 -08:00
Gregory Schier
eabc1bd305 Fix analytics again 2024-01-18 22:28:25 -08:00
Gregory Schier
9c312e12c1 Fix dialog close button 2024-01-18 20:57:42 -08:00
Gregory Schier
18ea9dda3d Fix URLBar expanded state inner buttons 2024-01-18 20:40:56 -08:00
Gregory Schier
56d4212f68 Some analytics fixes 2024-01-18 20:23:02 -08:00
Gregory Schier
d932c19513 Launch analytics events, changelog, better filter styles 2024-01-18 14:42:02 -08:00
Gregory Schier
b800f00b7e Bump version 2024-01-17 14:56:47 -08:00
Gregory Schier
e43af5234f Custom HTTP method names 2024-01-17 14:52:19 -08:00
Gregory Schier
aa59d96e55 Workspace header tweak Windows 2024-01-17 18:48:43 -08:00
Gregory Schier
5be04ceea6 Fix header in fullscreen mode on Mac 2024-01-17 09:34:47 -08:00
Gregory Schier
da3392ac53 Download response, and some fixes 2024-01-16 17:02:55 -08:00
Gregory Schier
a2c9c98b21 Fix editor toolbar blocking things 2024-01-15 21:44:53 -08:00
Gregory Schier
8298d6e031 Better request creation (Closes #14) 2024-01-15 21:39:27 -08:00
Gregory Schier
d6331022ad XPath plugin 2024-01-15 21:27:47 -08:00
Gregory Schier
5e75d8c9a7 Fix send icon 2024-01-15 15:43:55 -08:00
Gregory Schier
51944a212a Switch to Lucide icons 2024-01-15 15:42:28 -08:00
Gregory Schier
49ed756479 Improve response filter UX 2024-01-15 15:19:29 -08:00
Gregory Schier
1d207d5fbd JSONPath filter plugins working 2024-01-15 15:06:49 -08:00
Gregory Schier
6b1d15415d Move plugin stuff around 2024-01-15 14:33:51 -08:00
Gregory Schier
df3bfaaab7 Fix request duplication 2024-01-15 13:47:44 -08:00
Gregory Schier
ad0b8a8e7d Fix workspace defaults 2024-01-15 12:25:13 -08:00
Gregory Schier
d5459229b9 Better settings dialog 2024-01-15 12:16:44 -08:00
Gregory Schier
adbf596f0b Fix resize observer 2024-01-15 12:02:08 -08:00
Gregory Schier
c740966394 Move request-related settings to workspace 2024-01-15 11:52:36 -08:00
Gregory Schier
7adab73af3 Bump beta version 2024-01-14 20:30:25 -08:00
Gregory Schier
c964f255d8 fix mac decorations 2024-01-14 17:22:31 -08:00
Gregory Schier
a7ffed9716 Integrated titlebar windows 2024-01-14 16:44:04 -08:00
Gregory Schier
488d66d248 Further titlebar tweaks 2024-01-14 12:02:44 -08:00
Gregory Schier
93cb469cb8 Better titlebar control icons 2024-01-14 11:56:21 -08:00
Gregory Schier
03a2fc8ee5 Basic Linux/Windows integrated titlebar 2024-01-13 23:40:32 -08:00
Gregory Schier
2d72e5792e Fix hotkey formatting 2024-01-12 22:12:01 -08:00
Gregory Schier
9bf9a87f12 beta tag 2024-01-12 22:00:55 -08:00
Gregory Schier
e2fca399e0 fix appearance init 2024-01-12 21:59:46 -08:00
Gregory Schier
e13fdddf98 Vendor Openssl 2024-01-12 21:03:28 -08:00
Gregory Schier
890eea299d Hotkeys for request switcher 2024-01-12 21:03:20 -08:00
Gregory Schier
9beac00981 Appearance setting and gzip/etc support 2024-01-12 13:39:08 -08:00
Gregory Schier
1a64d7d9e6 Initial settings implementation 2024-01-11 21:13:17 -08:00
Gregory Schier
bd5ae12f2e Dropdown manages hotkeys now 2024-01-11 10:18:05 -08:00
Gregory Schier
dbaf1da3ce Hotkey for keyboard shortcut help 2024-01-10 22:05:16 -08:00
Gregory Schier
a03c5df440 Bump version number 2024-01-10 16:25:55 -08:00
Gregory Schier
0776f6a2be Add hotkey dialog and rust-only analytics 2024-01-10 16:18:08 -08:00
Gregory Schier
ac9d050d9e Fix beta icon 2024-01-08 17:07:42 -08:00
Gregory Schier
b885c358a3 Bump version to 2024.0.0 2024-01-08 15:57:59 -08:00
Gregory Schier
84d447973e Hotkey labels 2024-01-08 15:57:21 -08:00
Gregory Schier
019ec4de20 Show hotkeys on empty views 2024-01-08 15:13:44 -08:00
Gregory Schier
26189067cd Remove base env, fix hotkeys, and QoL improvements 2024-01-07 22:24:19 -08:00
Gregory Schier
caf39071af Fix hotkeys getting stuck on cmd+tab 2024-01-07 21:32:25 -08:00
Gregory Schier
08a1223482 Always show settings dropdown 2023-11-22 09:39:30 -08:00
Gregory Schier
24bd90745e Tweak margin 2023-11-22 09:37:50 -08:00
Gregory Schier
7a72920e66 Fix bottom-up dropdown positioning 2023-11-22 09:35:56 -08:00
Gregory Schier
81a8276e2b Bump version 2023-11-22 09:06:47 -08:00
Gregory Schier
8fb6f51555 Better linux/Windows support for hotkeys 2023-11-22 09:06:22 -08:00
Gregory Schier
b0026aff66 Good hotkey support 2023-11-22 09:01:48 -08:00
Gregory Schier
3ced7f7c18 Reset URL bar when request changes 2023-11-21 23:26:29 -08:00
Gregory Schier
33f6995193 Env dialog hotkey 2023-11-21 22:35:28 -08:00
Gregory Schier
8af526682a Remove response body and basic hotkeys 2023-11-21 22:15:01 -08:00
Gregory Schier
d0d4324957 Remove app-specific menu items 2023-11-21 19:18:40 -08:00
Gregory Schier
2bcd0e0bbe Fix macOS menu and fallback URL 2023-11-21 09:24:13 -08:00
Gregory Schier
1b99c7e10f Hide menu on windows/linux 2023-11-21 08:17:37 -08:00
Gregory Schier
2831bb61b8 Disable sandboxing (again) 2023-11-19 21:59:55 -08:00
Gregory Schier
320670de2a Fix drag-drop reorder 2023-11-19 21:43:01 -08:00
Gregory Schier
1f39a36f26 Postman ID generation 2023-11-19 20:54:02 -08:00
Gregory Schier
1f84ba716e Bump beta.3 2023-11-19 20:46:55 -08:00
Gregory Schier
d034965d9c Postman variables + urlencoded forms 2023-11-19 20:29:24 -08:00
Gregory Schier
5eb30489e5 Add back Windows/Linux builds 2023-11-19 18:22:13 -08:00
Gregory Schier
05458a0753 Remove Tauri context menu plugin 2023-11-19 18:21:10 -08:00
Gregory Schier
7975ef0699 Fix header padding windows/linux 2023-11-19 18:14:49 -08:00
Gregory Schier
5f810a1b4c Some tweaks 2023-11-19 18:13:32 -08:00
Gregory Schier
c4093e79cf Beta 2 2023-11-19 17:41:58 -08:00
Gregory Schier
956f8ed2ea Network entitlement 2023-11-19 17:41:46 -08:00
Gregory Schier
7b32f76a1e Try sandboxing again 2023-11-19 17:06:31 -08:00
Gregory Schier
0dd11bc051 Remove sandboxing 2023-11-17 09:33:16 -08:00
Gregory Schier
aa38df28af Out of beta 2023-11-17 07:53:26 -08:00
Gregory Schier
32962a6336 Tweak 2023-11-17 07:52:03 -08:00
Gregory Schier
b268b72a4a Format GraphQL variables 2023-11-17 07:51:03 -08:00
Gregory Schier
eb0c90311b Drag into folder (Closes #8) 2023-11-17 07:36:01 -08:00
Gregory Schier
1070bf8e8f Mostly move some stuff around 2023-11-16 18:53:34 -08:00
Gregory Schier
9bdb01987c Better update logic 2023-11-14 14:28:06 -08:00
Gregory Schier
3130fb948a Better dropdown menu 2023-11-14 10:56:56 -08:00
Gregory Schier
e46de9eebd Version to beta 2023-11-14 10:07:39 -08:00
Gregory Schier
4360355c8c Beta channel updates 2023-11-14 08:57:46 -08:00
Gregory Schier
3baaddba0b Multipart form UI and fixes 2023-11-14 00:32:02 -08:00
Gregory Schier
e77c1c2a46 Got multipart working (text-only) 2023-11-13 23:26:11 -08:00
Gregory Schier
38422d59fd Fix window title moving stoplights (for now) 2023-11-13 23:25:47 -08:00
Gregory Schier
c884cedfc2 Postman import form data 2023-11-13 11:48:28 -08:00
Gregory Schier
91074a35d8 Some small improvements 2023-11-13 11:44:29 -08:00
Gregory Schier
9a02b63a6b Form urlencoded bodies! 2023-11-13 11:28:37 -08:00
Gregory Schier
4470409a24 Url parameters done 2023-11-13 10:52:11 -08:00
Gregory Schier
8a978420be Minor tweaks 2023-11-12 21:16:42 -08:00
Gregory Schier
dfc01d51ca Custom updater code 2023-11-12 21:16:33 -08:00
Gregory Schier
758154fa14 Request body is now an object 2023-11-12 11:16:12 -08:00
Gregory Schier
ef23a85577 Some postman import improvements 2023-11-11 10:43:11 -08:00
Gregory Schier
e4533088ed Sync window title (Closes #13) 2023-11-10 15:55:50 -08:00
Gregory Schier
4ffce4a534 Dev app icon 2023-11-10 13:06:12 -08:00
Gregory Schier
6b77a62934 Upgrade sqlx 2023-11-10 12:28:33 -08:00
Gregory Schier
f603867040 Clean up importers 2023-11-10 11:39:17 -08:00
Gregory Schier
b6eb7418aa Fix is_empty check 2023-11-10 09:12:20 -08:00
Gregory Schier
39c97681cf Postman importer semi-complete 2023-11-10 09:08:20 -08:00
Gregory Schier
2d0f0d8f6b Tidy up logs and general cleanup 2023-11-09 20:33:09 -08:00
Gregory Schier
209a767c91 Move error alert to hook 2023-11-09 20:02:42 -08:00
Gregory Schier
71f2a724cb Show import errors 2023-11-09 20:00:19 -08:00
Gregory Schier
4b89b95738 Hacky Yaak import complete! 2023-11-09 19:40:31 -08:00
Gregory Schier
b535722acd More fallback request handing 2023-11-09 17:42:10 -08:00
Gregory Schier
a9806a06a2 Request fallback name in header 2023-11-09 17:34:57 -08:00
Gregory Schier
948e19b82f Better default request names 2023-11-09 17:26:04 -08:00
Gregory Schier
cd841fa13a Better non-named requests 2023-11-09 17:17:03 -08:00
Gregory Schier
3ffcf91abd Better Yaak export 2023-11-09 17:03:29 -08:00
Gregory Schier
9fd84a3bfc Refactor previous commit 2023-11-09 16:50:57 -08:00
Gregory Schier
627c451cd1 Default request name to URL(ish) 2023-11-09 16:47:00 -08:00
Gregory Schier
e6ee89464a Rename initial workspace to "Yaak" 2023-11-09 13:17:21 -08:00
Gregory Schier
136e9f2738 No analytics in dev 2023-11-09 11:46:52 -08:00
Gregory Schier
d97986e526 Analytics event properties 2023-11-09 11:44:59 -08:00
Gregory Schier
7661aa9819 Context menu, logs in DevTools, export, tweaks 2023-11-09 09:28:01 -08:00
Gregory Schier
b7596f3f78 Fix plugin (again) 2023-11-08 13:33:15 -08:00
Gregory Schier
4e2231674c Bundle plugin in Git 2023-11-08 13:06:49 -08:00
Gregory Schier
d36d023a5c Move plugins back 2023-11-08 12:34:14 -08:00
Gregory Schier
3d5a7ebe3d Fix permissions 2023-11-08 12:02:17 -08:00
Gregory Schier
819384e952 Bump version 2023-11-08 10:12:01 -08:00
Gregory Schier
2f34c5e821 Tweak theme 2023-11-08 10:11:29 -08:00
Gregory Schier
0c89c154ee Tweak theme 2023-11-08 09:56:13 -08:00
Gregory Schier
060dce7440 Track screen size, os, and version 2023-11-08 09:49:29 -08:00
Gregory Schier
91707529bd Rust analytics and a few tweaks 2023-11-08 09:13:32 -08:00
Gregory Schier
22f182a8eb Add basic analytics 2023-11-07 09:53:59 -08:00
Gregory Schier
6ccc42dc3f Cancel responses on startup 2023-11-06 13:06:15 -08:00
Gregory Schier
655f5a8eed Send all in a folder 2023-11-06 10:53:39 -08:00
Gregory Schier
cd06a72d6f Rework workspace header 2023-11-06 10:42:59 -08:00
Gregory Schier
0a5d71ecc2 Fix sidebar focus max recursion 2023-11-06 10:40:02 -08:00
Gregory Schier
9c214b619c Save after formatting GraphQL (Closes #9) 2023-11-06 07:20:47 -08:00
Gregory Schier
6fddb727be Move plugins back to root dir 2023-11-06 07:18:53 -08:00
Gregory Schier
b5d3b9a803 Persist sidebar collapsed state (Closes #10) 2023-11-06 07:18:42 -08:00
Gregory Schier
a74c8a94db Vite to bundle insomnia plugin 2023-11-05 22:13:22 -08:00
Gregory Schier
abc6d0ff1e Better import conversion 2023-11-05 14:46:08 -08:00
Gregory Schier
afdbcd0a38 Basic import of request body and bearer auth 2023-11-05 14:35:25 -08:00
Gregory Schier
c31ae805a6 Recursive Insomnia import! 2023-11-05 13:33:23 -08:00
Gregory Schier
7c1afd7fe5 Remove numbers from recent requests 2023-11-04 16:19:21 -07:00
Gregory Schier
ad470a3fd2 Fix drop marker indent 2023-11-04 11:16:41 -07:00
Gregory Schier
7755d06bba Folder actions 2023-11-04 10:48:18 -07:00
Gregory Schier
5aed4b79be Folder chevron icon 2023-11-03 23:10:44 -07:00
Gregory Schier
f0e3f29606 Fix arrow navigation for nested sidebar 2023-11-03 17:29:33 -07:00
Gregory Schier
abbcc525bf Folder-based drag-n-drop complete! 2023-11-03 16:29:21 -07:00
Gregory Schier
ef8ade45b1 Sidebar ordering 95% done! 2023-11-03 15:02:17 -07:00
Gregory Schier
8919d598c2 Nested sidebar ordering almost working 2023-11-03 14:08:46 -07:00
Gregory Schier
e21e42f5fe Add folder model 2023-11-03 07:49:44 -07:00
Gregory Schier
658aed8a29 Even better focus state 2023-11-02 22:23:21 -07:00
Gregory Schier
a666f7d216 Remove focus on env sidebar buttons 2023-11-02 22:18:13 -07:00
Gregory Schier
62429df469 Rearrange menus 2023-11-02 21:41:35 -07:00
Gregory Schier
7aed699c3f Refactor environment edit dialog 2023-11-02 20:38:33 -07:00
Gregory Schier
a14db0ab74 Base environments fully working 2023-11-02 18:43:39 -07:00
Gregory Schier
7b67770dc7 Initial "plugin" system with importer (#7) 2023-11-02 18:08:43 -07:00
Gregory Schier
7766d8439b Fix cursor color in single-line mode 2023-10-31 15:10:33 -07:00
Gregory Schier
fac0683a71 Fix sending of ephemeral requests 2023-10-30 08:24:49 -07:00
Gregory Schier
79e04967f5 Fix GQL introspection and bearer auth templating 2023-10-30 08:07:34 -07:00
Gregory Schier
47eb8947f5 Fix button styles 2023-10-30 07:27:27 -07:00
Gregory Schier
959c55315c Fix editor stale callbacks and recent item deletion 2023-10-30 07:07:14 -07:00
Gregory Schier
b392f0c00f Refactored some core UI 2023-10-30 06:35:52 -07:00
Gregory Schier
c8e674d015 Plugin module loading 2023-10-29 20:50:23 -07:00
Gregory Schier
90fbb81e1d Bump version 2023-10-29 17:22:27 -07:00
Gregory Schier
21e58ca644 Fix sidebar scroll 2023-10-29 17:19:03 -07:00
Gregory Schier
cfb0aa55ea A bit more playing with JS runtime 2023-10-29 17:05:48 -07:00
Gregory Schier
9e0c021481 Initial "Hello World" for plugins 2023-10-29 16:43:28 -07:00
Gregory Schier
4acca8dd06 Fix request creation 2023-10-29 12:05:05 -07:00
Gregory Schier
88eea09428 Restore recent environment on workspace change
Fixes #6
2023-10-29 11:32:55 -07:00
Gregory Schier
90d2743267 Fix var complete and env dialog actions 2023-10-29 11:18:55 -07:00
Gregory Schier
89945532a0 Remove broken key/value enter/backspace logic 2023-10-29 10:45:05 -07:00
Gregory Schier
671860e053 Delete key/value on backspace 2023-10-29 10:26:38 -07:00
Gregory Schier
a87ca6af47 Move workspace menu, better env mgmt, QoL 2023-10-29 09:45:16 -07:00
Gregory Schier
b59ea4991c Bump version 2023-10-28 23:41:58 -07:00
Gregory Schier
b6fd59219f Better listening for path changes 2023-10-28 23:41:24 -07:00
Gregory Schier
38ce7650c1 Bump version 2023-10-28 22:14:51 -07:00
Gregory Schier
2b21e28096 Fixed auto-focus in prompt and env dropdown 2023-10-28 22:14:12 -07:00
Gregory Schier
3206651248 Clear selected sidebar index on drag-drop end 2023-10-28 21:47:00 -07:00
Gregory Schier
ba8aa0e218 Revert debug name 2023-10-28 21:43:09 -07:00
Gregory Schier
975a001635 Fix drag-n-drop things 2023-10-28 21:42:35 -07:00
Gregory Schier
30e7f7ccfe Persist window paths 2023-10-28 21:23:46 -07:00
Gregory Schier
cca8d97d63 Bump version to 2023.1.0 2023-10-28 19:15:33 -07:00
Gregory Schier
455e6c3520 Update placeholders when env changes 2023-10-28 19:14:51 -07:00
Gregory Schier
d05ee3ec16 Placeholder error and fix env nav 2023-10-28 19:08:31 -07:00
Gregory Schier
b1b5d08e89 Recent requests/workspaces. Closes #1 2023-10-28 18:46:54 -07:00
Gregory Schier
6202e59daa Fix recent requests loading on startup 2023-10-28 18:27:18 -07:00
Gregory Schier
c4a8603b81 Add tauri window save state plugin 2023-10-28 13:14:27 -07:00
Gregory Schier
5d15d1565c Fix rustfmt 2023-10-28 12:45:25 -07:00
Gregory Schier
045ff558f8 Handle enabled/disabled variables and render multi 2023-10-28 11:36:40 -07:00
Gregory Schier
504ed583cc Remove unused Variable type 2023-10-28 11:31:45 -07:00
Gregory Schier
15087f2d5a Variables under Environment, and render all props 2023-10-28 11:29:29 -07:00
Gregory Schier
eb1cd1c14b Native Codemirror cursor 2023-10-27 13:14:41 -07:00
Gregory Schier
0918d86654 Only wrap URLBar on focus and hotkey to open recent requests 2023-10-27 12:40:43 -07:00
Gregory Schier
d0e2220df7 Resizing window no longer changes sidebar visibility
Fixes #4
2023-10-27 11:21:59 -07:00
Gregory Schier
ccb04f0b45 Auto-expand URL bar height 2023-10-27 10:57:07 -07:00
Gregory Schier
54b6e1c7c3 Environment deletion and better actions menu 2023-10-26 16:18:47 -07:00
Gregory Schier
7c8acdc956 Stop autocomplete from jumping around 2023-10-26 15:27:48 -07:00
Gregory Schier
3973ae15be Fix request creation from menu 2023-10-26 10:41:14 -07:00
Gregory Schier
86dadf4f5e Send requests with active environment 2023-10-26 10:32:06 -07:00
Gregory Schier
dc4cb4be74 Move create request and fix slow HTML highlighting 2023-10-26 09:42:19 -07:00
Gregory Schier
2a29c4b551 Better project selector, Fixes #2, and a bunch more 2023-10-26 09:11:44 -07:00
Gregory Schier
2f64f45aba Fixed some routing and introspection requests 2023-10-25 21:53:18 -07:00
Gregory Schier
2f998ddfb6 Environments in URL and better rendering 2023-10-25 11:13:00 -07:00
Gregory Schier
93369a779d Move responses dropdown to separate component 2023-10-25 07:59:10 -07:00
Gregory Schier
74e28123a8 Fix dialog height 2023-10-25 00:02:51 -07:00
Gregory Schier
fef7db8710 Better style when no active environment 2023-10-24 23:58:12 -07:00
Gregory Schier
cb5d7626ac Environment dropdown and actions 2023-10-24 09:17:29 -07:00
Gregory Schier
e74f9f33c0 Started on environment edit dialog 2023-10-23 21:00:36 -07:00
Gregory Schier
0d27c17e28 Hacky implementation of variable autocomplete 2023-10-23 10:31:21 -07:00
Gregory Schier
f0b6d32639 Rendered first variable! 2023-10-22 22:30:29 -07:00
Gregory Schier
9ebd506056 Updating environments! 2023-10-22 22:06:51 -07:00
Gregory Schier
a5a91d2444 Update .gitignore 2023-10-22 20:40:00 -07:00
Gregory Schier
4435a66ece Prettier and start of env editor 2023-10-22 20:38:57 -07:00
Gregory Schier
874a1079c3 Environments data model 2023-10-22 18:28:56 -07:00
Gregory Schier
1dc239d243 Environment data model backend 2023-10-22 16:05:09 -07:00
Gregory Schier
73a04276c0 Fix some eslint warnings 2023-10-22 11:02:39 -07:00
Gregory Schier
9955064484 Add APPLE_TEAM_ID 2023-10-18 14:12:08 -07:00
Gregory Schier
7975b528ec Revert artifacts things 2023-10-18 13:25:35 -07:00
Gregory Schier
7d9fbda975 Bump cargo deps 2023-10-18 13:25:20 -07:00
Gregory Schier
fce06747e3 Bump version 2023-10-18 12:14:38 -07:00
Gregory Schier
db0cca54a7 Fix sidebar drag-n-drop 2023-10-18 11:58:58 -07:00
Gregory Schier
4424caecc5 Retry button on introspection errors 2023-06-12 13:20:42 -07:00
Gregory Schier
b866dcd566 Fix autocomplete doc font size 2023-05-31 21:32:48 -07:00
Gregory Schier
9e4e6435ab Persist introspection queries and also improve 2023-05-31 21:29:41 -07:00
Gregory Schier
41b10ff442 Update tauri NPM 2023-05-29 12:49:50 -07:00
Gregory Schier
79f3307104 Fix graphql instrospection 2023-05-29 12:31:34 -07:00
Gregory Schier
026629cd6d Change version 2023-04-27 16:53:39 -07:00
Gregory Schier
cf570a8f88 Version 1.0.0 2023-04-27 16:47:49 -07:00
Gregory Schier
4a64384468 Fix window methods on Linux/Windows 2023-04-27 16:27:02 -07:00
Gregory Schier
a9065c3380 Cross platform window controls 2023-04-27 10:19:49 -07:00
Gregory Schier
e5e5548562 Fix ubuntu build 2023-04-26 17:00:30 -07:00
Gregory Schier
44cf2f670f Windows and Linux 2023-04-26 16:54:51 -07:00
Gregory Schier
d04d91d6dd Bump version 2023-04-26 16:47:50 -07:00
Gregory Schier
3eb25b1507 Very basic CSV viewer 2023-04-22 21:53:04 +08:00
Gregory Schier
a95cae5610 Truncate response files 2023-04-14 14:15:33 -07:00
Gregory Schier
9cc6e62f28 Randomly offset new windows 2023-04-14 14:05:23 -07:00
Gregory Schier
24117f7c8d Fix text encoding and delete responses 2023-04-14 13:50:41 -07:00
Gregory Schier
7245e6e593 Delete response files 2023-04-14 12:17:11 -07:00
Gregory Schier
4a5b1f4da3 Readonly editor disable tabindex 2023-04-13 22:36:11 -07:00
Gregory Schier
c1ac67cc31 Fix imageview padding 2023-04-13 22:33:47 -07:00
Gregory Schier
4085361b7e Duration and size tags 2023-04-13 20:50:17 -07:00
Gregory Schier
8b4227dbff Comment 2023-04-13 18:55:32 -07:00
Gregory Schier
3f7ed9e177 Always store response on filesystem 2023-04-13 18:52:56 -07:00
Gregory Schier
f9f1ba9e24 Support binary responses! 2023-04-13 18:48:40 -07:00
Gregory Schier
29309500a6 Blur backdrop 2023-04-11 16:12:26 -07:00
Gregory Schier
6b07fe105f Autofocus buttons 2023-04-11 14:04:23 -07:00
Gregory Schier
90b03a0b97 Better opening workspaces and redirect workspace to recent request 2023-04-11 11:11:36 -07:00
Gregory Schier
d54a468006 Button disabled style opacity 2023-04-10 16:03:45 -07:00
Gregory Schier
7e98b6d853 Dropdown keys and pointer events 2023-04-10 16:02:29 -07:00
Gregory Schier
8069094201 Upgrade deno core 2023-04-10 11:16:25 -07:00
Gregory Schier
b28dc01e6b Tweak response history 2023-04-09 23:15:51 -07:00
Gregory Schier
6bc1f9f494 Fixed multi-window model sync 2023-04-09 22:32:47 -07:00
Gregory Schier
e85dd32005 Tweak recent requests 2023-04-09 22:25:00 -07:00
Gregory Schier
efc4e3bf6c Toggle settings 2023-04-09 22:12:16 -07:00
Gregory Schier
768a13ff4d Fix sidebar request focus 2023-04-09 22:03:41 -07:00
Gregory Schier
a8d73f74f4 More stuff on sidebar 2023-04-09 21:52:04 -07:00
Gregory Schier
4dddfc0cc5 Small tweaks 2023-04-09 15:32:13 -07:00
Gregory Schier
4163bebe3b Request history navigator 2023-04-09 15:26:54 -07:00
Gregory Schier
ad13744d14 Enter name on create workspace 2023-04-09 12:27:02 -07:00
Gregory Schier
9f9b3a5b21 Rename workspace 2023-04-09 12:23:41 -07:00
Gregory Schier
63e68baeb1 Sidebar hover transitions 2023-04-06 16:30:46 -07:00
Gregory Schier
4382ca6582 Remove some more key value usage 2023-04-06 16:26:56 -07:00
Gregory Schier
99f2f4c211 A bunch of tweaks 2023-04-06 16:05:25 -07:00
Gregory Schier
6b23379e5a macOS 12 2023-04-06 08:39:30 -07:00
Gregory Schier
4deda36e8f Remove system tray icon 2023-04-06 08:15:40 -07:00
Gregory Schier
0ce7831cfb Minor tweaks 2023-04-04 17:21:02 -07:00
Gregory Schier
4e9005e240 Fix some things 2023-04-04 16:56:45 -07:00
Gregory Schier
9147252e5b Editor line wrapping support (not used yet) 2023-04-04 16:40:37 -07:00
Gregory Schier
b6b549ca18 remove janky last location tracking 2023-04-04 16:23:08 -07:00
Gregory Schier
85c25fb71e Fix resize cursor 2023-04-04 16:12:45 -07:00
Gregory Schier
50637ba9fd Better button styles 2023-04-04 15:40:25 -07:00
Gregory Schier
bfe55dd55a Bump version 2023-04-04 13:56:24 -07:00
Gregory Schier
1a2bb3d12e Some small fixes 2023-04-04 13:56:14 -07:00
Gregory Schier
452a0c3ed5 A few fixes 2023-04-04 13:31:48 -07:00
Gregory Schier
b594a4690f Better status tags and delete request on key 2023-04-04 12:36:30 -07:00
Gregory Schier
7d2ba43463 Remove expects from request sending 2023-04-04 08:14:32 -07:00
Gregory Schier
107db42c33 Fix web view height 2023-04-04 07:51:41 -07:00
Gregory Schier
dbacb9fc8d Fix autocomplete spacing 2023-04-04 07:51:19 -07:00
Gregory Schier
1fa3499ca6 Fix input focus border 2023-04-03 12:19:37 -07:00
Gregory Schier
62f3198d27 Hot keys and cleanup 2023-04-03 07:59:49 -07:00
Gregory Schier
529550934b Add entitlemet for v8 2023-04-02 20:23:21 -07:00
Gregory Schier
ca3b22a881 Debug codesigned build 2023-04-02 19:09:14 -07:00
Gregory Schier
0ecf2d9123 Disable code signing 2023-04-02 18:27:14 -07:00
Gregory Schier
af9bb7138b Fix tauri script command 2023-04-02 17:25:24 -07:00
Gregory Schier
6cbfa74f97 Fix universal binary 2023-04-02 17:12:20 -07:00
Gregory Schier
2845fb8d35 Bump version 2023-04-02 15:44:41 -07:00
Gregory Schier
fbe2660a57 Fix toolchain 2023-04-02 15:44:21 -07:00
Gregory Schier
1519282ac6 Add proper target 2023-04-02 15:42:19 -07:00
Gregory Schier
5d8d8dca70 Try universal binary 2023-04-02 15:33:13 -07:00
Gregory Schier
0254e2c31d Fix version 2023-04-02 15:25:24 -07:00
Gregory Schier
368b494d62 Bump version 2023-04-02 14:54:41 -07:00
Gregory Schier
00d2213d05 Notorization (hopefully) 2023-04-02 14:53:49 -07:00
Gregory Schier
b04602bcb9 Change tabs again 2023-04-02 11:11:53 -07:00
Gregory Schier
80bfbd503a Better response headers 2023-04-02 10:45:41 -07:00
Gregory Schier
bfa186aebb Show response headers 2023-04-01 23:43:22 -07:00
Gregory Schier
818595e961 Some more refactoring 2023-04-01 21:48:30 -07:00
Gregory Schier
b5e9852f8d Refactor debounce and tauri event listeners 2023-04-01 21:39:46 -07:00
Gregory Schier
ff3734d65a Fix dropdown and dialog key handling 2023-04-01 21:04:39 -07:00
Gregory Schier
4aa771ba29 Refactor sidebar display 2023-04-01 20:58:53 -07:00
Gregory Schier
5848c381fa Better GraphQL schema fetching 2023-04-01 17:53:36 -07:00
Gregory Schier
03b35beae4 More eslint fixes 2023-04-01 15:48:37 -07:00
Gregory Schier
49856bb6f7 Add React hooks eslint 2023-04-01 15:26:57 -07:00
Gregory Schier
0cb56f1a85 Fix Tauri listeners 2023-04-01 00:02:17 -07:00
Gregory Schier
4a0d698776 Memo editor 2023-03-31 23:19:15 -07:00
Gregory Schier
a4ebdb5736 Fix request duplication 2023-03-31 22:54:32 -07:00
Gregory Schier
6a5ecc2880 Fix tauri listeners causing too many updates 2023-03-31 22:48:34 -07:00
Gregory Schier
7a5bd92442 Remove import 2023-03-31 22:42:41 -07:00
Gregory Schier
63c1111608 Fix 2023-03-31 22:42:26 -07:00
Gregory Schier
5704fb560a Upgrade Deno 2023-03-31 22:42:08 -07:00
Gregory Schier
33dc3b719d Tweak 2023-03-31 16:14:25 -07:00
Gregory Schier
d3329b4628 Fix send hotkey 2023-03-31 16:13:34 -07:00
Gregory Schier
42e2c9f96f Better schema fetching 2023-03-31 16:02:09 -07:00
Gregory Schier
3e8a10757f Finally fix the editor! 2023-03-31 15:56:35 -07:00
Gregory Schier
8028d82fd0 Fix editor blurring bug! 2023-03-31 13:53:28 -07:00
Gregory Schier
4ad9feba68 Better model updates 2023-03-31 13:21:02 -07:00
Gregory Schier
ef469be7a9 Fix text obscuring 2023-03-30 17:22:52 -07:00
Gregory Schier
14cd73d75a Button ring colors 2023-03-30 17:17:07 -07:00
Gregory Schier
fbe6039845 Remove dummy button 2023-03-30 17:12:38 -07:00
Gregory Schier
f3fbd070dd Confirm deletions 2023-03-30 17:09:11 -07:00
Gregory Schier
7c2611a5a7 Dedicated event for model creation 2023-03-30 16:49:49 -07:00
Gregory Schier
ae949f4616 Unify text selection color 2023-03-30 16:36:24 -07:00
Gregory Schier
89da434c0e Some icon tweaks 2023-03-30 16:29:14 -07:00
Gregory Schier
4a98d1d655 Fix strict mode editor blur bug 2023-03-30 10:38:33 -07:00
Gregory Schier
bb41f0e4fe Better tauri listeners and stuff 2023-03-30 09:05:54 -07:00
Gregory Schier
d2e0717d91 Remove updated_by, remember last location 2023-03-30 08:11:51 -07:00
Gregory Schier
7912204fcb Remove hardcoded window config 2023-03-29 22:16:21 -07:00
Gregory Schier
83e41ad618 Fix build 2023-03-29 22:15:55 -07:00
Gregory Schier
dd7e46c2cc Remove unused import 2023-03-29 21:53:49 -07:00
Gregory Schier
fb7424714a Better multi-window updates 2023-03-29 21:53:20 -07:00
Gregory Schier
47481b711e Fixed key/value stuff 2023-03-29 14:46:36 -07:00
Gregory Schier
0f86c3a731 Fix bundle parts 2023-03-29 14:00:34 -07:00
Gregory Schier
b91d1b8b3c Fix(ish) multiwindow updates 2023-03-29 11:15:37 -07:00
Gregory Schier
cd5ae6691c Obscure text 2023-03-29 10:16:51 -07:00
Gregory Schier
0f58986b4c Simple auth schemes 2023-03-29 09:03:38 -07:00
Gregory Schier
af9755c513 Good start to multi-window 2023-03-28 18:29:40 -07:00
Gregory Schier
56ce25f953 Focus traps for dialog and dropdown 2023-03-26 23:07:09 -07:00
Gregory Schier
ed70c15ee9 Confirmation Dialogs 2023-03-26 12:02:20 -07:00
Gregory Schier
d88ae99425 Floating sidebar 2023-03-26 10:09:28 -07:00
Gregory Schier
cf7ef55b7d Panel icons 2023-03-25 23:29:04 -07:00
Gregory Schier
2f12424f8d Update dialog 2023-03-25 21:59:18 -07:00
Gregory Schier
96aacec4fc Optimistically-update key values 2023-03-25 21:54:00 -07:00
Gregory Schier
7e57bb98a8 More tweaks 2023-03-25 21:40:14 -07:00
Gregory Schier
298f5c5a99 Animate dropdown 2023-03-25 21:36:17 -07:00
Gregory Schier
bf44ea7864 Animate sidebar transition 2023-03-25 21:31:52 -07:00
Gregory Schier
9abdc45e93 Refactor and improve layout resizing 2023-03-25 21:16:10 -07:00
Gregory Schier
0d82cc7574 Even better layouts 2023-03-25 18:33:01 -07:00
Gregory Schier
402b2a551f Better grid layouts 2023-03-25 18:12:09 -07:00
Gregory Schier
700c589ae2 Global layout component 2023-03-25 13:26:31 -07:00
Gregory Schier
8929d736d9 Better dropdown separator 2023-03-25 11:06:05 -07:00
Gregory Schier
8c65fce357 Fix sidebar drag 2023-03-24 08:37:52 -07:00
Gregory Schier
b81f1e9e6b Upgrade TYpescript 2023-03-23 15:37:36 -07:00
Gregory Schier
44d083d773 Fix resize 2023-03-23 07:47:58 -07:00
Gregory Schier
2b308282d4 Style tweak 2023-03-21 23:59:09 -07:00
Gregory Schier
c7738743c5 GraphQL autocomplete and duplicate request 2023-03-21 23:54:45 -07:00
Gregory Schier
abc60667c6 Tweak sidebar drag resizer 2023-03-21 19:36:32 -07:00
Gregory Schier
53162e8bca Minor style tweaks 2023-03-21 18:31:05 -07:00
Gregory Schier
6b95574e3d Pull out resize bar 2023-03-21 16:53:49 -07:00
Gregory Schier
c9d62ae961 Resize titlebar and tweak things 2023-03-21 16:42:52 -07:00
Gregory Schier
a70019927d Remove icon generation from build script 2023-03-21 14:21:07 -07:00
Gregory Schier
01cd7f951a query client cache and better body types 2023-03-21 11:38:37 -07:00
Gregory Schier
50f92bcfab Fix workspace deletion 2023-03-21 09:32:15 -07:00
Gregory Schier
362d9f8e59 Use proper gray for syntax 2023-03-20 17:15:12 -07:00
Gregory Schier
0b1cf53942 fix import 2023-03-20 17:13:14 -07:00
Gregory Schier
a4c769b33c Minor tweaks 2023-03-20 17:12:19 -07:00
Gregory Schier
74a1cb61c1 Fix URL bar spacing 2023-03-20 17:01:29 -07:00
Gregory Schier
268545c728 Better radio dropdown type 2023-03-20 16:54:26 -07:00
Gregory Schier
184bbb01c5 Typesafe routing and CM line height issue 2023-03-20 16:47:36 -07:00
Gregory Schier
66fa7ac419 Better tab dropdown handling 2023-03-20 14:14:30 -07:00
Gregory Schier
a80f3d997e Fix request creation priority 2023-03-20 13:56:03 -07:00
Gregory Schier
efa7c24c9f Remove log 2023-03-20 13:49:35 -07:00
Gregory Schier
91de21c7ad Handle "no body" case 2023-03-20 13:49:21 -07:00
Gregory Schier
1e9ba57ef0 Small fix 2023-03-20 13:37:14 -07:00
Gregory Schier
464389b248 Fix pointer window drag 2023-03-20 13:34:49 -07:00
Gregory Schier
107eb72eda Fix extra dropdown element 2023-03-20 13:19:23 -07:00
Gregory Schier
ee7bf838f4 Remove most of Radix UI 2023-03-20 13:16:58 -07:00
Gregory Schier
f63bcd94d1 Better Header validation 2023-03-20 01:38:05 -07:00
Gregory Schier
16d5cb6ade Tweak sidebar 2023-03-20 01:30:45 -07:00
Gregory Schier
63decdef8b Fix pair editor container 2023-03-20 01:18:44 -07:00
Gregory Schier
e54e88f46d Fix input thingy 2023-03-20 01:14:13 -07:00
Gregory Schier
b6f53d059e Container queries! 2023-03-20 01:08:41 -07:00
Gregory Schier
9e1771c5ec Better pair editor delete button 2023-03-20 00:30:42 -07:00
Gregory Schier
8306bc2198 Pair validation 2023-03-20 00:17:29 -07:00
Gregory Schier
3810fb7d51 Don't send disabled headers 2023-03-20 00:05:19 -07:00
Gregory Schier
5b4984113e Small tweak 2023-03-20 00:04:40 -07:00
Gregory Schier
baf9efe246 Pair checkboxes and fix twig indent 2023-03-20 00:03:33 -07:00
Gregory Schier
f4a3109a31 Rewrote twig grammar 2023-03-19 22:12:11 -07:00
Gregory Schier
241f2f39ec Re-order of pair editor 2023-03-19 13:28:57 -07:00
Gregory Schier
d9b40dca83 Better header editor and added completion data 2023-03-19 11:09:21 -07:00
Gregory Schier
cb3f053057 A couple tweaks 2023-03-19 01:01:13 -07:00
Gregory Schier
c4ab045e57 Persist sort priority! 2023-03-19 00:48:09 -07:00
Gregory Schier
d9b38efd97 Create new workspace, and more optimizations 2023-03-18 19:36:31 -07:00
Gregory Schier
5981588c95 Optimized a few components 2023-03-18 18:49:01 -07:00
Gregory Schier
388bef59b8 Fix sidebar drag-n-drop 2023-03-18 18:09:36 -07:00
Gregory Schier
3d8de61c1c Got drag opacity working 2023-03-18 15:06:38 -07:00
Gregory Schier
91b818f98d Good start to drag-n-drop sidebar! 2023-03-18 14:41:07 -07:00
Gregory Schier
3a7f0898f9 Fix mixed parser 2023-03-17 17:57:43 -07:00
Gregory Schier
c2f6de875a Dropdown highlight 2023-03-17 17:32:24 -07:00
Gregory Schier
0647001807 Beginnings of autocomplete for headers 2023-03-17 16:51:20 -07:00
Gregory Schier
4181d87792 Flatten migrations, kvs lib, fix tabs 2023-03-17 08:36:21 -07:00
Gregory Schier
58cf0a2015 Sidebar item dropdown 2023-03-16 15:37:53 -07:00
Gregory Schier
d80c3d305b Sidebar dragging 2023-03-16 14:34:49 -07:00
Gregory Schier
4d64a2bc2f Add devtools toggle hotkey 2023-03-16 11:25:38 -07:00
Gregory Schier
5c54beaaa9 Adjust window sizes 2023-03-16 11:16:23 -07:00
Gregory Schier
eee98f32b2 Store appearance in k/v 2023-03-16 11:01:30 -07:00
Gregory Schier
0949de66bf Hotkeys and view mode kvs 2023-03-16 09:24:28 -07:00
Gregory Schier
5a6acb24d9 Some fixes 2023-03-15 23:33:46 -07:00
Gregory Schier
2e5cab62c7 Got key values working 2023-03-15 23:24:41 -07:00
Gregory Schier
903db5fffd Fix editor padding 2023-03-15 17:29:35 -07:00
Gregory Schier
e3faf32708 Minor style updates 2023-03-15 17:25:04 -07:00
Gregory Schier
97926ddc03 Add stuff to app header 2023-03-15 16:35:19 -07:00
Gregory Schier
e891804051 Better request delete and formatting 2023-03-15 09:41:38 -07:00
Gregory Schier
a2982f8b77 Fix graphql and other things 2023-03-15 09:06:56 -07:00
Gregory Schier
321941baab Header editor to pair editor 2023-03-15 08:09:45 -07:00
Gregory Schier
e2e25dc30b Better header editor 2023-03-15 07:54:04 -07:00
Gregory Schier
1170ca4789 Strict mode and tweak layout padding 2023-03-14 20:19:45 -07:00
Gregory Schier
d159f62138 Add GraphQL variables editor 2023-03-14 19:56:02 -07:00
Gregory Schier
25005eef1b GraphQL query editor transformer works! 2023-03-14 19:08:18 -07:00
Gregory Schier
b17824c88d Add body type to request and tab dropdown 2023-03-14 11:18:56 -07:00
Gregory Schier
00f4a008f8 Improved header editor 2023-03-14 00:54:41 -07:00
Gregory Schier
3e2bc67b59 Fix platform check 2023-03-14 00:15:01 -07:00
Gregory Schier
efe072c7c4 Some small changes 2023-03-14 00:08:03 -07:00
Gregory Schier
59f1d11e40 Move stuff around 2023-03-13 23:30:14 -07:00
Gregory Schier
5f947ac983 Refactor hooks to be easier to use 2023-03-13 23:25:41 -07:00
Gregory Schier
aa66f957f2 Fix header editor and scroll in general 2023-03-13 19:37:36 -07:00
Gregory Schier
cf5f69271f Lazy load routes 2023-03-13 13:56:13 -07:00
Gregory Schier
c6653af782 Back to React 2023-03-13 09:50:49 -07:00
Gregory Schier
fa1f33a2ac Move some stuff around 2023-03-13 09:24:38 -07:00
Gregory Schier
9f479882ad Fix URL bar 2023-03-13 00:13:25 -07:00
Gregory Schier
41db316489 Fix ButtonLink and edit request names 2023-03-13 00:11:23 -07:00
Gregory Schier
50f0f5885e Remove old rust cache action 2023-03-12 22:48:43 -07:00
Gregory Schier
a609f09d50 Better rust cache 2023-03-12 22:47:43 -07:00
Gregory Schier
43f2aa3068 Start GraphQL support 2023-03-12 22:43:25 -07:00
Gregory Schier
a094e13bd5 Fix artifact tag 2023-03-12 21:41:15 -07:00
Gregory Schier
eb076afbe4 Remove wasm stuff 2023-03-12 21:25:31 -07:00
Gregory Schier
7b41488a38 Use tauri action 2023-03-12 21:13:08 -07:00
Gregory Schier
ca12d48352 Fix traffic lights thingy 2023-03-12 20:47:52 -07:00
Gregory Schier
0331d3b2b0 Cache cargo bin for "install" 2023-03-12 19:10:39 -07:00
Gregory Schier
4d4814583c Refformat 2023-03-12 19:03:27 -07:00
Gregory Schier
da3e158516 Fix artifact upload 2023-03-12 19:01:48 -07:00
Gregory Schier
7b9d6baff0 Fix dev 2023-03-12 18:39:02 -07:00
Gregory Schier
550f1b7c6f Split out macos deps 2023-03-12 18:36:25 -07:00
Gregory Schier
74e8ee1786 Cache workflow 2023-03-12 18:28:14 -07:00
Gregory Schier
55d3ea01b6 Add wasm-pack 2023-03-12 18:19:20 -07:00
Gregory Schier
4cfe51cbb2 Install rsw 2023-03-12 18:14:38 -07:00
Gregory Schier
1f5ac60523 Fix artifact names 2023-03-12 18:13:00 -07:00
Gregory Schier
26bf4f2abd Fix build command 2023-03-12 18:11:24 -07:00
Gregory Schier
54671fff79 Update secrets context 2023-03-12 18:07:57 -07:00
Gregory Schier
378309d763 Update workflow name 2023-03-12 18:05:45 -07:00
Gregory Schier
e7d4bba8b3 Fix workflow 2023-03-12 18:05:13 -07:00
Gregory Schier
fe3de0bc98 Start of auto updates 2023-03-12 18:04:11 -07:00
Gregory Schier
f3e38d7b71 Fix tabbing to tabs 2023-03-11 23:32:39 -08:00
Gregory Schier
eafa3b2de9 Fix eslint errors 2023-03-11 23:29:25 -08:00
Gregory Schier
d8cc075bd0 Got tab content scrolling working 2023-03-11 22:36:13 -08:00
Gregory Schier
bdb877a936 Removed some debug stuff 2023-03-10 10:43:15 -08:00
Gregory Schier
5c96e83a22 Refactor editor to update better 2023-03-10 10:39:23 -08:00
Gregory Schier
43abb57f77 Fix headers persistence and better sending 2023-03-09 13:38:17 -08:00
Gregory Schier
9c6d821978 Hook up header editor! 2023-03-09 13:07:13 -08:00
Gregory Schier
7e9babf515 Fix 2023-03-09 10:58:27 -08:00
Gregory Schier
ee36baf432 Fix blur de-select speed 2023-03-09 10:57:34 -08:00
Gregory Schier
0bf57dcab7 Fix Codemirror performance!! 2023-03-09 10:50:55 -08:00
Gregory Schier
e647d23adc Switch to Preact!!! 2023-03-09 00:47:25 -08:00
Gregory Schier
d1b5b9c371 Move some things around 2023-03-08 23:20:15 -08:00
Gregory Schier
92ec514442 Better scrollbar color 2023-03-08 19:23:24 -08:00
Gregory Schier
107466dd58 Zoom, better sizes, color picker, sidebar footer 2023-03-08 19:22:04 -08:00
Gregory Schier
4246260ce6 Read-only editor 2023-03-08 16:53:13 -08:00
Gregory Schier
936787d327 More theme tweaks 2023-03-08 16:37:20 -08:00
Gregory Schier
f976397283 Debounce autocomplete 2023-03-08 11:25:20 -08:00
Gregory Schier
657c6ad9a9 Minor theme updates again 2023-03-08 09:43:35 -08:00
Gregory Schier
ede07c3b0e Editor tweaks 2023-03-07 23:05:33 -08:00
Gregory Schier
9326e8dcce Remove unneeded space 2023-03-07 22:58:13 -08:00
Gregory Schier
8ff2adf833 Fix small view 2023-03-07 22:55:51 -08:00
Gregory Schier
5e387b513a Minor style tweaks 2023-03-07 22:21:58 -08:00
Gregory Schier
dca316c0d5 Lots more theme stuff 2023-03-07 21:52:21 -08:00
Gregory Schier
db2d786d50 Start of themes 2023-03-07 11:24:38 -08:00
Gregory Schier
c0d7962142 More subtle layout tweaks 2023-03-06 08:57:57 -08:00
Gregory Schier
5d14354ca9 More layout fiddling and error page 2023-03-04 22:26:00 -08:00
Gregory Schier
347dace6de More work on the layout 2023-03-04 21:51:17 -08:00
Gregory Schier
d952c75e3c Try new layout and a bunch of editor fixes 2023-03-04 19:06:12 -08:00
Gregory Schier
abc3745be1 Fix autocomplete inside dialog 2023-03-03 17:03:20 -08:00
Gregory Schier
1382d7c523 Beginnings of Header Editor 2023-03-03 13:18:57 -08:00
Gregory Schier
3de0edf0f9 Fix tailwind dark selector 2023-03-03 07:54:19 -08:00
Gregory Schier
5513d39152 Fix content type in URL 2023-03-02 23:17:09 -08:00
Gregory Schier
1a9547d1d2 Add Dialog component 2023-03-02 18:46:10 -08:00
Gregory Schier
26cc64d3a0 More colors 2023-03-02 17:56:53 -08:00
Gregory Schier
e465b33365 Minor style tweaks 2023-03-02 16:16:41 -08:00
Gregory Schier
957739ba5e Improved autocompletion! 2023-03-02 11:14:51 -08:00
Gregory Schier
59967374c5 Rename, fix autocomplete and singleline, etc... 2023-03-02 10:42:43 -08:00
Gregory Schier
43bc346a2b Editor placeholder 2023-03-01 14:22:10 -08:00
Gregory Schier
5fbd3f67cd Some minor bugs 2023-03-01 14:16:02 -08:00
Gregory Schier
e352343d62 Fix migrations for build and iframe rendering 2023-03-01 10:31:50 -08:00
Gregory Schier
baee0f0c6f Tweaks 2023-03-01 10:19:21 -08:00
Gregory Schier
fcc5eead88 Response streaming 2023-03-01 09:05:00 -08:00
Gregory Schier
29d1f687d1 Autocomplete, and more CM stuff! 2023-02-28 22:54:54 -08:00
Gregory Schier
f568266c7f Some small tweaks 2023-02-28 17:25:59 -08:00
Gregory Schier
a1e42b8ddb Better editor updating 2023-02-28 12:41:03 -08:00
Gregory Schier
e6389b1153 URL highlighting with inline CM 2023-02-28 11:26:26 -08:00
Gregory Schier
f0835acb33 Implement request deletion 2023-02-27 15:42:06 -08:00
Gregory Schier
35b04b219f Tauri events for request model updates 2023-02-27 13:28:50 -08:00
Gregory Schier
01b62e936a Split request upsert command 2023-02-27 10:00:57 -08:00
Gregory Schier
09d16a03ef Add toggle for pretty view 2023-02-27 09:08:48 -08:00
Gregory Schier
bb61602fd2 Small tweaks 2023-02-26 15:25:55 -08:00
Gregory Schier
f30b78ea1f Add variable highlighting widgets 2023-02-26 15:06:14 -08:00
Gregory Schier
6ee652ca75 Dropdown scrolling 2023-02-25 23:33:07 -08:00
Gregory Schier
67d8bbc154 Show response body size 2023-02-25 23:08:19 -08:00
Gregory Schier
b852484559 A bunch more small things 2023-02-25 23:04:31 -08:00
Gregory Schier
0b077e5e88 Added react-router 2023-02-25 18:04:14 -08:00
Gregory Schier
381d957db2 Migrations and initial data stuff 2023-02-25 16:39:18 -08:00
Gregory Schier
f8f77abc12 Minor tweaks 2023-02-24 17:01:48 -08:00
Gregory Schier
0b0484c610 Dummy requests in sidebar 2023-02-24 16:46:56 -08:00
Gregory Schier
2b769088af Codemirror initial value support 2023-02-24 16:43:47 -08:00
Gregory Schier
856d13c603 Send request body 2023-02-24 16:09:19 -08:00
Gregory Schier
76e398b8a1 Vendor basicSetup 2023-02-24 14:51:56 -08:00
Gregory Schier
24a7d85be0 Additional methods and tweaks 2023-02-24 14:10:25 -08:00
Gregory Schier
989271f653 Focus states 2023-02-24 12:35:13 -08:00
Gregory Schier
3fd8cd5713 Hook up theme and clear responses 2023-02-24 12:13:30 -08:00
Gregory Schier
43f1c7caf6 SQLite store in proper dir 2023-02-22 20:18:14 -08:00
Gregory Schier
bd91ac88a3 Started on grid layout 2023-02-22 19:44:44 -08:00
Gregory Schier
fbcbf2e5a5 Save responses in DB 2023-02-22 18:53:44 -08:00
Gregory Schier
31eb03da0d Better URL bar 2023-02-22 16:15:25 -08:00
Gregory Schier
a4dbfed712 Cleaner URL bar and some improvements 2023-02-22 15:58:04 -08:00
Gregory Schier
784cb53ec6 Refactor classname usage 2023-02-21 18:03:57 -08:00
Gregory Schier
43c799bd60 Some minor tweaks 2023-02-21 17:56:48 -08:00
1278 changed files with 74101 additions and 90906 deletions

View File

@@ -1,37 +0,0 @@
---
description: Generate formatted release notes for Yaak releases
allowed-tools: Bash(git tag:*)
---
Generate formatted release notes for Yaak releases by analyzing git history and pull request descriptions.
## What to do
1. Identifies the version tag and previous version
2. Retrieves all commits between versions (scoped to stable or beta)
3. Fetches PR descriptions for linked issues to find:
- Feedback URLs (feedback.yaak.app)
- Additional context and descriptions
- Installation links for plugins
4. Formats the release notes using the standard Yaak format:
- Changelog badge at the top
- Bulleted list of changes with PR links
- Feedback links where available
- Full changelog comparison link at the bottom
## Output Format
The skill generates markdown-formatted release notes following this structure:
```markdown
[![Changelog](https://img.shields.io/badge/Changelog-VERSION-blue)](https://yaak.app/changelog/VERSION)
- Feature/fix description in by @username [#123](https://github.com/mountain-loop/yaak/pull/123)
- [Linked feedback item](https://feedback.yaak.app/p/item) by @username in [#456](https://github.com/mountain-loop/yaak/pull/456)
- A simple item that doesn't have a feedback or PR link
**Full Changelog**: https://github.com/mountain-loop/yaak/compare/vPREV...vCURRENT
```
**IMPORTANT**: Always add a blank lines around the markdown code fence and output the markdown code block last
**IMPORTANT**: PRs by `@gschier` should not mention the @username

View File

@@ -1,22 +0,0 @@
# Project Rules
## General Development
- **NEVER** commit or push without explicit confirmation
## Build and Lint
- **ALWAYS** run `npm run lint` after modifying TypeScript or JavaScript files
- Run `npm run bootstrap` after changing plugin runtime or MCP server code
## Plugin System
### Backend Constraints
- Always use `UpdateSource::Plugin` when calling database methods from plugin events
- Never send timestamps (`createdAt`, `updatedAt`) from TypeScript - Rust backend controls these
- Backend uses `NaiveDateTime` (no timezone) so avoid sending ISO timestamp strings
### MCP Server
- MCP server has **no active window context** - cannot call `window.workspaceId()`
- Get workspace ID from `workspaceCtx.yaak.workspace.list()` instead
## Rust Type Generation
- Run `cd src-tauri && cargo test --package yaak-plugins` to regenerate TypeScript bindings after modifying Rust event types

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."

18
.github/workflows/ci-js.yml vendored Normal file
View File

@@ -0,0 +1,18 @@
on:
pull_request:
branches: [develop]
name: CI (JS)
jobs:
test:
name: Lint/Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: lts/*
- run: npm ci
- run: npm run lint
- run: npm test

36
.github/workflows/ci-rust.yml vendored Normal file
View File

@@ -0,0 +1,36 @@
on:
pull_request:
branches: [develop]
paths:
- src-tauri/**
- .github/workflows/**
name: CI (Rust)
defaults:
run:
working-directory: src-tauri
jobs:
test:
name: Check/Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: |
sudo apt-get update
sudo apt-get install -y libwebkit2gtk-4.1-dev
- uses: dtolnay/rust-toolchain@stable
- uses: actions/cache@v3
continue-on-error: false
with:
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- run: cargo check --all
- run: cargo test --all

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

@@ -7,125 +7,69 @@ jobs:
build-artifacts:
permissions:
contents: write
name: Build
strategy:
fail-fast: false
matrix:
include:
- platform: 'macos-latest' # for Arm-based Macs (M1 and above).
- 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.
- 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'
- platform: 'ubuntu-22.04' # for Tauri v1 you could replace this with ubuntu-20.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/checkout@v4
- name: setup node
uses: actions/setup-node@v4
with:
node-version: lts/*
- name: install Rust stable
uses: dtolnay/rust-toolchain@stable
with:
targets: ${{ matrix.targets }}
- uses: Swatinem/rust-cache@v2
# Those targets are only used on macos runners so it's in an `if` to slightly speed up windows and linux builds.
targets: ${{ matrix.platform == 'macos-latest' && 'aarch64-apple-darwin,x86_64-apple-darwin' || '' }}
- uses: actions/cache@v3
continue-on-error: false
with:
workspaces: 'src-tauri'
shared-key: ci
cache-on-failure: true
- name: install dependencies (Linux only)
if: matrix.os == 'ubuntu'
path: |
~/.cargo/bin/
~/.cargo/registry/index/
~/.cargo/registry/cache/
~/.cargo/git/db/
target/
key: ${{ runner.os }}-cargo-${{ hashFiles('**/Cargo.lock') }}
restore-keys: ${{ runner.os }}-cargo-
- name: install dependencies (ubuntu only)
if: matrix.platform == 'ubuntu-22.04' # This must match the platform value defined above.
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
sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf
- name: Install dependencies
run: npm ci
- name: Run lint
run: npm run lint
- name: Run 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 }}
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: '[Changelog __VERSION__](https://yaak.app/blog/__VERSION__)'
releaseBody: 'https://yaak.app/changelog/__VERSION__'
releaseDraft: true
prerelease: true
args: '${{ matrix.args }} --config ./src-tauri/tauri.release.conf.json'
prerelease: false
args: ${{ matrix.args }}

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: '.'

9
.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,9 @@ dist-ssr
*.sln
*.sw?
.eslintcache
out
*.sqlite
*.sqlite-*
.cargo
.tmp
tmp
.zed
codebook.toml
plugins/**/build

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
}

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"
}

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

@@ -4,20 +4,20 @@
<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: #1b1a29;
}
}
}
</style>
</head>
@@ -26,9 +26,6 @@
<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>

19445
package-lock.json generated
View File

File diff suppressed because it is too large Load Diff

View File

@@ -1,101 +1,117 @@
{
"name": "yaak-app",
"name": "yaak",
"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-external/mcp-server",
"plugins-external/template-function-faker",
"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 tauri-dev:desktop",
"tauri-dev:desktop": "tauri dev --no-watch --config ./src-tauri/tauri-dev.conf.json",
"tauri-dev:ios": "tauri ios dev --force-ip-prompt --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:exporter-curl": "cd plugins/exporter-curl && vite build --emptyOutDir",
"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:importer-curl": "cd plugins/importer-curl && 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.2.2",
"@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/react-query": "^5.35.5",
"@tauri-apps/api": ">=2.0.0-beta.0",
"@tauri-apps/plugin-clipboard-manager": "^2.1.0-beta.1",
"@tauri-apps/plugin-dialog": ">=2.0.0-beta.0",
"@tauri-apps/plugin-fs": ">=2.0.0-beta.0",
"@tauri-apps/plugin-os": ">=2.0.0-beta.0",
"@tauri-apps/plugin-shell": ">=2.0.0-beta.0",
"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",
"fast-fuzzy": "^1.12.0",
"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-pdf": "^9.0.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#v2",
"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",
"@tanstack/react-query-devtools": "^5.35.5",
"@tauri-apps/cli": ">=2.0.0-beta.0",
"@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",
"internal-ip": "^8.0.0",
"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.4.5",
"vite": "^5.0.0",
"vite-plugin-static-copy": "^1.0.5",
"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,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,82 +0,0 @@
// This file was generated by [ts-rs](https://github.com/Aleph-Alpha/ts-rs). Do not edit this file manually.
export type AnyModel = CookieJar | Environment | Folder | GraphQlIntrospection | GrpcConnection | GrpcEvent | GrpcRequest | HttpRequest | HttpResponse | HttpResponseEvent | KeyValue | Plugin | Settings | SyncState | WebsocketConnection | WebsocketEvent | WebsocketRequest | Workspace | WorkspaceMeta;
export type ClientCertificate = { host: string, port: number | null, crtFile: string | null, keyFile: string | null, pfxFile: string | null, passphrase: string | null, enabled?: boolean, };
export type Cookie = { raw_cookie: string, domain: CookieDomain, expires: CookieExpires, path: [string, boolean], };
export type CookieDomain = { "HostOnly": string } | { "Suffix": string } | "NotPresent" | "Empty";
export type CookieExpires = { "AtUtc": string } | "SessionEnd";
export type CookieJar = { model: "cookie_jar", id: string, createdAt: string, updatedAt: string, workspaceId: string, cookies: Array<Cookie>, name: string, };
export type EditorKeymap = "default" | "vim" | "vscode" | "emacs";
export type EncryptedKey = { encryptedKey: string, };
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 GraphQlIntrospection = { model: "graphql_introspection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, content: string | null, };
export type GrpcConnection = { model: "grpc_connection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, elapsed: number, error: string | null, method: string, service: string, status: number, state: GrpcConnectionState, trailers: { [key in string]?: string }, url: string, };
export type GrpcConnectionState = "initialized" | "connected" | "closed";
export type GrpcEvent = { model: "grpc_event", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, connectionId: string, content: string, error: string | null, eventType: GrpcEventType, metadata: { [key in string]?: string }, status: number | null, };
export type GrpcEventType = "info" | "error" | "client_message" | "server_message" | "connection_start" | "connection_end";
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 HttpResponseEvent = { model: "http_response_event", id: string, createdAt: string, updatedAt: string, workspaceId: string, responseId: string, event: HttpResponseEventData, };
/**
* Serializable representation of HTTP response events for DB storage.
* This mirrors `yaak_http::sender::HttpResponseEvent` but with serde support.
* The `From` impl is in yaak-http to avoid circular dependencies.
*/
export type HttpResponseEventData = { "type": "setting", name: string, value: string, } | { "type": "info", message: string, } | { "type": "redirect", url: string, status: number, behavior: string, } | { "type": "send_url", method: string, path: string, } | { "type": "receive_url", version: string, status: string, } | { "type": "header_up", name: string, value: string, } | { "type": "header_down", name: string, value: string, } | { "type": "chunk_sent", bytes: number, } | { "type": "chunk_received", bytes: number, };
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 KeyValue = { model: "key_value", id: string, createdAt: string, updatedAt: string, key: string, namespace: string, value: string, };
export type Plugin = { model: "plugin", id: string, createdAt: string, updatedAt: string, checkedAt: string | null, directory: string, enabled: boolean, url: string | null, };
export type ProxySetting = { "type": "enabled", http: string, https: string, auth: ProxySettingAuth | null, bypass: string, disabled: boolean, } | { "type": "disabled" };
export type ProxySettingAuth = { user: string, password: string, };
export type Settings = { model: "settings", id: string, createdAt: string, updatedAt: string, appearance: string, clientCertificates: Array<ClientCertificate>, coloredMethods: boolean, editorFont: string | null, editorFontSize: number, editorKeymap: EditorKeymap, editorSoftWrap: boolean, hideWindowControls: boolean, useNativeTitlebar: boolean, interfaceFont: string | null, interfaceFontSize: number, interfaceScale: number, openWorkspaceNewWindow: boolean | null, proxy: ProxySetting | null, themeDark: string, themeLight: string, updateChannel: string, hideLicenseBadge: boolean, autoupdate: boolean, autoDownloadUpdates: boolean, checkNotifications: boolean, };
export type SyncState = { model: "sync_state", id: string, workspaceId: string, createdAt: string, updatedAt: string, flushedAt: string, modelId: string, checksum: string, relPath: string, syncDir: string, };
export type WebsocketConnection = { model: "websocket_connection", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, elapsed: number, error: string | null, headers: Array<HttpResponseHeader>, state: WebsocketConnectionState, status: number, url: string, };
export type WebsocketConnectionState = "initialized" | "connected" | "closing" | "closed";
export type WebsocketEvent = { model: "websocket_event", id: string, createdAt: string, updatedAt: string, workspaceId: string, requestId: string, connectionId: string, isServer: boolean, message: Array<number>, messageType: WebsocketEventType, };
export type WebsocketEventType = "binary" | "close" | "frame" | "open" | "ping" | "pong" | "text";
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, };
export type WorkspaceMeta = { model: "workspace_meta", id: string, workspaceId: string, createdAt: string, updatedAt: string, encryptionKey: EncryptedKey | null, settingSyncDir: string | null, };

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,95 +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,
WorkspaceInfo,
} from '../bindings/gen_events.ts';
import type { HttpRequest } from '../bindings/gen_models.ts';
import type { JsonValue } from '../bindings/serde_json/JsonValue';
export type WorkspaceHandle = Pick<WorkspaceInfo, 'id' | 'name'>;
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']>;
create(
args: Omit<Partial<HttpRequest>, 'id' | 'model' | 'createdAt' | 'updatedAt'> &
Pick<HttpRequest, 'workspaceId' | 'url'>,
): Promise<HttpRequest>;
update(
args: Omit<Partial<HttpRequest>, 'model' | 'createdAt' | 'updatedAt'> &
Pick<HttpRequest, 'id'>,
): Promise<HttpRequest>;
delete(args: { id: string }): Promise<HttpRequest>;
};
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;
};
workspace: {
list(): Promise<WorkspaceHandle[]>;
withContext(handle: WorkspaceHandle): Context;
};
}

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,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,861 +0,0 @@
import { applyFormInputDefaults, validateTemplateFunctionArgs } from '@yaakapp-internal/lib/templateFunction';
import {
BootRequest,
DeleteKeyValueResponse,
DeleteModelResponse,
FindHttpResponsesResponse,
GetCookieValueRequest,
GetCookieValueResponse,
GetHttpRequestByIdResponse,
GetKeyValueResponse,
GrpcRequestAction,
HttpAuthenticationAction,
HttpRequest,
HttpRequestAction,
InternalEvent,
InternalEventPayload,
ListCookieNamesResponse,
ListWorkspacesResponse,
PluginContext,
PromptTextResponse,
RenderGrpcRequestResponse,
RenderHttpRequestResponse,
SendHttpRequestResponse,
TemplateFunction,
TemplateRenderResponse,
UpsertModelResponse,
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[];
},
create: async (args) => {
const payload = {
type: 'upsert_model_request',
model: {
name: '',
method: 'GET',
...args,
id: '',
model: 'http_request',
},
} as InternalEventPayload;
const response = await this.#sendForReply<UpsertModelResponse>(context, payload);
return response.model as HttpRequest;
},
update: async (args) => {
const payload = {
type: 'upsert_model_request',
model: {
model: 'http_request',
...args,
},
} as InternalEventPayload;
const response = await this.#sendForReply<UpsertModelResponse>(context, payload);
return response.model as HttpRequest;
},
delete: async (args) => {
const payload = {
type: 'delete_model_request',
model: 'http_request',
id: args.id,
} as InternalEventPayload;
const response = await this.#sendForReply<DeleteModelResponse>(context, payload);
return response.model as HttpRequest;
},
},
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);
},
},
workspace: {
list: async () => {
const payload = {
type: 'list_workspaces_request'
} as InternalEventPayload;
const response = await this.#sendForReply<ListWorkspacesResponse>(context, payload);
return response.workspaces.map((w) => ({
id: w.id,
name: w.name,
// Hide label from plugin authors, but keep it for internal routing
_label: (w as any).label as string,
}));
},
withContext: (workspaceHandle: { id: string; name: string; _label?: string }) => {
// Create a new context with the workspace's window label
const newContext: PluginContext = {
...context,
label: workspaceHandle._label || null,
workspaceId: workspaceHandle.id,
};
return this.#newCtx(newContext);
},
},
};
}
}
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,151 +0,0 @@
import { applyFormInputDefaults } from '@yaakapp-internal/lib/templateFunction';
import { CallTemplateFunctionArgs } from '@yaakapp-internal/plugins';
import { Context, DynamicTemplateFunctionArg } from '@yaakapp/api';
import { describe, expect, test } from 'vitest';
import { applyDynamicFormInput } 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"
]
}

View File

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

View File

@@ -1,76 +0,0 @@
# Yaak Faker Plugin
This is a template function that generates realistic fake data
for testing and development using [FakerJS](https://fakerjs.dev).
![CleanShot 2024-09-19 at 13 56 33@2x](https://github.com/user-attachments/assets/2f935110-4af2-4236-a50d-18db5454176d)
## Example JSON Body
Here's an example JSON body that uses fake data:
```json
{
"id": "${[ faker.string.uuid() ]}",
"name": "${[ faker.person.fullName() ]}",
"email": "${[ faker.internet.email() ]}",
"phone": "${[ faker.phone.number() ]}",
"address": {
"street": "${[ faker.location.streetAddress() ]}",
"city": "${[ faker.location.city() ]}",
"country": "${[ faker.location.country() ]}",
"zipCode": "${[ faker.location.zipCode() ]}"
},
"company": "${[ faker.company.name() ]}",
"website": "${[ faker.internet.url() ]}"
}
```
This will generate a random JSON body on every request:
```json
{
"id": "589f0aec-7310-4bf2-81c4-0b1bb7f1c3c1",
"name": "Lucy Gottlieb-Weissnat",
"email": "Destiny_Herzog@gmail.com",
"phone": "411.805.2871 x699",
"address": {
"street": "846 Christ Mills",
"city": "Spencerfurt",
"country": "United Kingdom",
"zipCode": "20354"
},
"company": "Emard, Kohler and Rutherford",
"website": "https://watery-detective.org"
}
```
## Available Categories
The plugin provides access to all FakerJS modules and their methods:
| Category | Description | Example Methods |
|------------|---------------------------|--------------------------------------------|
| `airline` | Airline-related data | `aircraftType`, `airline`, `airplane` |
| `animal` | Animal names and types | `bear`, `bird`, `cat`, `dog`, `fish` |
| `color` | Colors in various formats | `human`, `rgb`, `hex`, `hsl` |
| `commerce` | E-commerce data | `department`, `product`, `price` |
| `company` | Company information | `name`, `catchPhrase`, `bs` |
| `database` | Database-related data | `column`, `type`, `collation` |
| `date` | Date and time values | `recent`, `future`, `past`, `between` |
| `finance` | Financial data | `account`, `amount`, `currency` |
| `git` | Git-related data | `branch`, `commitEntry`, `commitSha` |
| `hacker` | Tech/hacker terminology | `abbreviation`, `noun`, `phrase` |
| `image` | Image URLs and data | `avatar`, `url`, `dataUri` |
| `internet` | Internet-related data | `email`, `url`, `ip`, `userAgent` |
| `location` | Geographic data | `city`, `country`, `latitude`, `longitude` |
| `lorem` | Lorem ipsum text | `word`, `sentence`, `paragraph` |
| `person` | Personal information | `firstName`, `lastName`, `fullName` |
| `music` | Music-related data | `genre`, `songName`, `artist` |
| `number` | Numeric data | `int`, `float`, `binary`, `hex` |
| `phone` | Phone numbers | `number`, `imei` |
| `science` | Scientific data | `chemicalElement`, `unit` |
| `string` | String utilities | `uuid`, `alpha`, `alphanumeric` |
| `system` | System-related data | `fileName`, `mimeType`, `fileExt` |
| `vehicle` | Vehicle information | `vehicle`, `manufacturer`, `model` |
| `word` | Word generation | `adjective`, `adverb`, `conjunction` |

View File

@@ -1,23 +0,0 @@
{
"name": "@yaak/faker",
"private": true,
"version": "0.1.0",
"displayName": "Faker",
"description": "Template functions for generating fake data using FakerJS",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins-external/faker"
},
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev"
},
"dependencies": {
"@faker-js/faker": "^10.1.0"
},
"devDependencies": {
"@types/node": "^25.0.3",
"typescript": "^5.9.3"
}
}

View File

@@ -1,106 +0,0 @@
import { faker } from '@faker-js/faker';
import type { PluginDefinition, TemplateFunctionArg } from '@yaakapp/api';
const modules = [
'airline',
'animal',
'color',
'commerce',
'company',
'database',
'date',
'finance',
'git',
'hacker',
'image',
'internet',
'location',
'lorem',
'person',
'music',
'number',
'phone',
'science',
'string',
'system',
'vehicle',
'word',
];
function normalizeResult(result: unknown): string {
if (typeof result === 'string') return result;
return JSON.stringify(result);
}
// Whatever Yaaks arg type shape is rough example
function args(modName: string, fnName: string): TemplateFunctionArg[] {
return [
{
type: 'banner',
color: 'info',
inputs: [
{
type: 'markdown',
content: `Need help? View documentation for [\`${modName}.${fnName}(…)\`](https://fakerjs.dev/api/${encodeURIComponent(modName)}.html#${encodeURIComponent(fnName)})`,
},
],
},
{
name: 'options',
label: 'Arguments',
type: 'editor',
language: 'json',
optional: true,
placeholder: 'e.g. { "min": 1, "max": 10 } or 10 or ["en","US"]',
},
];
}
export const plugin: PluginDefinition = {
templateFunctions: modules.flatMap((modName) => {
// @ts-expect-error - Dynamic access to faker modules
const mod = faker[modName];
return Object.keys(mod)
.filter((n) => n !== 'faker')
.map((fnName) => ({
name: ['faker', modName, fnName].join('.'),
args: args(modName, fnName),
async onRender(_ctx, args) {
const fn = mod[fnName] as (...a: unknown[]) => unknown;
const options = args.values.options;
// No options supplied
if (options == null || options === '') {
return normalizeResult(fn());
}
// Try JSON first
let parsed: unknown = options;
if (typeof options === 'string') {
try {
parsed = JSON.parse(options);
} catch {
// Not valid JSON maybe just a scalar
const n = Number(options);
if (!Number.isNaN(n)) {
parsed = n;
} else {
parsed = options;
}
}
}
let result: unknown;
if (Array.isArray(parsed)) {
// Treat as positional arguments
result = fn(...parsed);
} else {
// Treat as a single argument (option object or scalar)
result = fn(parsed);
}
return normalizeResult(result);
},
}));
}),
};

View File

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

View File

@@ -1,35 +0,0 @@
# Yaak MCP Server Plugin
Exposes Yaak's functionality via the [Model Context Protocol (MCP)](https://modelcontextprotocol.io/).
## Setup
Add this to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
```json
{
"mcpServers": {
"yaak": {
"command": "npx",
"args": ["-y", "mcp-remote", "http://127.0.0.1:64343/mcp"]
}
}
}
```
Restart Claude Desktop and make sure Yaak is running.
## Available Tools
- `list_http_requests` - List all HTTP requests in a workspace
- `get_http_request` - Get details of a specific HTTP request
- `send_http_request` - Send an HTTP request and get the response
- `create_http_request` - Create a new HTTP request
- `update_http_request` - Update an existing HTTP request
- `delete_http_request` - Delete an HTTP request
- `list_folders` - List all folders in a workspace
- `list_workspaces` - List all open workspaces
- `get_workspace_id` - Get the current workspace ID
- `get_environment_id` - Get the current environment ID
- `copy_to_clipboard` - Copy text to the system clipboard
- `show_toast` - Show a toast notification in Yaak

View File

@@ -1,28 +0,0 @@
{
"name": "@yaak/mcp-server",
"private": true,
"version": "0.1.3",
"displayName": "MCP Server",
"description": "Expose Yaak functionality via Model Context Protocol",
"minYaakVersion": "2025.10.0-beta.6",
"repository": {
"type": "git",
"url": "https://github.com/mountain-loop/yaak.git",
"directory": "plugins-external/mcp-server"
},
"scripts": {
"build": "yaakcli build",
"dev": "yaakcli dev"
},
"dependencies": {
"@hono/mcp": "^0.2.3",
"@hono/node-server": "^1.19.7",
"@modelcontextprotocol/sdk": "^1.25.1",
"hono": "^4.11.3",
"zod": "^3.25.76"
},
"devDependencies": {
"@types/node": "^25.0.3",
"typescript": "^5.9.3"
}
}

View File

@@ -1,25 +0,0 @@
import type { Context, PluginDefinition } from '@yaakapp/api';
import { createMcpServer } from './server.js';
const serverPort = 64343;
let mcpServer: Awaited<ReturnType<typeof createMcpServer>> | null = null;
export const plugin: PluginDefinition = {
async init(ctx: Context) {
// Start the server after waiting, so there's an active window open to do things
// like show the startup toast.
setTimeout(() => {
mcpServer = createMcpServer({ yaak: ctx }, serverPort);
}, 5000);
},
async dispose() {
console.log('Disposing MCP Server plugin');
if (mcpServer) {
await mcpServer.close();
mcpServer = null;
}
},
};

View File

@@ -1,58 +0,0 @@
import { serve } from '@hono/node-server';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { WebStandardStreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/webStandardStreamableHttp.js';
import { Hono } from 'hono';
import { registerFolderTools } from './tools/folder.js';
import { registerHttpRequestTools } from './tools/httpRequest.js';
import { registerToastTools } from './tools/toast.js';
import { registerWindowTools } from './tools/window.js';
import { registerWorkspaceTools } from './tools/workspace.js';
import type { McpServerContext } from './types.js';
export function createMcpServer(ctx: McpServerContext, port: number) {
const server = new McpServer({
name: 'yaak-mcp-server',
version: '0.1.0',
});
// Register all tools
registerToastTools(server, ctx);
registerHttpRequestTools(server, ctx);
registerFolderTools(server, ctx);
registerWindowTools(server, ctx);
registerWorkspaceTools(server, ctx);
// Create a stateless transport
const transport = new WebStandardStreamableHTTPServerTransport();
// Create Hono app
const app = new Hono();
// MCP endpoint - reuse the same transport for all requests
app.all('/mcp', (c) => transport.handleRequest(c.req.raw));
// Connect server to transport
server.connect(transport).then(() => {
console.log(`MCP Server running at http://127.0.0.1:${port}/mcp`);
ctx.yaak.toast.show({
message: `MCP Server running on port ${port}`,
icon: 'check_circle',
color: 'success',
});
});
// Start the HTTP server
const honoServer = serve({
fetch: app.fetch,
port,
hostname: '127.0.0.1',
});
return {
server,
close: async () => {
honoServer.close();
await server.close();
},
};
}

View File

@@ -1,33 +0,0 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod/v4';
import type { McpServerContext } from '../types.js';
import { getWorkspaceContext } from './helpers.js';
export function registerFolderTools(server: McpServer, ctx: McpServerContext) {
server.registerTool(
'list_folders',
{
title: 'List Folders',
description: 'List all folders in a workspace',
inputSchema: z.object({
workspaceId: z
.string()
.optional()
.describe('Workspace ID (required if multiple workspaces are open)'),
}),
},
async ({ workspaceId }) => {
const workspaceCtx = await getWorkspaceContext(ctx, workspaceId);
const folders = await workspaceCtx.yaak.folder.list();
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(folders, null, 2),
},
],
};
},
);
}

View File

@@ -1,32 +0,0 @@
import type { McpServerContext } from '../types.js';
export async function getWorkspaceContext(
ctx: McpServerContext,
workspaceId?: string,
): Promise<McpServerContext> {
const workspaces = await ctx.yaak.workspace.list();
if (!workspaceId && workspaces.length > 1) {
const workspaceList = workspaces.map((w, i) => `${i + 1}. ${w.name} (ID: ${w.id})`).join('\n');
throw new Error(
`Multiple workspaces are open. Please specify which workspace to use.\n\n` +
`Currently open workspaces:\n${workspaceList}\n\n` +
`You can use the list_workspaces tool to get workspace IDs, then use other tools ` +
`with the workspace context. For example, ask the user which workspace they want ` +
`to work with by presenting them with the numbered list above.`,
);
}
const workspace = workspaceId ? workspaces.find((w) => w.id === workspaceId) : workspaces[0];
if (!workspace) {
const workspaceList = workspaces.map((w) => `- ${w.name} (ID: ${w.id})`).join('\n');
throw new Error(
`Workspace with ID "${workspaceId}" not found.\n\n` +
`Available workspaces:\n${workspaceList}`,
);
}
return {
yaak: ctx.yaak.workspace.withContext(workspace),
};
}

View File

@@ -1,219 +0,0 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod/v4';
import type { McpServerContext } from '../types.js';
import { getWorkspaceContext } from './helpers.js';
export function registerHttpRequestTools(server: McpServer, ctx: McpServerContext) {
server.registerTool(
'list_http_requests',
{
title: 'List HTTP Requests',
description: 'List all HTTP requests in a workspace',
inputSchema: z.object({
workspaceId: z
.string()
.optional()
.describe('Workspace ID (required if multiple workspaces are open)'),
}),
},
async ({ workspaceId }) => {
const workspaceCtx = await getWorkspaceContext(ctx, workspaceId);
const requests = await workspaceCtx.yaak.httpRequest.list();
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(requests, null, 2),
},
],
};
},
);
server.registerTool(
'get_http_request',
{
title: 'Get HTTP Request',
description: 'Get details of a specific HTTP request by ID',
inputSchema: z.object({
id: z.string().describe('The HTTP request ID'),
workspaceId: z
.string()
.optional()
.describe('Workspace ID (required if multiple workspaces are open)'),
}),
},
async ({ id, workspaceId }) => {
const workspaceCtx = await getWorkspaceContext(ctx, workspaceId);
const request = await workspaceCtx.yaak.httpRequest.getById({ id });
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(request, null, 2),
},
],
};
},
);
server.registerTool(
'send_http_request',
{
title: 'Send HTTP Request',
description: 'Send an HTTP request and get the response',
inputSchema: z.object({
id: z.string().describe('The HTTP request ID to send'),
environmentId: z.string().optional().describe('Optional environment ID to use'),
workspaceId: z
.string()
.optional()
.describe('Workspace ID (required if multiple workspaces are open)'),
}),
},
async ({ id, workspaceId }) => {
const workspaceCtx = await getWorkspaceContext(ctx, workspaceId);
const httpRequest = await workspaceCtx.yaak.httpRequest.getById({ id });
if (httpRequest == null) {
throw new Error(`HTTP request with ID ${id} not found`);
}
const response = await workspaceCtx.yaak.httpRequest.send({ httpRequest });
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(response, null, 2),
},
],
};
},
);
server.registerTool(
'create_http_request',
{
title: 'Create HTTP Request',
description: 'Create a new HTTP request',
inputSchema: z.object({
workspaceId: z
.string()
.optional()
.describe('Workspace ID (required if multiple workspaces are open)'),
name: z
.string()
.optional()
.describe('Request name (empty string to auto-generate from URL)'),
url: z.string().describe('Request URL'),
method: z.string().optional().describe('HTTP method (defaults to GET)'),
folderId: z.string().optional().describe('Parent folder ID'),
description: z.string().optional().describe('Request description'),
headers: z
.array(
z.object({
name: z.string(),
value: z.string(),
enabled: z.boolean().default(true),
}),
)
.optional()
.describe('Request headers'),
urlParameters: z
.array(
z.object({
name: z.string(),
value: z.string(),
enabled: z.boolean().default(true),
}),
)
.optional()
.describe('URL query parameters'),
}),
},
async ({ workspaceId: ogWorkspaceId, ...args }) => {
const workspaceCtx = await getWorkspaceContext(ctx, ogWorkspaceId);
const workspaceId = await workspaceCtx.yaak.window.workspaceId();
if (!workspaceId) {
throw new Error('No workspace is open');
}
const httpRequest = await workspaceCtx.yaak.httpRequest.create({
workspaceId: workspaceId,
...args,
});
return {
content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }],
};
},
);
server.registerTool(
'update_http_request',
{
title: 'Update HTTP Request',
description: 'Update an existing HTTP request',
inputSchema: z.object({
id: z.string().describe('HTTP request ID to update'),
workspaceId: z
.string()
.optional()
.describe('Workspace ID (required if multiple workspaces are open)'),
name: z.string().optional().describe('Request name'),
url: z.string().optional().describe('Request URL'),
method: z.string().optional().describe('HTTP method'),
folderId: z.string().optional().describe('Parent folder ID'),
description: z.string().optional().describe('Request description'),
headers: z
.array(
z.object({
name: z.string(),
value: z.string(),
enabled: z.boolean().default(true),
}),
)
.optional()
.describe('Request headers'),
urlParameters: z
.array(
z.object({
name: z.string(),
value: z.string(),
enabled: z.boolean().default(true),
}),
)
.optional()
.describe('URL query parameters'),
}),
},
async ({ id, workspaceId, ...updates }) => {
const workspaceCtx = await getWorkspaceContext(ctx, workspaceId);
const httpRequest = await workspaceCtx.yaak.httpRequest.update({ id, ...updates });
return {
content: [{ type: 'text' as const, text: JSON.stringify(httpRequest, null, 2) }],
};
},
);
server.registerTool(
'delete_http_request',
{
title: 'Delete HTTP Request',
description: 'Delete an HTTP request by ID',
inputSchema: z.object({
id: z.string().describe('HTTP request ID to delete'),
}),
},
async ({ id }) => {
const httpRequest = await ctx.yaak.httpRequest.delete({ id });
return {
content: [
{ type: 'text' as const, text: `Deleted: ${httpRequest.name} (${httpRequest.id})` },
],
};
},
);
}

View File

@@ -1,59 +0,0 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import type { Color, Icon } from '@yaakapp/api';
import * as z from 'zod/v4';
import type { McpServerContext } from '../types.js';
const ICON_VALUES = [
'alert_triangle',
'check',
'check_circle',
'chevron_down',
'copy',
'info',
'pin',
'search',
'trash',
] as const satisfies readonly Icon[];
const COLOR_VALUES = [
'primary',
'secondary',
'info',
'success',
'notice',
'warning',
'danger',
] as const satisfies readonly Color[];
export function registerToastTools(server: McpServer, ctx: McpServerContext) {
server.registerTool(
'show_toast',
{
title: 'Show Toast',
description: 'Show a toast notification in Yaak',
inputSchema: z.object({
message: z.string().describe('The message to display'),
icon: z.enum(ICON_VALUES).optional().describe('Icon name'),
color: z.enum(COLOR_VALUES).optional().describe('Toast color'),
timeout: z.number().optional().describe('Timeout in milliseconds'),
}),
},
async ({ message, icon, color, timeout }) => {
await ctx.yaak.toast.show({
message,
icon,
color,
timeout,
});
return {
content: [
{
type: 'text' as const,
text: `✓ Toast shown: "${message}"`,
},
],
};
},
);
}

View File

@@ -1,50 +0,0 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod/v4';
import type { McpServerContext } from '../types.js';
import { getWorkspaceContext } from './helpers.js';
export function registerWindowTools(server: McpServer, ctx: McpServerContext) {
server.registerTool(
'get_workspace_id',
{
title: 'Get Workspace ID',
description: 'Get the current workspace ID',
inputSchema: z.object({}),
},
async () => {
const workspaceCtx = await getWorkspaceContext(ctx);
const workspaceId = await workspaceCtx.yaak.window.workspaceId();
return {
content: [
{
type: 'text' as const,
text: workspaceId || 'No workspace open',
},
],
};
},
);
server.registerTool(
'get_environment_id',
{
title: 'Get Environment ID',
description: 'Get the current environment ID',
inputSchema: z.object({}),
},
async () => {
const workspaceCtx = await getWorkspaceContext(ctx);
const environmentId = await workspaceCtx.yaak.window.environmentId();
return {
content: [
{
type: 'text' as const,
text: environmentId || 'No environment selected',
},
],
};
},
);
}

View File

@@ -1,26 +0,0 @@
import type { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import * as z from 'zod/v4';
import type { McpServerContext } from '../types.js';
export function registerWorkspaceTools(server: McpServer, ctx: McpServerContext) {
server.registerTool(
'list_workspaces',
{
title: 'List Workspaces',
description: 'List all open workspaces in Yaak',
inputSchema: z.object({}),
},
async () => {
const workspaces = await ctx.yaak.workspace.list();
return {
content: [
{
type: 'text' as const,
text: JSON.stringify(workspaces, null, 2),
},
],
};
},
);
}

View File

@@ -1,5 +0,0 @@
import type { Context } from '@yaakapp/api';
export interface McpServerContext {
yaak: Context;
}

View File

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

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"
}
}

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