attio 0.0.1-experimental.20250311 → 0.0.1-experimental.20250321
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/attio.js +0 -2
- package/lib/commands/version/index.js +1 -3
- package/lib/machines/init-machine.js +11 -6
- package/lib/templates/javascript/src/cat-fact.jsx +16 -0
- package/lib/templates/javascript/src/get-cat-fact.server.js +6 -0
- package/lib/templates/javascript/src/hello-world-dialog.jsx +6 -5
- package/lib/templates/typescript/src/cat-fact.tsx +16 -0
- package/lib/templates/typescript/src/get-cat-fact.server.ts +6 -0
- package/lib/templates/typescript/src/hello-world-dialog.tsx +5 -4
- package/package.json +1 -1
- package/lib/api/publish-version.js +0 -15
- package/lib/commands/connection/add.js +0 -67
- package/lib/commands/connection/index.js +0 -9
- package/lib/commands/connection/list.js +0 -20
- package/lib/commands/connection/remove.js +0 -20
- package/lib/commands/version/publish.js +0 -48
- package/lib/machines/add-connection-machine.js +0 -559
- package/lib/machines/list-connections-machine.js +0 -174
- package/lib/machines/publish-version-machine.js +0 -230
- package/lib/machines/remove-connection-machine.js +0 -253
- package/lib/templates/javascript/src/advice.jsx +0 -16
- package/lib/templates/javascript/src/get-advice.server.js +0 -6
- package/lib/templates/typescript/src/advice.tsx +0 -16
- package/lib/templates/typescript/src/get-advice.server.ts +0 -6
package/lib/attio.js
CHANGED
|
@@ -3,7 +3,6 @@ import { Command } from "commander";
|
|
|
3
3
|
import { init } from "./commands/init.js";
|
|
4
4
|
import { build } from "./commands/build.js";
|
|
5
5
|
import { dev } from "./commands/dev.js";
|
|
6
|
-
import { connection } from "./commands/connection/index.js";
|
|
7
6
|
import { version } from "./commands/version/index.js";
|
|
8
7
|
const program = new Command();
|
|
9
8
|
program
|
|
@@ -13,6 +12,5 @@ program
|
|
|
13
12
|
.addCommand(init)
|
|
14
13
|
.addCommand(build)
|
|
15
14
|
.addCommand(dev)
|
|
16
|
-
.addCommand(connection)
|
|
17
15
|
.addCommand(version)
|
|
18
16
|
.parse();
|
|
@@ -1,9 +1,7 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { versionCreate } from "./create.js";
|
|
3
3
|
import { versionList } from "./list.js";
|
|
4
|
-
import { versionPublish } from "./publish.js";
|
|
5
4
|
export const version = new Command("version")
|
|
6
5
|
.description("Manage app versions")
|
|
7
6
|
.addCommand(versionCreate)
|
|
8
|
-
.addCommand(versionList)
|
|
9
|
-
.addCommand(versionPublish);
|
|
7
|
+
.addCommand(versionList);
|
|
@@ -86,13 +86,18 @@ export const initMachine = setup({
|
|
|
86
86
|
loadAppInfo: fromPromise(async ({ input: { token, developerSlug, appSlug } }) => {
|
|
87
87
|
const spinner = new Spinner();
|
|
88
88
|
spinner.start("Loading app information...");
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
89
|
+
try {
|
|
90
|
+
const appInfo = await getAppInfo({ token, developerSlug, appSlug });
|
|
91
|
+
if (appInfo === null) {
|
|
92
|
+
spinner.error("App not found");
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
spinner.success(`App found: ${appInfo.title}`);
|
|
96
|
+
return appInfo;
|
|
97
|
+
}
|
|
98
|
+
finally {
|
|
99
|
+
spinner.stop();
|
|
93
100
|
}
|
|
94
|
-
spinner.success(`App found: ${appInfo.title}`);
|
|
95
|
-
return appInfo;
|
|
96
101
|
}),
|
|
97
102
|
},
|
|
98
103
|
actions: {
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import {TextBlock, useAsyncCache} from "attio/client"
|
|
3
|
+
import getCatFact from "./get-cat-fact.server"
|
|
4
|
+
|
|
5
|
+
export function CatFact({recordId}) {
|
|
6
|
+
// By passing in the recordId, the result will be cached for each recordId
|
|
7
|
+
const {
|
|
8
|
+
values: {catFact},
|
|
9
|
+
// ^^^^^^^– this key matches
|
|
10
|
+
// vvvvvvv– this key
|
|
11
|
+
} = useAsyncCache({catFact: [getCatFact, recordId]})
|
|
12
|
+
// ^^^^^^^^^^ ^^^^^^^^
|
|
13
|
+
// async fn parameter(s)
|
|
14
|
+
|
|
15
|
+
return <TextBlock align="center">{`"${catFact}"`}</TextBlock>
|
|
16
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export default async function getCatFact(recordId) {
|
|
2
|
+
// We don't really need the recordId for this API, but this is how we could use a parameter
|
|
3
|
+
const response = await fetch(`https://catfact.ninja/fact?${recordId}`)
|
|
4
|
+
const data = await response.json()
|
|
5
|
+
return data.fact
|
|
6
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import {TextBlock} from "attio/client"
|
|
3
|
-
import {
|
|
3
|
+
import {CatFact} from "./cat-fact"
|
|
4
4
|
|
|
5
|
-
const Loading = () => <TextBlock>Loading
|
|
5
|
+
const Loading = () => <TextBlock>Loading cat fact...</TextBlock>
|
|
6
6
|
|
|
7
|
-
export function HelloWorldDialog
|
|
7
|
+
export function HelloWorldDialog({recordId}) {
|
|
8
|
+
// A simple counter to demonstrate that this is just regular React code.
|
|
8
9
|
const [seconds, setSeconds] = React.useState(0)
|
|
9
10
|
React.useEffect(() => {
|
|
10
11
|
const timeout = setTimeout(() => setSeconds(seconds + 1), 1000)
|
|
@@ -16,9 +17,9 @@ export function HelloWorldDialog ({recordId}) {
|
|
|
16
17
|
<TextBlock align="left">
|
|
17
18
|
I am a dialog. I have been open for: {seconds} second{seconds === 1 ? "" : "s"}
|
|
18
19
|
</TextBlock>
|
|
19
|
-
{/* The hook in
|
|
20
|
+
{/* The hook in CatFact will suspend until the cat fact is loaded. */}
|
|
20
21
|
<React.Suspense fallback={<Loading />}>
|
|
21
|
-
<
|
|
22
|
+
<CatFact recordId={recordId} />
|
|
22
23
|
</React.Suspense>
|
|
23
24
|
</>
|
|
24
25
|
)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import React from "react"
|
|
2
|
+
import {TextBlock, useAsyncCache} from "attio/client"
|
|
3
|
+
import getCatFact from "./get-cat-fact.server"
|
|
4
|
+
|
|
5
|
+
export function CatFact({recordId}: {recordId: string}) {
|
|
6
|
+
// By passing in the recordId, the result will be cached for each recordId
|
|
7
|
+
const {
|
|
8
|
+
values: {catFact},
|
|
9
|
+
// ^^^^^^^– this key matches
|
|
10
|
+
// vvvvvvv– this key
|
|
11
|
+
} = useAsyncCache({catFact: [getCatFact, recordId]})
|
|
12
|
+
// ^^^^^^^^^^ ^^^^^^^^
|
|
13
|
+
// async fn parameter(s)
|
|
14
|
+
|
|
15
|
+
return <TextBlock align="center">{`"${catFact}"`}</TextBlock>
|
|
16
|
+
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export default async function getCatFact(recordId: string): Promise<string> {
|
|
2
|
+
// We don't really need the recordId for this API, but this is how we could use a parameter
|
|
3
|
+
const response = await fetch(`https://catfact.ninja/fact?${recordId}`)
|
|
4
|
+
const data = await response.json()
|
|
5
|
+
return data.fact
|
|
6
|
+
}
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import React from "react"
|
|
2
2
|
import {TextBlock} from "attio/client"
|
|
3
|
-
import {
|
|
3
|
+
import {CatFact} from "./cat-fact"
|
|
4
4
|
|
|
5
|
-
const Loading = () => <TextBlock>Loading
|
|
5
|
+
const Loading = () => <TextBlock>Loading cat fact...</TextBlock>
|
|
6
6
|
|
|
7
7
|
export function HelloWorldDialog({recordId}: {recordId: string}) {
|
|
8
|
+
// A simple counter to demonstrate that this is just regular React code.
|
|
8
9
|
const [seconds, setSeconds] = React.useState(0)
|
|
9
10
|
React.useEffect(() => {
|
|
10
11
|
const timeout = setTimeout(() => setSeconds(seconds + 1), 1000)
|
|
@@ -16,9 +17,9 @@ export function HelloWorldDialog({recordId}: {recordId: string}) {
|
|
|
16
17
|
<TextBlock align="left">
|
|
17
18
|
I am a dialog. I have been open for: {seconds} second{seconds === 1 ? "" : "s"}
|
|
18
19
|
</TextBlock>
|
|
19
|
-
{/* The hook in
|
|
20
|
+
{/* The hook in CatFact will suspend until the cat fact is loaded. */}
|
|
20
21
|
<React.Suspense fallback={<Loading />}>
|
|
21
|
-
<
|
|
22
|
+
<CatFact recordId={recordId} />
|
|
22
23
|
</React.Suspense>
|
|
23
24
|
</>
|
|
24
25
|
)
|
package/package.json
CHANGED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
import { API } from "../env.js";
|
|
3
|
-
import { handleError } from "./handle-error.js";
|
|
4
|
-
import { makeHeaders } from "./make-headers.js";
|
|
5
|
-
const publishVersionSchema = z.object({
|
|
6
|
-
success: z.literal(true),
|
|
7
|
-
});
|
|
8
|
-
export async function publishVersion({ token, devSlug, appId, major, minor, }) {
|
|
9
|
-
const response = await fetch(`${API}/developer-accounts/${devSlug}/apps/${appId}/prod-versions/${major}/${minor}/publish`, {
|
|
10
|
-
method: "POST",
|
|
11
|
-
headers: makeHeaders(token),
|
|
12
|
-
});
|
|
13
|
-
await handleError(response);
|
|
14
|
-
return publishVersionSchema.parse(await response.json());
|
|
15
|
-
}
|
|
@@ -1,67 +0,0 @@
|
|
|
1
|
-
import { Command, Option } from "commander";
|
|
2
|
-
import { createActor } from "xstate";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { addConnectionMachine } from "../../machines/add-connection-machine.js";
|
|
5
|
-
export const optionsSchema = z.object({
|
|
6
|
-
label: z.string().optional(),
|
|
7
|
-
description: z.string().optional(),
|
|
8
|
-
type: z.enum(["secret", "oauth2-code"]).optional(),
|
|
9
|
-
authorizeUrl: z
|
|
10
|
-
.string()
|
|
11
|
-
.url("Invalid URL, e.g. https://authorization-server.com/authorize")
|
|
12
|
-
.startsWith("https://", { message: "Must provide secure authorize URL (https://...)" })
|
|
13
|
-
.optional(),
|
|
14
|
-
accessTokenUrl: z
|
|
15
|
-
.string()
|
|
16
|
-
.url("Invalid URL, e.g. https://authorization-server.com/token")
|
|
17
|
-
.startsWith("https://", { message: "Must provide secure access token URL (https://...)" })
|
|
18
|
-
.optional(),
|
|
19
|
-
scopes: z
|
|
20
|
-
.string()
|
|
21
|
-
.refine((value) => value.split(",").every((scope) => scope.trim().length > 0), {
|
|
22
|
-
message: "Invalid OAuth scopes format. Must be a comma-delimited list of non-empty strings.",
|
|
23
|
-
})
|
|
24
|
-
.optional(),
|
|
25
|
-
clientId: z.string().optional(),
|
|
26
|
-
clientSecret: z.string().optional(),
|
|
27
|
-
dev: z.boolean().default(false),
|
|
28
|
-
});
|
|
29
|
-
export const connectionAdd = new Command("add")
|
|
30
|
-
.description("Create a new connection for your Attio app")
|
|
31
|
-
.addOption(new Option("--label", "The label for your connection that will be displayed to users"))
|
|
32
|
-
.addOption(new Option("--description", "A more detailed description of your connection that will be displayed to users"))
|
|
33
|
-
.addOption(new Option("--type <type>", "The type of connection to create").choices([
|
|
34
|
-
"secret",
|
|
35
|
-
"oauth2-code",
|
|
36
|
-
]))
|
|
37
|
-
.addOption(new Option("--authorize-url <url>", "The authorize URL for OAuth connection"))
|
|
38
|
-
.addOption(new Option("--access-token-url <url>", "The access token URL for OAuth connection"))
|
|
39
|
-
.addOption(new Option("--scopes <scopes>", "A comma separated list of scopes for OAuth connection"))
|
|
40
|
-
.addOption(new Option("--client-id <id>", "The client ID for OAuth connection"))
|
|
41
|
-
.addOption(new Option("--client-secret <secret>", "The client secret for OAuth connection"))
|
|
42
|
-
.addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
|
|
43
|
-
.action((unparsedOptions) => {
|
|
44
|
-
let options;
|
|
45
|
-
try {
|
|
46
|
-
options = optionsSchema.parse(unparsedOptions);
|
|
47
|
-
}
|
|
48
|
-
catch (error) {
|
|
49
|
-
if (error instanceof z.ZodError) {
|
|
50
|
-
process.stderr.write("\nInvalid options:\n");
|
|
51
|
-
error.errors.forEach((err) => {
|
|
52
|
-
process.stderr.write(`- ${err.path.map((p) => String(p).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)).join(".")}: ${err.message}\n`);
|
|
53
|
-
});
|
|
54
|
-
process.exit(1);
|
|
55
|
-
}
|
|
56
|
-
throw error;
|
|
57
|
-
}
|
|
58
|
-
const actor = createActor(addConnectionMachine, {
|
|
59
|
-
input: options,
|
|
60
|
-
});
|
|
61
|
-
if (options.dev) {
|
|
62
|
-
actor.subscribe((state) => {
|
|
63
|
-
console.log("state:", state.value);
|
|
64
|
-
});
|
|
65
|
-
}
|
|
66
|
-
actor.start();
|
|
67
|
-
});
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
import { Command } from "commander";
|
|
2
|
-
import { connectionAdd } from "./add.js";
|
|
3
|
-
import { connectionList } from "./list.js";
|
|
4
|
-
import { connectionRemove } from "./remove.js";
|
|
5
|
-
export const connection = new Command("connection")
|
|
6
|
-
.description("Manage app connections")
|
|
7
|
-
.addCommand(connectionAdd)
|
|
8
|
-
.addCommand(connectionList)
|
|
9
|
-
.addCommand(connectionRemove);
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Command, Option } from "commander";
|
|
2
|
-
import { createActor } from "xstate";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { listConnectionsMachine } from "../../machines/list-connections-machine.js";
|
|
5
|
-
export const optionsSchema = z.object({
|
|
6
|
-
dev: z.boolean().default(false),
|
|
7
|
-
});
|
|
8
|
-
export const connectionList = new Command("list")
|
|
9
|
-
.description("List all connections for the current major version of your Attio app")
|
|
10
|
-
.addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
|
|
11
|
-
.action((unparsedOptions) => {
|
|
12
|
-
const options = optionsSchema.parse(unparsedOptions);
|
|
13
|
-
const actor = createActor(listConnectionsMachine);
|
|
14
|
-
if (options.dev) {
|
|
15
|
-
actor.subscribe((state) => {
|
|
16
|
-
console.log("state:", state.value);
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
actor.start();
|
|
20
|
-
});
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
import { Command, Option } from "commander";
|
|
2
|
-
import { createActor } from "xstate";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { removeConnectionMachine } from "../../machines/remove-connection-machine.js";
|
|
5
|
-
export const optionsSchema = z.object({
|
|
6
|
-
dev: z.boolean().default(false),
|
|
7
|
-
});
|
|
8
|
-
export const connectionRemove = new Command("remove")
|
|
9
|
-
.description("Remove a connection from your Attio app")
|
|
10
|
-
.addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
|
|
11
|
-
.action((unparsedOptions) => {
|
|
12
|
-
const options = optionsSchema.parse(unparsedOptions);
|
|
13
|
-
const actor = createActor(removeConnectionMachine);
|
|
14
|
-
if (options.dev) {
|
|
15
|
-
actor.subscribe((state) => {
|
|
16
|
-
console.log("state:", state.value);
|
|
17
|
-
});
|
|
18
|
-
}
|
|
19
|
-
actor.start();
|
|
20
|
-
});
|
|
@@ -1,48 +0,0 @@
|
|
|
1
|
-
import { Argument, Command, Option } from "commander";
|
|
2
|
-
import { createActor } from "xstate";
|
|
3
|
-
import { z } from "zod";
|
|
4
|
-
import { publishVersionMachine } from "../../machines/publish-version-machine.js";
|
|
5
|
-
export const argsSchema = z
|
|
6
|
-
.string()
|
|
7
|
-
.regex(/^\d+\.\d+$/, 'Version must be in the format "major.minor" (e.g., "1.0")')
|
|
8
|
-
.optional()
|
|
9
|
-
.describe(`The version to invite someone to (e.g: "1.0"). If you don't specify a version, you will be prompted to choose.`);
|
|
10
|
-
export const optionsSchema = z.object({
|
|
11
|
-
dev: z.boolean().default(false),
|
|
12
|
-
});
|
|
13
|
-
export const versionPublish = new Command("publish")
|
|
14
|
-
.description("Publish an unpublished production version of your Attio app")
|
|
15
|
-
.addArgument(new Argument("<version>", `The version to publish (e.g: "1.0"). If you don't specify a version, you will be prompted to choose.`).argOptional())
|
|
16
|
-
.addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
|
|
17
|
-
.action((unparsedArgs, unparsedOptions) => {
|
|
18
|
-
const versionResult = argsSchema.safeParse(unparsedArgs);
|
|
19
|
-
const optionsResult = optionsSchema.safeParse(unparsedOptions);
|
|
20
|
-
if (!versionResult.success) {
|
|
21
|
-
process.stderr.write("\nInvalid version argument:\n");
|
|
22
|
-
versionResult.error.errors.forEach((err) => {
|
|
23
|
-
process.stderr.write(`- ${err.message}\n`);
|
|
24
|
-
});
|
|
25
|
-
process.exit(1);
|
|
26
|
-
}
|
|
27
|
-
if (!optionsResult.success) {
|
|
28
|
-
process.stderr.write("\nInvalid options:\n");
|
|
29
|
-
optionsResult.error.errors.forEach((err) => {
|
|
30
|
-
process.stderr.write(`- ${err.path.map((p) => String(p).replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`)).join(".")}: ${err.message}\n`);
|
|
31
|
-
});
|
|
32
|
-
process.exit(1);
|
|
33
|
-
}
|
|
34
|
-
const version = versionResult.data;
|
|
35
|
-
const options = optionsResult.data;
|
|
36
|
-
const actor = createActor(publishVersionMachine, {
|
|
37
|
-
input: {
|
|
38
|
-
major: version ? Number(version.split(".")[0]) : undefined,
|
|
39
|
-
minor: version ? Number(version.split(".")[1]) : undefined,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
|
-
if (options.dev) {
|
|
43
|
-
actor.subscribe((state) => {
|
|
44
|
-
console.log("state:", state.value);
|
|
45
|
-
});
|
|
46
|
-
}
|
|
47
|
-
actor.start();
|
|
48
|
-
});
|