Skip to content

Commit 747cbca

Browse files
Add vibe code, add watch and rework fetch(#48)
* Ready for review. * Optimised images with calibre/image-actions * Check. * . * Ready. * Linting fix. * Optimised images with calibre/image-actions * Review changes. AI assisted coding, fetch with plugins, and hot reload. --------- Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
1 parent f3ac873 commit 747cbca

10 files changed

Lines changed: 310 additions & 3 deletions

File tree

.vitepress/sidebars/guides.ts

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,13 @@ export const guidesSidebar: DefaultTheme.SidebarItem[] = [
99
link: "/guides/",
1010
},
1111
{
12-
text: "Configuring your Package",
12+
text: "Configuring Your Package",
1313
link: "/guides/config",
1414
},
15+
{
16+
text: "AI Assisted Coding",
17+
link: "/guides/vibe_coding",
18+
},
1519
],
1620
},
1721
{
@@ -50,6 +54,10 @@ export const guidesSidebar: DefaultTheme.SidebarItem[] = [
5054
text: "Sending HTTP Requests",
5155
link: "/guides/components/request",
5256
},
57+
{
58+
text: "Sending a Fetch Request",
59+
link: "/guides/components/fetch",
60+
},
5361
{
5462
text: "Sending Events to the Frontend",
5563
link: "/guides/components/events",

.vitepress/sidebars/reference.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ export const referenceSidebar: DefaultTheme.SidebarItem[] = [
3131
text: "Runtime",
3232
items: [
3333
{
34-
text: "Backend modules",
34+
text: "Backend Modules",
3535
link: "/reference/modules/",
3636
},
3737
],

src/_images/cursor_add_docs.png

5.58 KB
Loading
8.4 KB
Loading

src/_images/cursor_rules.png

27.8 KB
Loading

src/_images/pnpm_watch.png

44 KB
Loading

src/_images/pnpm_watch_server.png

5.77 KB
Loading

src/guides/components/fetch.md

Lines changed: 235 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,235 @@
1+
# Sending a Fetch Request
2+
3+
Caido's [HTTP Module](/reference/modules/caido/http.md) provides an implementation of the [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API). With this module, you can create and send asynchronous HTTP requests and handle their responses.
4+
5+
In this guide, we'll cover how to create a custom endpoint that sends a `fetch` request in a backend plugin, and call it from a frontend plugin.
6+
7+
::: warning NOTE
8+
The request and response objects of this module differ from those used in the [Backend SDK](/reference/sdks/backend/index.md) and [Workflow SDK](/reference/sdks/workflow/index.md). Due to this, their properties and methods differ as well. Additionally, they are not routed through the proxy and must adhere to the HTTP specification in order to be interpreted correctly.
9+
:::
10+
11+
## fetch()
12+
13+
The `fetch()` function takes either a URL or a [Request](/reference/modules/caido/http.html#request) object and as it's `input` parameter and an optional [RequestOpts](/reference/modules/caido/http.html#requestopts) parameter that can be included to configure specific elements of the request. The function will return a Promise that resolves to a [Response](/reference/modules/caido/http.html#response) object:
14+
15+
``` ts
16+
fetch(input: string | Request, init?: RequestOpts): Promise<Response>
17+
```
18+
19+
## Creating and Sending a Request
20+
21+
First, the necessary type aliases are imported. `SDK` is the interface used to interact with Caido. `DefineAPI` is used to structure the API: definining what methods or endpoints are available, the parameters those methods accept and what types of values they return.
22+
23+
``` ts
24+
import { SDK, DefineAPI } from "caido:plugin";
25+
```
26+
27+
To send a request, you will also need to import the `Request` class and the `fetch()` function from the `caido:http` module.
28+
29+
``` ts
30+
// Request object under the alias of FetchRequest.
31+
import { Request as FetchRequest, fetch } from "caido:http";
32+
```
33+
34+
Next, let's define an asynchronous function, specify request elements, and output the details to the [backend logs](https://docs.caido.io/reference/internal_files.html). In this example we define two URL query parameters, the `Accept` header, and the `User-Agent` header.
35+
36+
``` ts
37+
export async function callApi(sdk: SDK) {
38+
// Create a URL with search parameters.
39+
const url = "https://example.com?" + new URLSearchParams({
40+
paramA: "a",
41+
paramB: "b"
42+
}).toString();
43+
44+
// Create a new fetch request with various RequestOpts.
45+
const fetchRequest = new FetchRequest(url, {
46+
// If no method is explicitly set, defaults to GET.
47+
headers: {
48+
"Accept": "application/json",
49+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
50+
}
51+
});
52+
53+
// Log the fetch request details.
54+
sdk.console.log("\nFetch Request:");
55+
sdk.console.log(`URL: ${fetchRequest.url}`);
56+
sdk.console.log(`Method: ${fetchRequest.method}`);
57+
sdk.console.log("Headers: " + JSON.stringify(Object.fromEntries(fetchRequest.headers.entries())));
58+
```
59+
60+
We must await for the request to be sent and processed before we are able to obtain data from the response. By accessing the response properties, we can print the data to the backend logs.
61+
62+
``` ts
63+
try {
64+
const response = await fetch(fetchRequest);
65+
66+
// Log the response data.
67+
sdk.console.log("\nFetch Response:");
68+
sdk.console.log(`Status: ${response.status}`);
69+
sdk.console.log(`Status Text: ${response.statusText}`);
70+
sdk.console.log("Headers: " + JSON.stringify(Object.fromEntries(response.headers.entries())));
71+
72+
return {
73+
status: response.status,
74+
statusText: response.statusText,
75+
headers: Object.fromEntries(response.headers.entries())
76+
};
77+
} catch (error: any) {
78+
sdk.console.error("Error making fetch request: " + error.message);
79+
return `Error: ${error.message}`;
80+
}
81+
}
82+
```
83+
84+
Using the `DefineAPI` utility, we state that the `callApi` function is available to be called and link the definition using `typeof`. The definition is stored in the type alias `API` and exported so it can be used in other files.
85+
86+
``` ts
87+
export type API = DefineAPI<{
88+
callApi: typeof callApi;
89+
}>;
90+
```
91+
92+
Next, we define an initialization function that will add the `API` type alias to the base `SDK` and register the `callApi` function under the name `"callApi"`.
93+
94+
``` ts
95+
export function init(sdk: SDK<API>) {
96+
sdk.api.register("callApi", callApi);
97+
}
98+
```
99+
100+
::: tip
101+
To view the entire script, expand the following:
102+
103+
<details>
104+
<summary>Full Script</summary>
105+
106+
``` js
107+
import type { SDK, DefineAPI } from "caido:plugin";
108+
import { Request as FetchRequest, fetch } from "caido:http";
109+
110+
export async function callApi(sdk: SDK) {
111+
// Create a URL with search parameters.
112+
const url = "https://example.com?" + new URLSearchParams({
113+
paramA: "a",
114+
paramB: "b"
115+
}).toString();
116+
117+
// Create a new fetch request with various RequestOpts.
118+
const fetchRequest = new FetchRequest(url, {
119+
// If no method is explicitly set, defaults to GET.
120+
headers: {
121+
"Accept": "application/json",
122+
"User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"
123+
}
124+
});
125+
126+
// Log the fetch request details.
127+
sdk.console.log("\nFetch Request:");
128+
sdk.console.log(`URL: ${fetchRequest.url}`);
129+
sdk.console.log(`Method: ${fetchRequest.method}`);
130+
sdk.console.log("Headers: " + JSON.stringify(Object.fromEntries(fetchRequest.headers.entries())));
131+
132+
try {
133+
const response = await fetch(fetchRequest);
134+
135+
// Log the response data.
136+
sdk.console.log("\nFetch Response:");
137+
sdk.console.log(`Status: ${response.status}`);
138+
sdk.console.log(`Status Text: ${response.statusText}`);
139+
sdk.console.log("Headers: " + JSON.stringify(Object.fromEntries(response.headers.entries())));
140+
141+
return {
142+
status: response.status,
143+
statusText: response.statusText,
144+
headers: Object.fromEntries(response.headers.entries())
145+
};
146+
} catch (error: any) {
147+
sdk.console.error("Error making fetch request: " + error.message);
148+
return `Error: ${error.message}`;
149+
}
150+
}
151+
152+
export type API = DefineAPI<{
153+
callApi: typeof callApi;
154+
}>;
155+
156+
export function init(sdk: SDK<API>) {
157+
sdk.api.register("callApi", callApi);
158+
}
159+
```
160+
161+
</details>
162+
:::
163+
164+
::: info
165+
Within the logs, the message will resemble:
166+
167+
```
168+
2025-04-29T16:06:01.503261Z DEBUG actix-rt|system:0|arbiter:23 api|controller: Calling plugin (6aff5b11-5baf-452a-8971-f4c6f1eb7859) function: callApi
169+
2025-04-29T16:06:01.503303Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 plugin|executor: Calling method callApi (6aff5b11-5baf-452a-8971-f4c6f1eb7859)
170+
2025-04-29T16:06:01.503316Z DEBUG plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|runtime: Triggering API callApi (6aff5b11-5baf-452a-8971-f4c6f1eb7859)
171+
2025-04-29T16:06:01.503391Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk:
172+
Fetch Request:
173+
2025-04-29T16:06:01.503406Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk: URL: https://example.com?paramA=a&paramB=b
174+
2025-04-29T16:06:01.503412Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk: Method: GET
175+
2025-04-29T16:06:01.503446Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk: Headers: {"accept":"application/json","user-agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/135.0.0.0 Safari/537.36"}
176+
2025-04-29T16:06:02.856522Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk:
177+
Fetch Response:
178+
2025-04-29T16:06:02.856554Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk: Status: 200
179+
2025-04-29T16:06:02.856562Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk: Status Text: OK
180+
2025-04-29T16:06:02.856650Z INFO plugin:6aff5b11-5baf-452a-8971-f4c6f1eb7859 js|sdk: Headers: {"accept-ranges":"bytes","content-type":"text/html","etag":"\"84238dfc8092e5d9c0dac8ef93371a07:1736799080.121134\"","last-modified":"Mon, 13 Jan 2025 20:11:20 GMT","vary":"Accept-Encoding","content-encoding":"gzip","cache-control":"max-age=2775","date":"Tue, 29 Apr 2025 16:06:01 GMT","alt-svc":"h3=\":443\"; ma=93600,h3-29=\":443\"; ma=93600,quic=\":443\"; ma=93600; v=\"43\"","content-length":"648","connection":"keep-alive"}
181+
```
182+
183+
:::
184+
185+
::: tip
186+
To view how the endpoint can be called with a frontend plugin, expand the following:
187+
188+
<details>
189+
<summary>Full Script</summary>
190+
191+
``` js
192+
import type { Caido } from "@caido/sdk-frontend";
193+
import type { API } from "../../backend/src/index.ts";
194+
195+
export type CaidoSDK = Caido<API>;
196+
197+
const createPage = (sdk: CaidoSDK) => {
198+
199+
const resultText = document.createElement("p");
200+
resultText.textContent = "Result will appear here.";
201+
202+
const calculateButton = sdk.ui.button({
203+
variant: "primary",
204+
label: "Fetch",
205+
});
206+
207+
calculateButton.addEventListener("click", async () => {
208+
const result = await sdk.backend.callApi();
209+
resultText.textContent = `Result: ${JSON.stringify(result, null, 2)}`;
210+
});
211+
212+
const container = document.createElement("div");
213+
container.appendChild(calculateButton);
214+
container.appendChild(resultText);
215+
216+
const card = sdk.ui.card({
217+
body: container
218+
});
219+
220+
sdk.navigation.addPage("/fetch-page", {
221+
body: card
222+
});
223+
}
224+
225+
export function init(sdk: CaidoSDK) {
226+
createPage(sdk);
227+
228+
sdk.sidebar.registerItem("Fetch", "/fetch-page", {
229+
icon: "fas fa-paper-plane"
230+
});
231+
}
232+
```
233+
234+
</details>
235+
:::

src/guides/index.md

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,10 +46,34 @@ pnpm build
4646

4747
You can then install this plugin package directly from the Caido application.
4848

49+
## Hot Reload
50+
51+
Instead of uninstalling, rebuilding, and installing your plugin to view the changes you make during development, Caido offers the [Devtools](https://github.com/caido-community/devtools) plugin that will auto-update the plugin whenever code changes are detected.
52+
53+
To use the Devtools plugin:
54+
55+
1. First navigate to the [Plugins](https://docs.caido.io/guides/plugins.html) interface, select Community Store, and click `+ Install`.
56+
57+
2. Next, run the following command from the root directory of the plugin to both build and watch for file changes:
58+
59+
``` bash
60+
pnpm watch
61+
```
62+
63+
<img alt="Output of pnpm watch command." src="/_images/pnpm_watch.png" center/>
64+
65+
3. Then, input the development server URL in the Devtools plugin and click the `Connect` button.
66+
67+
<img alt="Development server URL input." src="/_images/pnpm_watch_server.png" center/>
68+
4969
## What's next?
5070

5171
Now that you've created your first package, you can start building out your own frontend and backend plugins.
5272

53-
We highly recommend looking at existing plugins in the Community Store to get an idea of what's possible. All plugins are open-sourced and available for you to review and learn from.
73+
We highly recommend looking at existing plugins in the [Community Store](https://github.com/caido-community) to get an idea of what's possible. All plugins are open-sourced and available for you to review and learn from.
5474

5575
There are also a few guides available on this site to help you get started.
76+
77+
::: tip
78+
[Learn how to leverage AI tools to build Caido plugins.](./vibe_coding.md)
79+
:::

src/guides/vibe_coding.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
# Using AI to Assist in Plugin Development
2+
3+
::: warning DISCLAIMER
4+
While AI assistants can be helpful in development, they may sometimes provide incorrect or incomplete information. Always verify AI-generated code and suggestions against the official documentation and test thoroughly.
5+
:::
6+
7+
## Cursor
8+
9+
If you are using [Cursor](https://www.cursor.com/en) as your IDE, you can load the Caido documentation websites into your workspace so that Cursor becomes aware of the Caido SDK:
10+
11+
1. Open Cursor and click `View` in the menu bar and select `Command Palette` (_or use `CTRL + Shift + P`_) and search for:
12+
13+
```
14+
Add New Custom Docs
15+
```
16+
17+
2. Select the option and enter the documentation URL:
18+
19+
```
20+
https://developer.caido.io/
21+
```
22+
23+
3. Provide a reference name and click `Confirm`.
24+
25+
<img alt="Add dev docs to Cursor." src="/_images/cursor_add_docs.png" center/>
26+
27+
4. Cursor will index the website allowing you to reference the documentation in prompts by using `@<reference name>`.
28+
29+
::: tip TIPS
30+
31+
- Add `https://github.com/caido/sdk-js` to Cursor as well so it has the SDK alongside the developer documentation examples.
32+
33+
- Also add `https://docs.caido.io/` to Cursor so you can reference interfaces for context.
34+
35+
- You can also customize prompt responses by navigating to `Settings`, `Rules`, and writing your preferences in the `User Rules` input field.
36+
:::
37+
38+
## Custom GPT
39+
40+
[This custom GPT is trained on Caido documentation, SDKs, and additional sources to answer your prompts with higher accuracy.](https://chatgpt.com/g/g-68095eb17eb08191ba19fd85f0a516ec-caido-developer-assistant)

0 commit comments

Comments
 (0)