Skip to content

Commit 55c72bc

Browse files
Merge branch 'deco-cx:main' into main
2 parents 60503dc + 26b03a4 commit 55c72bc

File tree

457 files changed

+79203
-287
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

457 files changed

+79203
-287
lines changed

.DS_Store

0 Bytes
Binary file not shown.

MAINTAINERS.txt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
guitavano
22
matheusgr
33
viktormarinho
4+
mcandeia
5+
pedrofrxncx
6+
lucis

NewAppPrompt.md

Lines changed: 357 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,357 @@
1+
# Prompt to Create a New App
2+
3+
> Use this prompt in an AI editor (Cursor, Windsurf) to create a new app
4+
5+
**Important:** Run this alongside information about the app you're creating, be
6+
it a documentation or OpenAPI specification.
7+
8+
A deco app allows a service API to be exposed using Typescript functions. After
9+
a service it wrapped in an app, it can be used
10+
11+
- As a data source in deco CMS
12+
- As a MCP server in deco.chat
13+
14+
For the AI to create the necessary typings and functions, you need to provide a
15+
data source for the API documentation. It can be the plain text of an API docs
16+
or an OpenAPI specification.
17+
18+
## Instructions
19+
20+
A new app should be placed inside a folder on the root of this repo with the app
21+
name. For example, if we're creating an app for Figma, the app files will be on
22+
`figma/`
23+
24+
### client.ts
25+
26+
The client.ts is one of the central pieces of an app. It defines:
27+
28+
- Typings for the data entities that the API accepts/returns.
29+
- All API methods with typed input and output
30+
31+
The client interface follows a specific pattern that works with the
32+
`createHttpClient` utility. Here's a detailed example:
33+
34+
```typescript
35+
// First, define your data types/interfaces
36+
export interface GithubUser {
37+
login: string;
38+
id: number;
39+
avatar_url: string;
40+
}
41+
42+
// Then define your client interface
43+
// The key format must be: "HTTP_METHOD /path/to/endpoint"
44+
// Parameters in the URL path must be prefixed with : (e.g. :username)
45+
// Optional parameters must end with ? (e.g. :filter?)
46+
export interface GithubClient {
47+
// Simple GET request with URL parameters
48+
"GET /users/:username": {
49+
response: GithubUser; // Type of the response
50+
};
51+
52+
// POST request with body and URL parameters
53+
"POST /users/:username": {
54+
response: GithubUser;
55+
body: {
56+
filter: string;
57+
};
58+
};
59+
60+
// GET request with query parameters (searchParams)
61+
"GET /search/users": {
62+
response: { users: GithubUser[] };
63+
searchParams: {
64+
q: string;
65+
page?: number;
66+
per_page?: number;
67+
};
68+
};
69+
70+
// POST with both URL params, query params and body
71+
"POST /repos/:owner/:repo/issues": {
72+
response: { id: number };
73+
body: {
74+
title: string;
75+
body: string;
76+
};
77+
searchParams: {
78+
assignee?: string;
79+
labels?: string[];
80+
};
81+
};
82+
}
83+
```
84+
85+
Key points about the client interface:
86+
87+
1. **HTTP Methods**: Must be one of: GET, PUT, POST, DELETE, PATCH, HEAD
88+
89+
2. **URL Parameters**:
90+
- Required parameters: `:paramName`
91+
- Optional parameters: `:paramName?`
92+
- Wildcard parameters: `*` or `*paramName`
93+
94+
3. **Response Type**:
95+
- Always defined in the `response` property
96+
- Can be any TypeScript type/interface
97+
- Optional if the endpoint doesn't return data
98+
99+
4. **Request Body**:
100+
- Defined in the `body` property
101+
- Required for POST/PUT/PATCH methods
102+
- Must be a JSON-serializable object
103+
104+
5. **Query Parameters**:
105+
- Defined in the `searchParams` property
106+
- All parameters are optional by default
107+
- Can be primitive types or arrays
108+
109+
Example usage with the client:
110+
111+
```typescript
112+
const api = createHttpClient<GithubClient>({
113+
base: "https://api.github.com",
114+
headers: new Headers({
115+
"Authorization": `Bearer ${token}`,
116+
}),
117+
});
118+
119+
// Using URL parameters
120+
const user = await api["GET /users/:username"]({
121+
username: "octocat",
122+
});
123+
124+
// Using query parameters
125+
const search = await api["GET /search/users"]({
126+
q: "john",
127+
page: 1,
128+
per_page: 10,
129+
});
130+
131+
// Using body and URL parameters
132+
const issue = await api["POST /repos/:owner/:repo/issues"](
133+
{
134+
owner: "octocat",
135+
repo: "Hello-World",
136+
},
137+
{
138+
body: {
139+
title: "Found a bug",
140+
body: "This is a bug report",
141+
},
142+
},
143+
);
144+
```
145+
146+
The client interface is used by the `createHttpClient` utility to:
147+
148+
- Type-check URL parameters
149+
- Type-check request bodies
150+
- Type-check query parameters
151+
- Provide type-safe responses
152+
- Handle URL construction
153+
- Handle JSON serialization
154+
- Manage headers and authentication
155+
156+
### mod.ts
157+
158+
The mod.ts is the entrypoint for the app and it declares the **app
159+
configuration**, like API token or account name. This is information that is
160+
required for all methods in the API and it might be better if the user informs
161+
only once (when installing the app).
162+
163+
It also instantiates the client or any other SDK/information that will be passed
164+
as context for every action and loader when executed. It uses the
165+
166+
Example:
167+
168+
```typescript
169+
import type { App, FnContext } from "@deco/deco";
170+
import { fetchSafe } from "../utils/fetch.ts";
171+
import { createHttpClient } from "../utils/http.ts";
172+
import type { Secret } from "../website/loaders/secret.ts";
173+
import manifest, { Manifest } from "./manifest.gen.ts";
174+
import { ClientInterfaceExample } from "./client.ts";
175+
176+
export type AppContext = FnContext<State, Manifest>;
177+
178+
export interface Props {
179+
/**
180+
* @title Account Name
181+
* @description erploja2 etc
182+
*/
183+
account: string;
184+
185+
/**
186+
* @title API token
187+
* @description The token for accessing your API
188+
*/
189+
token?: string | Secret;
190+
}
191+
192+
// Here we define the state of the app
193+
// You choose what to put in the state
194+
export interface State extends Omit<Props, "token"> {
195+
api: ReturnType<typeof createHttpClient<ClientInterfaceExample>>;
196+
}
197+
198+
/**
199+
* @name App Template
200+
* @description This is an template of an app to be used as a reference.
201+
* @category Developer Tools
202+
* @logo https://
203+
*/
204+
export default function App(props: Props): App<Manifest, State> {
205+
const { token, account: _account } = props;
206+
207+
const _stringToken = typeof token === "string" ? token : token?.get?.() ?? "";
208+
209+
const api = createHttpClient<ClientInterfaceExample>({
210+
base: `https://api.github.com/users/guitavano`,
211+
headers: new Headers({ "Authorization": `Bearer ${_stringToken}` }),
212+
fetcher: fetchSafe,
213+
});
214+
215+
// it is the state of the app, all data
216+
// here will be available in the context of
217+
// loaders, actions and workflows
218+
const state = { ...props, api };
219+
220+
return {
221+
state,
222+
manifest,
223+
};
224+
}
225+
```
226+
227+
### Actions and Loaders
228+
229+
An app is used, after installed, by calling its actions and loaders.
230+
231+
Actions and Loaders are Typescript functions that abstract the API methods of
232+
the service being wrapped.
233+
234+
Loaders are used to retrieve data.
235+
236+
Actions are used when you save or update data in the external services.
237+
238+
To declare an action or loader, it's just needed to create a `{actionName}.ts`
239+
inside `{appFolder}/actions/` or `{loaderName}.ts` inside
240+
`{appFolder}/loaders/`. You can use intermediary folders for organization.
241+
242+
Examples:
243+
244+
### Loader Example
245+
246+
```typescript
247+
import { AppContext } from "../mod.ts";
248+
import { GithubUser } from "../utils/types.ts";
249+
250+
interface Props {
251+
username: string;
252+
}
253+
254+
/**
255+
* @title This name will appear on the admin
256+
*/
257+
const loader = async (
258+
props: Props,
259+
_req: Request,
260+
ctx: AppContext,
261+
): Promise<GithubUser> => {
262+
const response = await ctx.api[`GET /users/:username`]({
263+
username: props.username,
264+
});
265+
266+
const result = await response.json();
267+
268+
return result;
269+
};
270+
271+
export default loader;
272+
```
273+
274+
### Action Example
275+
276+
```typescript
277+
import { AppContext } from "../mod.ts";
278+
import { GithubUser } from "../utils/types.ts";
279+
280+
interface Props {
281+
username: string;
282+
}
283+
284+
/**
285+
* @title This name will appear on the admin
286+
*/
287+
const action = async (
288+
props: Props,
289+
_req: Request,
290+
ctx: AppContext,
291+
): Promise<GithubUser> => {
292+
const response = await ctx.api[`POST /users/:username`]({
293+
username: props.username,
294+
}, { body: { filter: "filter" } });
295+
296+
const result = await response.json();
297+
298+
return result;
299+
};
300+
301+
export default action;
302+
```
303+
304+
### deco.ts
305+
306+
In root `deco.ts`, add a new entry for the newly created app.
307+
308+
```
309+
const config = {
310+
apps: [
311+
app("deno"),
312+
app("figma"),
313+
app("unsplash"),
314+
app("reflect"),
315+
app("grain"),
316+
app("slack"),
317+
```
318+
319+
### Manifest
320+
321+
In every app folder there's also a `manifest.gen.ts` that exports all actions
322+
and loaders from an app. You don't need to worry about this file because it will
323+
be automatically generated when running `deno task start` in the root folder.
324+
325+
Generate a first version so the app doesn't break: Example
326+
327+
```typescript
328+
// DO NOT EDIT. This file is generated by deco.
329+
// This file SHOULD be checked into source version control.
330+
// This file is automatically updated during development when running `dev.ts`.
331+
332+
import * as $$$$$$$$$0 from "./actions/myAction.ts";
333+
import * as $$$0 from "./loaders/myLoader.ts";
334+
import * as $$$$$$0 from "./sections/mySection.tsx";
335+
336+
const manifest = {
337+
"loaders": {
338+
"app-template/loaders/myLoader.ts": $$$0,
339+
},
340+
"sections": {
341+
"app-template/sections/mySection.tsx": $$$$$$0,
342+
},
343+
"actions": {
344+
"app-template/actions/myAction.ts": $$$$$$$$$0,
345+
},
346+
"name": "app-template",
347+
"baseUrl": import.meta.url,
348+
};
349+
350+
export type Manifest = typeof manifest;
351+
352+
export default manifest;
353+
```
354+
355+
## Manifest Gen
356+
357+
In the end, run `deno task start` to regenerate the manifest.

0 commit comments

Comments
 (0)