attio 0.0.1-experimental.20250326 → 0.0.1-experimental.20250327
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/api/auth.js +6 -1
- package/lib/commands/dev/boot.js +29 -0
- package/lib/commands/dev/build-javascript.js +49 -0
- package/lib/commands/dev/bundle-javascript.js +49 -0
- package/lib/commands/dev/graphql-code-gen.js +42 -0
- package/lib/commands/dev/graphql-server.js +66 -0
- package/lib/commands/dev/onboarding.js +29 -0
- package/lib/commands/dev/prepare-build-contexts.js +75 -0
- package/lib/commands/dev/upload.js +60 -0
- package/lib/commands/dev/validate-typescript.js +90 -0
- package/lib/commands/dev.js +33 -14
- package/lib/machines/actors.js +2 -2
- package/lib/util/listen-for-key.js +27 -0
- package/lib/util/load-attio-cli-package-json.js +2 -2
- package/lib/util/load-env.js +1 -1
- package/lib/util/print-message.js +1 -1
- package/package.json +1 -1
- package/lib/machines/dev-machine.js +0 -619
package/lib/api/auth.js
CHANGED
|
@@ -47,7 +47,12 @@ export async function auth() {
|
|
|
47
47
|
const state = randomBytes(32).toString("base64url");
|
|
48
48
|
const port = await findAvailablePort(3000, 65000);
|
|
49
49
|
const redirectUri = `http://localhost:${port}`;
|
|
50
|
-
|
|
50
|
+
let resolveToken;
|
|
51
|
+
let rejectToken;
|
|
52
|
+
const tokenPromise = new Promise((resolve, reject) => {
|
|
53
|
+
resolveToken = resolve;
|
|
54
|
+
rejectToken = reject;
|
|
55
|
+
});
|
|
51
56
|
const app = new Hono();
|
|
52
57
|
let serverRef;
|
|
53
58
|
app.get("/", async (c) => {
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { ensureAuthed } from "../../api/ensure-authed.js";
|
|
2
|
+
import { loadEnv } from "../../util/load-env.js";
|
|
3
|
+
import { determineWorkspace } from "../../machines/actors.js";
|
|
4
|
+
import { fetchAppInfo } from "../../machines/actors.js";
|
|
5
|
+
import { getAppSlugFromPackageJson } from "../../machines/actors.js";
|
|
6
|
+
import { loadAttioCliPackageJson } from "../../util/load-attio-cli-package-json.js";
|
|
7
|
+
import { createDevVersion } from "../../api/create-dev-version.js";
|
|
8
|
+
export async function boot({ workspaceSlug }) {
|
|
9
|
+
const token = await ensureAuthed();
|
|
10
|
+
const appSlug = await getAppSlugFromPackageJson();
|
|
11
|
+
const appInfo = await fetchAppInfo({ token, appSlug });
|
|
12
|
+
const workspace = await determineWorkspace({ token, workspaceSlug });
|
|
13
|
+
const environmentVariables = await loadEnv();
|
|
14
|
+
const packageJson = loadAttioCliPackageJson();
|
|
15
|
+
const devVersion = await createDevVersion({
|
|
16
|
+
token,
|
|
17
|
+
appId: appInfo.app_id,
|
|
18
|
+
targetWorkspaceId: workspace.workspace_id,
|
|
19
|
+
environmentVariables,
|
|
20
|
+
cliVersion: packageJson.version,
|
|
21
|
+
});
|
|
22
|
+
return {
|
|
23
|
+
token,
|
|
24
|
+
appId: appInfo.app_id,
|
|
25
|
+
appSlug,
|
|
26
|
+
devVersionId: devVersion.app_dev_version_id,
|
|
27
|
+
workspace,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import chokidar from "chokidar";
|
|
2
|
+
import notifier from "node-notifier";
|
|
3
|
+
import { clearTerminal } from "../../util/clear-terminal.js";
|
|
4
|
+
import { errorsAndWarningsSchema } from "../../build.js";
|
|
5
|
+
import { prepareBuildContexts } from "./prepare-build-contexts.js";
|
|
6
|
+
import { printJsError } from "../../util/typescript.js";
|
|
7
|
+
const notify = (errors) => {
|
|
8
|
+
const totalErrors = (errors.errors?.length || 0) + (errors.warnings?.length || 0);
|
|
9
|
+
notifier.notify({
|
|
10
|
+
title: `JavaScript ${totalErrors === 1 ? "Error" : "Errors"}`,
|
|
11
|
+
message: `There ${totalErrors === 1 ? "was one error" : `were ${totalErrors} errors`} in your JavaScript code`,
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export function buildJavaScript(onSuccess) {
|
|
15
|
+
const watcher = chokidar.watch(["src/**/*.{js,jsx,ts,tsx}"], {
|
|
16
|
+
ignored: ["**/node_modules/**", "**/dist/**", "**/*.graphql.d.ts", "**/*.gql.d.ts"],
|
|
17
|
+
cwd: ".",
|
|
18
|
+
});
|
|
19
|
+
let buildContexts;
|
|
20
|
+
async function handleBuild() {
|
|
21
|
+
try {
|
|
22
|
+
if (!buildContexts) {
|
|
23
|
+
buildContexts = await prepareBuildContexts();
|
|
24
|
+
}
|
|
25
|
+
const results = await Promise.all(buildContexts.map(async (context) => context.rebuild()));
|
|
26
|
+
const bundles = results.map((result) => result.outputFiles[0].text);
|
|
27
|
+
onSuccess?.(bundles);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
const errors = errorsAndWarningsSchema.parse(error);
|
|
31
|
+
clearTerminal();
|
|
32
|
+
errors.errors?.forEach((error) => printJsError(error, "error"));
|
|
33
|
+
errors.warnings?.forEach((warning) => printJsError(warning, "warning"));
|
|
34
|
+
notify(errors);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
watcher.on("ready", () => {
|
|
38
|
+
handleBuild();
|
|
39
|
+
watcher.on("all", (event, path) => {
|
|
40
|
+
if (event === "add" || event === "change" || event === "unlink") {
|
|
41
|
+
handleBuild();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
return async () => {
|
|
46
|
+
await Promise.all(buildContexts?.map(async (context) => context.dispose()) ?? []);
|
|
47
|
+
await watcher.close();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import chokidar from "chokidar";
|
|
2
|
+
import notifier from "node-notifier";
|
|
3
|
+
import { clearTerminal } from "../../util/clear-terminal.js";
|
|
4
|
+
import { errorsAndWarningsSchema } from "../../build.js";
|
|
5
|
+
import { prepareBuildContexts } from "./prepare-build-contexts.js";
|
|
6
|
+
import { printJsError } from "../../util/typescript.js";
|
|
7
|
+
const notify = (errors) => {
|
|
8
|
+
const totalErrors = (errors.errors?.length || 0) + (errors.warnings?.length || 0);
|
|
9
|
+
notifier.notify({
|
|
10
|
+
title: `JavaScript ${totalErrors === 1 ? "Error" : "Errors"}`,
|
|
11
|
+
message: `There ${totalErrors === 1 ? "was one error" : `were ${totalErrors} errors`} in your JavaScript code`,
|
|
12
|
+
});
|
|
13
|
+
};
|
|
14
|
+
export function bundleJavaScript(onSuccess) {
|
|
15
|
+
const watcher = chokidar.watch(["./src/**/*.{js,jsx,ts,tsx}"], {
|
|
16
|
+
ignored: ["**/node_modules/**", "**/dist/**", "**/*.graphql.d.ts", "**/*.gql.d.ts"],
|
|
17
|
+
cwd: process.cwd(),
|
|
18
|
+
});
|
|
19
|
+
let buildContexts;
|
|
20
|
+
async function handleBuild() {
|
|
21
|
+
try {
|
|
22
|
+
if (!buildContexts) {
|
|
23
|
+
buildContexts = await prepareBuildContexts();
|
|
24
|
+
}
|
|
25
|
+
const results = await Promise.all(buildContexts.map(async (context) => context.rebuild()));
|
|
26
|
+
const bundles = results.map((result) => result.outputFiles[0].text);
|
|
27
|
+
onSuccess?.(bundles);
|
|
28
|
+
}
|
|
29
|
+
catch (error) {
|
|
30
|
+
const errors = errorsAndWarningsSchema.parse(error);
|
|
31
|
+
clearTerminal();
|
|
32
|
+
errors.errors?.forEach((error) => printJsError(error, "error"));
|
|
33
|
+
errors.warnings?.forEach((warning) => printJsError(warning, "warning"));
|
|
34
|
+
notify(errors);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
watcher.on("ready", () => {
|
|
38
|
+
handleBuild();
|
|
39
|
+
watcher.on("all", (event) => {
|
|
40
|
+
if (event === "add" || event === "change" || event === "unlink") {
|
|
41
|
+
handleBuild();
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
return async () => {
|
|
46
|
+
await Promise.all(buildContexts?.map(async (context) => context.dispose()) ?? []);
|
|
47
|
+
await watcher.close();
|
|
48
|
+
};
|
|
49
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import chokidar from "chokidar";
|
|
2
|
+
import { readFileSync } from "fs";
|
|
3
|
+
import { parse } from "graphql";
|
|
4
|
+
import { findNodeModulesPath } from "../../util/find-node-modules-path.js";
|
|
5
|
+
import { generateOperations } from "../../graphql/generate-operations.js";
|
|
6
|
+
import { GraphQLError } from "../../graphql/graphql-error.js";
|
|
7
|
+
export function graphqlCodeGen(onSuccess) {
|
|
8
|
+
const watcher = chokidar.watch(["**/*.graphql", "**/*.gql"], {
|
|
9
|
+
ignored: ["**/node_modules/**", "**/dist/**"],
|
|
10
|
+
cwd: ".",
|
|
11
|
+
});
|
|
12
|
+
async function handleSchemaAndGenerate() {
|
|
13
|
+
try {
|
|
14
|
+
const schemaPath = await findNodeModulesPath(["schema.graphql"]);
|
|
15
|
+
if (!schemaPath) {
|
|
16
|
+
throw new Error("No schema.graphql found in node_modules");
|
|
17
|
+
}
|
|
18
|
+
const schema = parse(readFileSync(schemaPath, "utf8"));
|
|
19
|
+
await generateOperations(".", schema);
|
|
20
|
+
onSuccess?.();
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error instanceof GraphQLError) {
|
|
24
|
+
process.stderr.write(error.toString());
|
|
25
|
+
}
|
|
26
|
+
else {
|
|
27
|
+
throw error;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
watcher.on("ready", () => {
|
|
32
|
+
handleSchemaAndGenerate();
|
|
33
|
+
watcher.on("all", (event, path) => {
|
|
34
|
+
if (event === "add" || event === "change" || event === "unlink") {
|
|
35
|
+
handleSchemaAndGenerate();
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
return () => {
|
|
40
|
+
watcher.close();
|
|
41
|
+
};
|
|
42
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { fileURLToPath } from "url";
|
|
2
|
+
import { dirname } from "path";
|
|
3
|
+
import path from "path";
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import { buildSchema } from "graphql";
|
|
6
|
+
import { Hono } from "hono";
|
|
7
|
+
import { serve } from "@hono/node-server";
|
|
8
|
+
import { graphqlServer as graphqlServerMiddleware } from "@hono/graphql-server";
|
|
9
|
+
import { findAvailablePort } from "../../util/find-available-port.js";
|
|
10
|
+
import open from "open";
|
|
11
|
+
import { listenForKey } from "../../util/listen-for-key.js";
|
|
12
|
+
export function graphqlServer() {
|
|
13
|
+
let server = null;
|
|
14
|
+
let port;
|
|
15
|
+
let isShuttingDown = false;
|
|
16
|
+
const startServer = async () => {
|
|
17
|
+
if (isShuttingDown)
|
|
18
|
+
return;
|
|
19
|
+
try {
|
|
20
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
21
|
+
const currentDirPath = dirname(currentFilePath);
|
|
22
|
+
const schemaPath = path.resolve(currentDirPath, "..", "..", "..", "schema.graphql");
|
|
23
|
+
const schemaString = fs.readFileSync(schemaPath, "utf8");
|
|
24
|
+
port = await findAvailablePort(8700);
|
|
25
|
+
const schema = buildSchema(schemaString);
|
|
26
|
+
const app = new Hono();
|
|
27
|
+
const rootResolver = () => {
|
|
28
|
+
return {};
|
|
29
|
+
};
|
|
30
|
+
app.use("/graphql", graphqlServerMiddleware({
|
|
31
|
+
schema,
|
|
32
|
+
rootResolver,
|
|
33
|
+
graphiql: true,
|
|
34
|
+
}));
|
|
35
|
+
server = serve({
|
|
36
|
+
fetch: app.fetch,
|
|
37
|
+
port,
|
|
38
|
+
});
|
|
39
|
+
process.stdout.write(`\n\n Press "o" to open GraphQL Explorer.\n\n`);
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
process.stderr.write(`Failed to start GraphQL server: ${error}\n`);
|
|
43
|
+
throw error;
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
startServer().catch((error) => {
|
|
47
|
+
process.stderr.write(`GraphQL server failed to start: ${error}\n`);
|
|
48
|
+
});
|
|
49
|
+
const cleanup = listenForKey("o", () => {
|
|
50
|
+
if (port) {
|
|
51
|
+
open(`http://localhost:${port}/graphql`);
|
|
52
|
+
}
|
|
53
|
+
});
|
|
54
|
+
return () => {
|
|
55
|
+
isShuttingDown = true;
|
|
56
|
+
cleanup();
|
|
57
|
+
if (server) {
|
|
58
|
+
try {
|
|
59
|
+
server.close();
|
|
60
|
+
}
|
|
61
|
+
catch (error) {
|
|
62
|
+
process.stderr.write(`Error closing GraphQL server: ${error}\n`);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { listenForKey } from "../../util/listen-for-key.js";
|
|
2
|
+
import open from "open";
|
|
3
|
+
import { fetchInstallation } from "../../api/fetch-installation.js";
|
|
4
|
+
import { APP } from "../../env.js";
|
|
5
|
+
function prompt() {
|
|
6
|
+
process.stdout.write(`\n\n🚨 IMPORTANT: You will need to install your app in your workspace. Press "i" to open the app settings page, and then click "Install".\n\n`);
|
|
7
|
+
}
|
|
8
|
+
export function onboarding({ token, appId, appSlug, workspace, }) {
|
|
9
|
+
const haveInstallation = async () => (await fetchInstallation({ token, appId, workspaceId: workspace.workspace_id })) !== null;
|
|
10
|
+
const cleanup = listenForKey("i", () => {
|
|
11
|
+
open(`${APP}/${workspace.slug}/settings/apps/${appSlug}`);
|
|
12
|
+
});
|
|
13
|
+
const poll = async () => {
|
|
14
|
+
let installation = await haveInstallation();
|
|
15
|
+
if (!installation) {
|
|
16
|
+
prompt();
|
|
17
|
+
while (!installation) {
|
|
18
|
+
await new Promise((resolve) => setTimeout(resolve, 60_000));
|
|
19
|
+
installation = await haveInstallation();
|
|
20
|
+
if (!installation) {
|
|
21
|
+
prompt();
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
cleanup();
|
|
25
|
+
}
|
|
26
|
+
};
|
|
27
|
+
poll();
|
|
28
|
+
return cleanup;
|
|
29
|
+
}
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import * as esbuild from "esbuild";
|
|
2
|
+
import tmp from "tmp-promise";
|
|
3
|
+
import fs from "fs/promises";
|
|
4
|
+
import path from "path";
|
|
5
|
+
import { createClientBuildConfig } from "../../build/client/create-client-build-config.js";
|
|
6
|
+
import { generateClientEntry } from "../../build/client/generate-client-entry.js";
|
|
7
|
+
import { createServerBuildConfig } from "../../build/server/create-server-build-config.js";
|
|
8
|
+
import { generateServerEntry } from "../../build/server/generate-server-entry.js";
|
|
9
|
+
export async function prepareBuildContexts() {
|
|
10
|
+
const srcDir = "src";
|
|
11
|
+
const assetsDir = path.join(srcDir, "assets");
|
|
12
|
+
const webhooksDir = path.join(srcDir, "webhooks");
|
|
13
|
+
const eventsDir = path.join(srcDir, "events");
|
|
14
|
+
return Promise.all([
|
|
15
|
+
tmp.file({ postfix: ".js" }).then(async (tempFile) => {
|
|
16
|
+
let lastJS;
|
|
17
|
+
const updateTempFile = async () => {
|
|
18
|
+
const js = await generateClientEntry({
|
|
19
|
+
srcDirAbsolute: path.resolve(srcDir),
|
|
20
|
+
assetsDirAbsolute: path.resolve(assetsDir),
|
|
21
|
+
});
|
|
22
|
+
if (js === lastJS)
|
|
23
|
+
return;
|
|
24
|
+
lastJS = js;
|
|
25
|
+
await fs.writeFile(tempFile.path, js);
|
|
26
|
+
};
|
|
27
|
+
const esbuildContext = await esbuild.context({
|
|
28
|
+
...createClientBuildConfig({
|
|
29
|
+
entryPoint: tempFile.path,
|
|
30
|
+
srcDir,
|
|
31
|
+
}),
|
|
32
|
+
write: false,
|
|
33
|
+
outfile: path.resolve("dist", "index.js"),
|
|
34
|
+
loader: { ".png": "dataurl", ".graphql": "text", ".gql": "text" },
|
|
35
|
+
});
|
|
36
|
+
return {
|
|
37
|
+
rebuild: async () => {
|
|
38
|
+
await updateTempFile();
|
|
39
|
+
return esbuildContext.rebuild();
|
|
40
|
+
},
|
|
41
|
+
dispose: async () => {
|
|
42
|
+
await Promise.all([esbuildContext.dispose(), tempFile.cleanup()]);
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
}),
|
|
46
|
+
tmp.file({ postfix: ".js" }).then(async (tempFile) => {
|
|
47
|
+
let lastJS;
|
|
48
|
+
const updateTempFile = async () => {
|
|
49
|
+
const js = await generateServerEntry({
|
|
50
|
+
srcDirAbsolute: path.resolve(srcDir),
|
|
51
|
+
webhooksDirAbsolute: path.resolve(webhooksDir),
|
|
52
|
+
eventDirAbsolute: path.resolve(eventsDir),
|
|
53
|
+
});
|
|
54
|
+
if (js === lastJS)
|
|
55
|
+
return;
|
|
56
|
+
lastJS = js;
|
|
57
|
+
await fs.writeFile(tempFile.path, js);
|
|
58
|
+
};
|
|
59
|
+
const esbuildContext = await esbuild.context({
|
|
60
|
+
...createServerBuildConfig(tempFile.path),
|
|
61
|
+
write: false,
|
|
62
|
+
outfile: path.resolve("dist", "server.js"),
|
|
63
|
+
});
|
|
64
|
+
return {
|
|
65
|
+
rebuild: async () => {
|
|
66
|
+
await updateTempFile();
|
|
67
|
+
return esbuildContext.rebuild();
|
|
68
|
+
},
|
|
69
|
+
dispose: async () => {
|
|
70
|
+
await Promise.all([esbuildContext.dispose(), tempFile.cleanup()]);
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
}),
|
|
74
|
+
]);
|
|
75
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import Spinner from "tiny-spinner";
|
|
2
|
+
import notifier from "node-notifier";
|
|
3
|
+
import { startUpload } from "../../api/start-upload.js";
|
|
4
|
+
import { completeBundleUpload } from "../../api/complete-bundle-upload.js";
|
|
5
|
+
export async function upload({ token, contents, devVersionId, appId, }) {
|
|
6
|
+
const spinner = new Spinner();
|
|
7
|
+
try {
|
|
8
|
+
spinner.start("Uploading...");
|
|
9
|
+
const { client_bundle_upload_url, server_bundle_upload_url, app_dev_version_bundle_id: bundleId, } = await startUpload({
|
|
10
|
+
token,
|
|
11
|
+
appId,
|
|
12
|
+
devVersionId,
|
|
13
|
+
});
|
|
14
|
+
const [clientBundle, serverBundle] = contents;
|
|
15
|
+
await Promise.all([
|
|
16
|
+
fetch(client_bundle_upload_url, {
|
|
17
|
+
method: "PUT",
|
|
18
|
+
body: clientBundle,
|
|
19
|
+
headers: {
|
|
20
|
+
"Content-Type": "text/javascript",
|
|
21
|
+
"Content-Length": String(Buffer.from(clientBundle).length),
|
|
22
|
+
},
|
|
23
|
+
}),
|
|
24
|
+
fetch(server_bundle_upload_url, {
|
|
25
|
+
method: "PUT",
|
|
26
|
+
body: serverBundle,
|
|
27
|
+
headers: {
|
|
28
|
+
"Content-Type": "text/javascript",
|
|
29
|
+
"Content-Length": String(Buffer.from(serverBundle).length),
|
|
30
|
+
},
|
|
31
|
+
}),
|
|
32
|
+
]).catch((error) => {
|
|
33
|
+
process.stderr.write(`Upload Error: ${error}`);
|
|
34
|
+
});
|
|
35
|
+
await completeBundleUpload({
|
|
36
|
+
token,
|
|
37
|
+
appId,
|
|
38
|
+
devVersionId,
|
|
39
|
+
bundleId,
|
|
40
|
+
});
|
|
41
|
+
spinner.success(`Upload completed at ${new Date().toLocaleString()}.`);
|
|
42
|
+
notifier.notify({
|
|
43
|
+
title: "Upload Complete",
|
|
44
|
+
message: "New bundle uploaded to Attio",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
catch (error) {
|
|
48
|
+
if (error instanceof Error) {
|
|
49
|
+
spinner.error(`Upload failed: ${error.message}`);
|
|
50
|
+
notifier.notify({
|
|
51
|
+
title: "Upload Failed",
|
|
52
|
+
message: "Bundle upload to Attio failed",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
throw error;
|
|
56
|
+
}
|
|
57
|
+
finally {
|
|
58
|
+
spinner.stop();
|
|
59
|
+
}
|
|
60
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import chokidar from "chokidar";
|
|
2
|
+
import notifier from "node-notifier";
|
|
3
|
+
import { getDiagnostics, readConfig, typeScriptErrorSchema, } from "../../util/typescript.js";
|
|
4
|
+
import { printTsError } from "../../util/typescript.js";
|
|
5
|
+
import { clearTerminal } from "../../util/clear-terminal.js";
|
|
6
|
+
import path from "path";
|
|
7
|
+
const notify = (errors) => {
|
|
8
|
+
notifier.notify({
|
|
9
|
+
title: `TypeScript Error${errors.length === 1 ? "" : "s"}`,
|
|
10
|
+
message: `There ${errors.length === 1 ? "was one error" : `were ${errors.length} errors`} in your TypeScript code`,
|
|
11
|
+
});
|
|
12
|
+
};
|
|
13
|
+
export function validateTypeScript(onSuccess) {
|
|
14
|
+
let isShuttingDown = false;
|
|
15
|
+
const watcher = chokidar.watch(["src/**/*.ts", "src/**/*.tsx"], {
|
|
16
|
+
ignored: [
|
|
17
|
+
"**/node_modules/**",
|
|
18
|
+
"**/dist/**",
|
|
19
|
+
"**/*.graphql.d.ts",
|
|
20
|
+
"**/*.gql.d.ts",
|
|
21
|
+
],
|
|
22
|
+
cwd: ".",
|
|
23
|
+
persistent: true,
|
|
24
|
+
awaitWriteFinish: {
|
|
25
|
+
stabilityThreshold: 100,
|
|
26
|
+
pollInterval: 100,
|
|
27
|
+
},
|
|
28
|
+
});
|
|
29
|
+
async function handleValidation() {
|
|
30
|
+
if (isShuttingDown)
|
|
31
|
+
return;
|
|
32
|
+
try {
|
|
33
|
+
const program = await readConfig(path.resolve("tsconfig.json"));
|
|
34
|
+
if (program === "Not a TypeScript project") {
|
|
35
|
+
onSuccess?.();
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const errors = await getDiagnostics(program);
|
|
39
|
+
if (errors.length) {
|
|
40
|
+
clearTerminal();
|
|
41
|
+
errors.forEach(printTsError);
|
|
42
|
+
notify(errors);
|
|
43
|
+
}
|
|
44
|
+
else {
|
|
45
|
+
onSuccess?.();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
if (typeof error === "object" &&
|
|
50
|
+
error !== null &&
|
|
51
|
+
"code" in error &&
|
|
52
|
+
(error.code === "ENOENT" || error.code === "EACCES" || error.code === "EPERM")) {
|
|
53
|
+
throw error;
|
|
54
|
+
}
|
|
55
|
+
if (error instanceof Error) {
|
|
56
|
+
const tsError = typeScriptErrorSchema.parse({ text: error.message });
|
|
57
|
+
clearTerminal();
|
|
58
|
+
printTsError(tsError);
|
|
59
|
+
notify([tsError]);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
let watcherReady = false;
|
|
64
|
+
watcher.on("ready", () => {
|
|
65
|
+
watcherReady = true;
|
|
66
|
+
handleValidation();
|
|
67
|
+
watcher.on("all", (event, path) => {
|
|
68
|
+
if (event === "add" || event === "change" || event === "unlink") {
|
|
69
|
+
handleValidation();
|
|
70
|
+
}
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
watcher.on("error", (error) => {
|
|
74
|
+
process.stderr.write(`TypeScript watcher error: ${error}\n`);
|
|
75
|
+
});
|
|
76
|
+
return [
|
|
77
|
+
() => {
|
|
78
|
+
isShuttingDown = true;
|
|
79
|
+
if (watcherReady) {
|
|
80
|
+
try {
|
|
81
|
+
watcher.close();
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
process.stderr.write(`Error closing TypeScript watcher: ${error}\n`);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
handleValidation,
|
|
89
|
+
];
|
|
90
|
+
}
|
package/lib/commands/dev.js
CHANGED
|
@@ -1,26 +1,45 @@
|
|
|
1
1
|
import { Command, Option } from "commander";
|
|
2
|
-
import { createActor } from "xstate";
|
|
3
2
|
import { z } from "zod";
|
|
4
|
-
import
|
|
3
|
+
import chalk from "chalk";
|
|
4
|
+
import { boot } from "./dev/boot.js";
|
|
5
|
+
import { bundleJavaScript } from "./dev/bundle-javascript.js";
|
|
6
|
+
import { upload } from "./dev/upload.js";
|
|
7
|
+
import { printMessage } from "../util/print-message.js";
|
|
8
|
+
import { onboarding } from "./dev/onboarding.js";
|
|
9
|
+
import { validateTypeScript } from "./dev/validate-typescript.js";
|
|
10
|
+
import { graphqlCodeGen } from "./dev/graphql-code-gen.js";
|
|
5
11
|
export const optionsSchema = z.object({
|
|
6
|
-
dev: z.boolean().default(false),
|
|
7
12
|
workspace: z.string().optional(),
|
|
8
13
|
});
|
|
9
14
|
export const dev = new Command("dev")
|
|
10
15
|
.description("Develop your Attio app")
|
|
11
16
|
.addOption(new Option("--dev", "Run in development mode (additional debugging info)"))
|
|
12
17
|
.addOption(new Option("--workspace", "The slug of the workspace to use"))
|
|
13
|
-
.action((unparsedOptions) => {
|
|
14
|
-
const
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
.action(async (unparsedOptions) => {
|
|
19
|
+
const { workspace: workspaceSlug } = optionsSchema.parse(unparsedOptions);
|
|
20
|
+
const cleanupFunctions = [];
|
|
21
|
+
try {
|
|
22
|
+
const { token, appId, devVersionId, appSlug, workspace } = await boot({ workspaceSlug });
|
|
23
|
+
const cleanupOnboardingDaemon = onboarding({ token, appId, appSlug, workspace });
|
|
24
|
+
cleanupFunctions.push(cleanupOnboardingDaemon);
|
|
25
|
+
const [cleanupTs, triggerTs] = validateTypeScript();
|
|
26
|
+
cleanupFunctions.push(cleanupTs);
|
|
27
|
+
const cleanupGraphqlCodeGen = graphqlCodeGen(triggerTs);
|
|
28
|
+
cleanupFunctions.push(cleanupGraphqlCodeGen);
|
|
29
|
+
const cleanupJs = bundleJavaScript((contents) => {
|
|
30
|
+
upload({ token, contents, devVersionId, appId });
|
|
23
31
|
});
|
|
32
|
+
cleanupFunctions.push(cleanupJs);
|
|
33
|
+
printMessage("\n👀 Watching for changes...");
|
|
34
|
+
await new Promise((resolve) => {
|
|
35
|
+
process.on("SIGINT", async () => {
|
|
36
|
+
await Promise.all(cleanupFunctions.map(async (cleanup) => cleanup()));
|
|
37
|
+
resolve(undefined);
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
process.stderr.write(`${chalk.red("✖ " + error)}\n`);
|
|
43
|
+
process.exit(1);
|
|
24
44
|
}
|
|
25
|
-
actor.start();
|
|
26
45
|
});
|
package/lib/machines/actors.js
CHANGED
|
@@ -86,7 +86,7 @@ export const fromCallbackWithErrorHandling = (callback) => fromCallback((...args
|
|
|
86
86
|
});
|
|
87
87
|
}
|
|
88
88
|
});
|
|
89
|
-
export
|
|
89
|
+
export async function determineWorkspace({ token, workspaceSlug, }) {
|
|
90
90
|
const spinner = new Spinner();
|
|
91
91
|
spinner.start("Loading workspaces...");
|
|
92
92
|
try {
|
|
@@ -120,4 +120,4 @@ ${APP}/welcome/workspace-details
|
|
|
120
120
|
finally {
|
|
121
121
|
spinner.stop();
|
|
122
122
|
}
|
|
123
|
-
}
|
|
123
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
export function listenForKey(targetKey, onKeyPress) {
|
|
2
|
+
let cleanupFunction;
|
|
3
|
+
function handleKeyPress(data) {
|
|
4
|
+
const rawKey = data.toString();
|
|
5
|
+
const key = rawKey.trim().toLowerCase();
|
|
6
|
+
if (key === "\u0003") {
|
|
7
|
+
return;
|
|
8
|
+
}
|
|
9
|
+
if (key === targetKey.toLowerCase()) {
|
|
10
|
+
onKeyPress();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
if (process.stdin.isTTY) {
|
|
14
|
+
process.stdin.setRawMode(true);
|
|
15
|
+
process.stdin.resume();
|
|
16
|
+
process.stdin.setEncoding("utf8");
|
|
17
|
+
process.stdin.on("data", handleKeyPress);
|
|
18
|
+
}
|
|
19
|
+
cleanupFunction = () => {
|
|
20
|
+
if (process.stdin.isTTY) {
|
|
21
|
+
process.stdin.removeListener("data", handleKeyPress);
|
|
22
|
+
process.stdin.setRawMode(false);
|
|
23
|
+
process.stdin.pause();
|
|
24
|
+
}
|
|
25
|
+
};
|
|
26
|
+
return cleanupFunction;
|
|
27
|
+
}
|
|
@@ -12,14 +12,14 @@ export function loadAttioCliPackageJson() {
|
|
|
12
12
|
if (packageJson === undefined) {
|
|
13
13
|
const packageJsonPath = findUpSync(FILE_NAME, { cwd: fileURLToPath(import.meta.url) });
|
|
14
14
|
if (packageJsonPath === undefined) {
|
|
15
|
-
|
|
15
|
+
throw new Error("Failed to find package.json");
|
|
16
16
|
}
|
|
17
17
|
try {
|
|
18
18
|
const packageJsonContent = JSON.parse(readFileSync(packageJsonPath, "utf8"));
|
|
19
19
|
packageJson = packageJsonSchema.parse(packageJsonContent);
|
|
20
20
|
}
|
|
21
21
|
catch (error) {
|
|
22
|
-
|
|
22
|
+
throw new Error("Failed to find version in package.json");
|
|
23
23
|
}
|
|
24
24
|
}
|
|
25
25
|
return packageJson;
|
package/lib/util/load-env.js
CHANGED
package/package.json
CHANGED
|
@@ -1,619 +0,0 @@
|
|
|
1
|
-
import chokidar from "chokidar";
|
|
2
|
-
import open from "open";
|
|
3
|
-
import { assign, setup, enqueueActions, fromPromise } from "xstate";
|
|
4
|
-
import { APP } from "../env.js";
|
|
5
|
-
import { completeBundleUpload } from "../api/complete-bundle-upload.js";
|
|
6
|
-
import { createDevVersion } from "../api/create-dev-version.js";
|
|
7
|
-
import { startGraphqlServer } from "../api/start-graphql-server.js";
|
|
8
|
-
import { startUpload } from "../api/start-upload.js";
|
|
9
|
-
import notifier from "node-notifier";
|
|
10
|
-
import { loadEnv } from "../util/load-env.js";
|
|
11
|
-
import { loadAttioCliPackageJson } from "../util/load-attio-cli-package-json.js";
|
|
12
|
-
import { codeGenMachine } from "./code-gen-machine.js";
|
|
13
|
-
import { envMachine } from "./env-machine.js";
|
|
14
|
-
import { jsMachine } from "./js-machine.js";
|
|
15
|
-
import { tsMachine } from "./ts-machine.js";
|
|
16
|
-
import { fetchInstallation } from "../api/fetch-installation.js";
|
|
17
|
-
import Spinner from "tiny-spinner";
|
|
18
|
-
import { printMessage } from "../util/print-message.js";
|
|
19
|
-
import { printJsError, printTsError } from "../util/typescript.js";
|
|
20
|
-
import { clearTerminal } from "../util/clear-terminal.js";
|
|
21
|
-
import { setTerminalTitle } from "../util/set-terminal-title.js";
|
|
22
|
-
import { printLogo, showActorError } from "./actions.js";
|
|
23
|
-
import { askWithTypedChoices, authenticate, determineWorkspace, fromCallbackWithErrorHandling, loadAppInfo, loadAppSlug, } from "./actors.js";
|
|
24
|
-
import { ensureAuthed } from "../api/ensure-authed.js";
|
|
25
|
-
process.on("SIGINT", () => {
|
|
26
|
-
process.stdout.write("\x1B[?25h");
|
|
27
|
-
process.exit();
|
|
28
|
-
});
|
|
29
|
-
process.on("exit", () => {
|
|
30
|
-
process.stdout.write("\x1B[?25h");
|
|
31
|
-
});
|
|
32
|
-
export const devMachine = setup({
|
|
33
|
-
types: {
|
|
34
|
-
context: {},
|
|
35
|
-
events: {},
|
|
36
|
-
input: {},
|
|
37
|
-
children: {},
|
|
38
|
-
},
|
|
39
|
-
actors: {
|
|
40
|
-
"javascript": jsMachine,
|
|
41
|
-
"typescript": tsMachine,
|
|
42
|
-
"env": envMachine,
|
|
43
|
-
"code-gen": codeGenMachine,
|
|
44
|
-
"askForWorkspace": askWithTypedChoices(),
|
|
45
|
-
authenticate,
|
|
46
|
-
loadAppSlug,
|
|
47
|
-
loadAppInfo,
|
|
48
|
-
"keyboard": fromCallbackWithErrorHandling(({ sendBack }) => {
|
|
49
|
-
if (!process.stdin.isTTY)
|
|
50
|
-
return;
|
|
51
|
-
const originalIsRaw = process.stdin.isRaw;
|
|
52
|
-
const originalIsTTY = process.stdin.isTTY;
|
|
53
|
-
const ensureRawMode = () => {
|
|
54
|
-
if (process.stdin.isTTY) {
|
|
55
|
-
process.stdin.setRawMode(true);
|
|
56
|
-
}
|
|
57
|
-
};
|
|
58
|
-
const handleData = (data) => {
|
|
59
|
-
ensureRawMode();
|
|
60
|
-
const str = data.toString();
|
|
61
|
-
if (str === "\u0003") {
|
|
62
|
-
process.stdout.write("\x1B[?25h");
|
|
63
|
-
process.stdin.setRawMode(originalIsRaw);
|
|
64
|
-
process.exit();
|
|
65
|
-
}
|
|
66
|
-
switch (str) {
|
|
67
|
-
case "o":
|
|
68
|
-
sendBack({ type: "Open GraphQL Explorer" });
|
|
69
|
-
break;
|
|
70
|
-
case "i":
|
|
71
|
-
sendBack({ type: "Install Opened" });
|
|
72
|
-
break;
|
|
73
|
-
}
|
|
74
|
-
};
|
|
75
|
-
process.stdin.setRawMode(true);
|
|
76
|
-
process.stdin.resume();
|
|
77
|
-
process.stdin.on("data", handleData);
|
|
78
|
-
process.stdin.on("focus", ensureRawMode);
|
|
79
|
-
process.stdout.on("focus", ensureRawMode);
|
|
80
|
-
process.on("focus", ensureRawMode);
|
|
81
|
-
return () => {
|
|
82
|
-
process.stdin.removeListener("data", handleData);
|
|
83
|
-
process.stdin.removeListener("focus", ensureRawMode);
|
|
84
|
-
process.stdout.removeListener("focus", ensureRawMode);
|
|
85
|
-
process.removeListener("focus", ensureRawMode);
|
|
86
|
-
process.stdin.setRawMode(originalIsRaw);
|
|
87
|
-
if (originalIsTTY) {
|
|
88
|
-
process.stdin.pause();
|
|
89
|
-
}
|
|
90
|
-
};
|
|
91
|
-
}),
|
|
92
|
-
determineWorkspace,
|
|
93
|
-
"prepareUpload": fromPromise(async ({ input: { appInfo, workspace } }) => {
|
|
94
|
-
const environmentVariables = await loadEnv();
|
|
95
|
-
if (typeof environmentVariables === "string")
|
|
96
|
-
throw environmentVariables;
|
|
97
|
-
const packageJson = loadAttioCliPackageJson();
|
|
98
|
-
if (typeof packageJson === "string")
|
|
99
|
-
throw packageJson;
|
|
100
|
-
const devVersion = await createDevVersion({
|
|
101
|
-
token: await ensureAuthed(),
|
|
102
|
-
appId: appInfo.app_id,
|
|
103
|
-
targetWorkspaceId: workspace.workspace_id,
|
|
104
|
-
environmentVariables,
|
|
105
|
-
cliVersion: packageJson.version,
|
|
106
|
-
});
|
|
107
|
-
return devVersion;
|
|
108
|
-
}),
|
|
109
|
-
"upload": fromPromise(async ({ input: { token, contents, devVersion: { app_dev_version_id: devVersionId }, appInfo: { app_id: appId }, }, }) => {
|
|
110
|
-
const spinner = new Spinner();
|
|
111
|
-
try {
|
|
112
|
-
spinner.start("Uploading...");
|
|
113
|
-
const { client_bundle_upload_url, server_bundle_upload_url, app_dev_version_bundle_id: bundleId, } = await startUpload({
|
|
114
|
-
token,
|
|
115
|
-
appId,
|
|
116
|
-
devVersionId,
|
|
117
|
-
});
|
|
118
|
-
const [clientBundle, serverBundle] = contents;
|
|
119
|
-
await Promise.all([
|
|
120
|
-
fetch(client_bundle_upload_url, {
|
|
121
|
-
method: "PUT",
|
|
122
|
-
body: clientBundle,
|
|
123
|
-
headers: {
|
|
124
|
-
"Content-Type": "text/javascript",
|
|
125
|
-
"Content-Length": String(Buffer.from(clientBundle).length),
|
|
126
|
-
},
|
|
127
|
-
}),
|
|
128
|
-
fetch(server_bundle_upload_url, {
|
|
129
|
-
method: "PUT",
|
|
130
|
-
body: serverBundle,
|
|
131
|
-
headers: {
|
|
132
|
-
"Content-Type": "text/javascript",
|
|
133
|
-
"Content-Length": String(Buffer.from(serverBundle).length),
|
|
134
|
-
},
|
|
135
|
-
}),
|
|
136
|
-
]).catch((error) => {
|
|
137
|
-
process.stderr.write(`Upload Error: ${error}`);
|
|
138
|
-
});
|
|
139
|
-
await completeBundleUpload({
|
|
140
|
-
token,
|
|
141
|
-
appId,
|
|
142
|
-
devVersionId,
|
|
143
|
-
bundleId,
|
|
144
|
-
});
|
|
145
|
-
spinner.success(`Upload completed at ${new Date().toLocaleString()}.`);
|
|
146
|
-
notifier.notify({
|
|
147
|
-
title: "Upload Complete",
|
|
148
|
-
message: "New bundle uploaded to Attio",
|
|
149
|
-
});
|
|
150
|
-
}
|
|
151
|
-
catch (error) {
|
|
152
|
-
if (error instanceof Error) {
|
|
153
|
-
spinner.error(`Upload failed: ${error.message}`);
|
|
154
|
-
notifier.notify({
|
|
155
|
-
title: "Upload Failed",
|
|
156
|
-
message: "Bundle upload to Attio failed",
|
|
157
|
-
});
|
|
158
|
-
}
|
|
159
|
-
throw error;
|
|
160
|
-
}
|
|
161
|
-
finally {
|
|
162
|
-
spinner.stop();
|
|
163
|
-
}
|
|
164
|
-
}),
|
|
165
|
-
"watch": fromCallbackWithErrorHandling(({ sendBack }) => {
|
|
166
|
-
const watcher = chokidar.watch(["src", ".env"], {
|
|
167
|
-
ignored: "**/*.graphql.d.ts",
|
|
168
|
-
});
|
|
169
|
-
watcher.on("ready", () => watcher.on("all", () => {
|
|
170
|
-
sendBack({ type: "Change" });
|
|
171
|
-
}));
|
|
172
|
-
return () => {
|
|
173
|
-
watcher.close();
|
|
174
|
-
};
|
|
175
|
-
}),
|
|
176
|
-
"graphql": fromCallbackWithErrorHandling(({ sendBack }) => startGraphqlServer(sendBack)),
|
|
177
|
-
"loadInstallation": fromCallbackWithErrorHandling(({ sendBack, input: { token, appInfo, workspace } }) => {
|
|
178
|
-
fetchInstallation({
|
|
179
|
-
token,
|
|
180
|
-
appId: appInfo.app_id,
|
|
181
|
-
workspaceId: workspace.workspace_id,
|
|
182
|
-
}).then((installation) => {
|
|
183
|
-
if (installation) {
|
|
184
|
-
sendBack({ type: "Found Installation" });
|
|
185
|
-
}
|
|
186
|
-
else {
|
|
187
|
-
sendBack({ type: "No Installation" });
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
}),
|
|
191
|
-
"pollForInstallation": fromCallbackWithErrorHandling(({ sendBack, input: { token, appInfo, workspace } }) => {
|
|
192
|
-
const interval = setInterval(() => {
|
|
193
|
-
fetchInstallation({
|
|
194
|
-
token,
|
|
195
|
-
appId: appInfo.app_id,
|
|
196
|
-
workspaceId: workspace.workspace_id,
|
|
197
|
-
}).then((installation) => {
|
|
198
|
-
if (installation) {
|
|
199
|
-
sendBack({ type: "Found Installation" });
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
}, 2_000);
|
|
203
|
-
return () => clearInterval(interval);
|
|
204
|
-
}),
|
|
205
|
-
},
|
|
206
|
-
actions: {
|
|
207
|
-
clearUploadError: assign({ uploadError: undefined }),
|
|
208
|
-
showActorError,
|
|
209
|
-
printAuthError: (_, params) => {
|
|
210
|
-
process.stderr.write(`${params.error.message}\n\n`);
|
|
211
|
-
},
|
|
212
|
-
printError: (_, params) => {
|
|
213
|
-
printMessage(params.error);
|
|
214
|
-
},
|
|
215
|
-
printLogo: (_) => {
|
|
216
|
-
process.stdout.write("\x1B[?25l");
|
|
217
|
-
printLogo();
|
|
218
|
-
},
|
|
219
|
-
printWatching: (_) => {
|
|
220
|
-
printMessage("👀 Watching for changes...");
|
|
221
|
-
},
|
|
222
|
-
printTypeScriptErrors: (_, params) => {
|
|
223
|
-
if (params.errors.length) {
|
|
224
|
-
clearTerminal();
|
|
225
|
-
params.errors.forEach(printTsError);
|
|
226
|
-
notifier.notify({
|
|
227
|
-
title: "TypeScript Errors",
|
|
228
|
-
message: `There ${params.errors.length === 1 ? "was one error" : `were ${params.errors.length} errors`} in your TypeScript code`,
|
|
229
|
-
});
|
|
230
|
-
}
|
|
231
|
-
},
|
|
232
|
-
printJavaScriptErrors: (_, params) => {
|
|
233
|
-
if (params.errors.errors?.length || params.errors.warnings?.length) {
|
|
234
|
-
clearTerminal();
|
|
235
|
-
params.errors.errors?.forEach((error) => {
|
|
236
|
-
printJsError(error, "error");
|
|
237
|
-
});
|
|
238
|
-
params.errors.warnings?.forEach((warning) => {
|
|
239
|
-
printJsError(warning, "warning");
|
|
240
|
-
});
|
|
241
|
-
}
|
|
242
|
-
},
|
|
243
|
-
promptToInstall: (_) => {
|
|
244
|
-
process.stdout.write(`\n\n🚨 IMPORTANT: You will need to install your app in your workspace. Press "i" to open the app settings page, and then click "Install".\n\n`);
|
|
245
|
-
},
|
|
246
|
-
promptToOpenGraphQLExplorer: (_) => {
|
|
247
|
-
process.stdout.write(`\n\n Press "o" to open GraphQL Explorer.\n\n`);
|
|
248
|
-
},
|
|
249
|
-
openGraphQLExplorer: ({ context }) => {
|
|
250
|
-
open(`http://localhost:${context.graphqlPort}/graphql`);
|
|
251
|
-
},
|
|
252
|
-
openInstallPage: ({ context }) => {
|
|
253
|
-
open(`${APP}/${context.workspace.slug}/settings/apps/${context.appSlug}`);
|
|
254
|
-
},
|
|
255
|
-
saveTypeScriptErrors: assign({
|
|
256
|
-
typeScriptErrors: (_, params) => params.errors,
|
|
257
|
-
}),
|
|
258
|
-
saveJavaScriptErrors: assign({
|
|
259
|
-
javaScriptErrors: (_, params) => params.errors,
|
|
260
|
-
}),
|
|
261
|
-
clearTypeScriptErrors: assign({
|
|
262
|
-
typeScriptErrors: () => [],
|
|
263
|
-
}),
|
|
264
|
-
clearJavaScriptErrors: assign({
|
|
265
|
-
javaScriptErrors: () => undefined,
|
|
266
|
-
}),
|
|
267
|
-
sendChange: enqueueActions(({ enqueue, event }) => {
|
|
268
|
-
enqueue.sendTo("javascript", event);
|
|
269
|
-
enqueue.sendTo("typescript", event);
|
|
270
|
-
enqueue.sendTo("env", event);
|
|
271
|
-
enqueue.sendTo("code-gen", event);
|
|
272
|
-
}),
|
|
273
|
-
sendCodeGenerationDone: enqueueActions(({ enqueue }) => {
|
|
274
|
-
enqueue.sendTo("typescript", { type: "Change" });
|
|
275
|
-
}),
|
|
276
|
-
setToken: assign({
|
|
277
|
-
token: (_, params) => params.output,
|
|
278
|
-
}),
|
|
279
|
-
setAppInfo: assign({
|
|
280
|
-
appInfo: (_, params) => params.output,
|
|
281
|
-
}),
|
|
282
|
-
setAppSlug: assign({
|
|
283
|
-
appSlug: (_, params) => params.output,
|
|
284
|
-
}),
|
|
285
|
-
setWorkspace: assign({
|
|
286
|
-
workspace: (_, params) => params.output,
|
|
287
|
-
}),
|
|
288
|
-
setGraphqlPort: assign({
|
|
289
|
-
graphqlPort: (_, params) => params.port,
|
|
290
|
-
}),
|
|
291
|
-
setTerminalTitle: (_, params) => {
|
|
292
|
-
setTerminalTitle(params.devVersion?.app_id ? `attio dev – ${params.devVersion.app_id}` : `attio dev`);
|
|
293
|
-
},
|
|
294
|
-
setDevVersion: assign({
|
|
295
|
-
devVersion: (_, params) => params.output,
|
|
296
|
-
}),
|
|
297
|
-
setSuccess: assign({
|
|
298
|
-
jsContents: (_, params) => params.contents,
|
|
299
|
-
lastSuccessfulJavaScriptBuild: (_, params) => params.time,
|
|
300
|
-
}),
|
|
301
|
-
setUploadError: assign({
|
|
302
|
-
uploadError: (_, params) => params.error,
|
|
303
|
-
}),
|
|
304
|
-
},
|
|
305
|
-
}).createMachine({
|
|
306
|
-
context: ({ input }) => ({
|
|
307
|
-
jsContents: ["", ""],
|
|
308
|
-
appSlug: "",
|
|
309
|
-
token: "",
|
|
310
|
-
appInfo: { app_id: "", title: "" },
|
|
311
|
-
...input,
|
|
312
|
-
}),
|
|
313
|
-
id: "Dev Machine",
|
|
314
|
-
initial: "Authenticate",
|
|
315
|
-
states: {
|
|
316
|
-
"Watching": {
|
|
317
|
-
invoke: [
|
|
318
|
-
{
|
|
319
|
-
src: "javascript",
|
|
320
|
-
id: "javascript",
|
|
321
|
-
input: ({ self }) => ({ parentRef: self, write: false }),
|
|
322
|
-
},
|
|
323
|
-
{
|
|
324
|
-
src: "typescript",
|
|
325
|
-
id: "typescript",
|
|
326
|
-
input: ({ self }) => ({ parentRef: self }),
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
src: "env",
|
|
330
|
-
id: "env",
|
|
331
|
-
input: ({ self }) => ({ parentRef: self }),
|
|
332
|
-
},
|
|
333
|
-
{
|
|
334
|
-
src: "code-gen",
|
|
335
|
-
id: "code-gen",
|
|
336
|
-
input: ({ self }) => ({ parentRef: self }),
|
|
337
|
-
},
|
|
338
|
-
{ src: "watch" },
|
|
339
|
-
{
|
|
340
|
-
src: "keyboard",
|
|
341
|
-
},
|
|
342
|
-
],
|
|
343
|
-
states: {
|
|
344
|
-
JavaScript: {
|
|
345
|
-
states: {
|
|
346
|
-
"Building": {
|
|
347
|
-
on: {
|
|
348
|
-
"JavaScript Success": {
|
|
349
|
-
target: "Upload When Ready",
|
|
350
|
-
actions: [
|
|
351
|
-
{
|
|
352
|
-
type: "setSuccess",
|
|
353
|
-
params: ({ event }) => event,
|
|
354
|
-
},
|
|
355
|
-
"clearJavaScriptErrors",
|
|
356
|
-
],
|
|
357
|
-
},
|
|
358
|
-
"JavaScript Error": {
|
|
359
|
-
target: "Watching",
|
|
360
|
-
actions: [
|
|
361
|
-
{
|
|
362
|
-
type: "saveJavaScriptErrors",
|
|
363
|
-
params: ({ event }) => event,
|
|
364
|
-
},
|
|
365
|
-
{
|
|
366
|
-
type: "printJavaScriptErrors",
|
|
367
|
-
params: ({ event }) => event,
|
|
368
|
-
},
|
|
369
|
-
],
|
|
370
|
-
},
|
|
371
|
-
},
|
|
372
|
-
},
|
|
373
|
-
"Watching": {
|
|
374
|
-
entry: "printWatching",
|
|
375
|
-
},
|
|
376
|
-
"Uploading": {
|
|
377
|
-
invoke: {
|
|
378
|
-
src: "upload",
|
|
379
|
-
input: ({ context }) => {
|
|
380
|
-
return {
|
|
381
|
-
devVersion: context.devVersion,
|
|
382
|
-
appInfo: context.appInfo,
|
|
383
|
-
token: context.token,
|
|
384
|
-
contents: context.jsContents,
|
|
385
|
-
};
|
|
386
|
-
},
|
|
387
|
-
onDone: "Watching",
|
|
388
|
-
onError: {
|
|
389
|
-
target: "#Dev Machine.Error",
|
|
390
|
-
actions: { type: "showActorError", params: ({ event }) => event },
|
|
391
|
-
},
|
|
392
|
-
},
|
|
393
|
-
},
|
|
394
|
-
"Upload When Ready": {
|
|
395
|
-
always: {
|
|
396
|
-
target: "Uploading",
|
|
397
|
-
guard: ({ context }) => Boolean(context.devVersion),
|
|
398
|
-
},
|
|
399
|
-
},
|
|
400
|
-
},
|
|
401
|
-
initial: "Building",
|
|
402
|
-
on: {
|
|
403
|
-
"Change": {
|
|
404
|
-
target: "JavaScript",
|
|
405
|
-
actions: "sendChange",
|
|
406
|
-
},
|
|
407
|
-
"Auth Error": {
|
|
408
|
-
target: "#Dev Machine.Auth Error",
|
|
409
|
-
actions: {
|
|
410
|
-
type: "printAuthError",
|
|
411
|
-
params: ({ event }) => event,
|
|
412
|
-
},
|
|
413
|
-
},
|
|
414
|
-
},
|
|
415
|
-
invoke: {
|
|
416
|
-
src: "prepareUpload",
|
|
417
|
-
input: ({ context }) => ({
|
|
418
|
-
appInfo: context.appInfo,
|
|
419
|
-
workspace: context.workspace,
|
|
420
|
-
}),
|
|
421
|
-
onDone: {
|
|
422
|
-
target: "JavaScript",
|
|
423
|
-
actions: [
|
|
424
|
-
{ type: "setDevVersion", params: ({ event }) => event },
|
|
425
|
-
{ type: "setTerminalTitle", params: ({ event }) => event },
|
|
426
|
-
],
|
|
427
|
-
},
|
|
428
|
-
onError: {
|
|
429
|
-
target: "#Dev Machine.Error",
|
|
430
|
-
actions: { type: "showActorError", params: ({ event }) => event },
|
|
431
|
-
},
|
|
432
|
-
},
|
|
433
|
-
},
|
|
434
|
-
TypeScript: {
|
|
435
|
-
states: {
|
|
436
|
-
Validating: {
|
|
437
|
-
on: {
|
|
438
|
-
"TypeScript Success": {
|
|
439
|
-
target: "Watching",
|
|
440
|
-
actions: "clearTypeScriptErrors",
|
|
441
|
-
},
|
|
442
|
-
"TypeScript Error": {
|
|
443
|
-
target: "Watching",
|
|
444
|
-
actions: [
|
|
445
|
-
{
|
|
446
|
-
type: "printTypeScriptErrors",
|
|
447
|
-
params: ({ event }) => event,
|
|
448
|
-
},
|
|
449
|
-
{ type: "saveTypeScriptErrors", params: ({ event }) => event },
|
|
450
|
-
],
|
|
451
|
-
},
|
|
452
|
-
},
|
|
453
|
-
},
|
|
454
|
-
Watching: {},
|
|
455
|
-
},
|
|
456
|
-
initial: "Validating",
|
|
457
|
-
on: {
|
|
458
|
-
"Change": {
|
|
459
|
-
target: "TypeScript",
|
|
460
|
-
actions: "sendChange",
|
|
461
|
-
},
|
|
462
|
-
"Code Generation Done": {
|
|
463
|
-
target: "TypeScript",
|
|
464
|
-
description: `Re-enter TypeScript build when code generation is complete`,
|
|
465
|
-
actions: "sendCodeGenerationDone",
|
|
466
|
-
},
|
|
467
|
-
},
|
|
468
|
-
},
|
|
469
|
-
Graphql: {
|
|
470
|
-
invoke: {
|
|
471
|
-
src: "graphql",
|
|
472
|
-
},
|
|
473
|
-
states: {
|
|
474
|
-
"Not Started": {
|
|
475
|
-
on: {
|
|
476
|
-
"GraphQL Server Started": {
|
|
477
|
-
target: "Started",
|
|
478
|
-
actions: {
|
|
479
|
-
type: "setGraphqlPort",
|
|
480
|
-
params: ({ event }) => event,
|
|
481
|
-
},
|
|
482
|
-
},
|
|
483
|
-
},
|
|
484
|
-
},
|
|
485
|
-
"Started": {
|
|
486
|
-
on: {
|
|
487
|
-
"Open GraphQL Explorer": {
|
|
488
|
-
target: "Started",
|
|
489
|
-
actions: "openGraphQLExplorer",
|
|
490
|
-
},
|
|
491
|
-
},
|
|
492
|
-
},
|
|
493
|
-
},
|
|
494
|
-
initial: "Not Started",
|
|
495
|
-
},
|
|
496
|
-
Onboarding: {
|
|
497
|
-
states: {
|
|
498
|
-
"Looking for Installations": {
|
|
499
|
-
invoke: {
|
|
500
|
-
src: "loadInstallation",
|
|
501
|
-
input: ({ context }) => ({
|
|
502
|
-
appInfo: context.appInfo,
|
|
503
|
-
token: context.token,
|
|
504
|
-
devVersion: context.devVersion,
|
|
505
|
-
workspace: context.workspace,
|
|
506
|
-
}),
|
|
507
|
-
},
|
|
508
|
-
on: {
|
|
509
|
-
"Found Installation": "Done",
|
|
510
|
-
"No Installation": "Prompt to Install",
|
|
511
|
-
},
|
|
512
|
-
},
|
|
513
|
-
"Done": {
|
|
514
|
-
type: "final",
|
|
515
|
-
},
|
|
516
|
-
"Prompt to Install": {
|
|
517
|
-
entry: "promptToInstall",
|
|
518
|
-
},
|
|
519
|
-
"Poll For Installation": {
|
|
520
|
-
invoke: {
|
|
521
|
-
src: "pollForInstallation",
|
|
522
|
-
input: ({ context }) => ({
|
|
523
|
-
appInfo: context.appInfo,
|
|
524
|
-
token: context.token,
|
|
525
|
-
devVersion: context.devVersion,
|
|
526
|
-
workspace: context.workspace,
|
|
527
|
-
}),
|
|
528
|
-
},
|
|
529
|
-
on: {
|
|
530
|
-
"Found Installation": "Done",
|
|
531
|
-
},
|
|
532
|
-
after: {
|
|
533
|
-
"60000": "Prompt to Install",
|
|
534
|
-
},
|
|
535
|
-
},
|
|
536
|
-
},
|
|
537
|
-
initial: "Looking for Installations",
|
|
538
|
-
on: {
|
|
539
|
-
"Install Opened": {
|
|
540
|
-
target: ".Poll For Installation",
|
|
541
|
-
actions: "openInstallPage",
|
|
542
|
-
},
|
|
543
|
-
},
|
|
544
|
-
},
|
|
545
|
-
},
|
|
546
|
-
type: "parallel",
|
|
547
|
-
entry: "promptToOpenGraphQLExplorer",
|
|
548
|
-
},
|
|
549
|
-
"Authenticate": {
|
|
550
|
-
invoke: {
|
|
551
|
-
src: "authenticate",
|
|
552
|
-
onDone: {
|
|
553
|
-
target: "Loading App Slug",
|
|
554
|
-
actions: { type: "setToken", params: ({ event }) => event },
|
|
555
|
-
},
|
|
556
|
-
onError: {
|
|
557
|
-
target: "Error",
|
|
558
|
-
actions: {
|
|
559
|
-
type: "showActorError",
|
|
560
|
-
params: ({ event }) => event,
|
|
561
|
-
},
|
|
562
|
-
},
|
|
563
|
-
},
|
|
564
|
-
},
|
|
565
|
-
"Auth Error": {
|
|
566
|
-
type: "final",
|
|
567
|
-
},
|
|
568
|
-
"Loading App Info": {
|
|
569
|
-
invoke: {
|
|
570
|
-
src: "loadAppInfo",
|
|
571
|
-
input: ({ context }) => context,
|
|
572
|
-
onDone: {
|
|
573
|
-
target: "Determine Workspace",
|
|
574
|
-
actions: { type: "setAppInfo", params: ({ event }) => event },
|
|
575
|
-
},
|
|
576
|
-
onError: {
|
|
577
|
-
target: "Error",
|
|
578
|
-
actions: { type: "showActorError", params: ({ event }) => event },
|
|
579
|
-
},
|
|
580
|
-
},
|
|
581
|
-
},
|
|
582
|
-
"Loading App Slug": {
|
|
583
|
-
invoke: {
|
|
584
|
-
src: "loadAppSlug",
|
|
585
|
-
onDone: {
|
|
586
|
-
target: "Loading App Info",
|
|
587
|
-
actions: { type: "setAppSlug", params: ({ event }) => event },
|
|
588
|
-
},
|
|
589
|
-
onError: {
|
|
590
|
-
target: "Error",
|
|
591
|
-
actions: { type: "showActorError", params: ({ event }) => event },
|
|
592
|
-
},
|
|
593
|
-
},
|
|
594
|
-
},
|
|
595
|
-
"Error": {
|
|
596
|
-
type: "final",
|
|
597
|
-
},
|
|
598
|
-
"Determine Workspace": {
|
|
599
|
-
invoke: {
|
|
600
|
-
src: "determineWorkspace",
|
|
601
|
-
input: ({ context }) => context,
|
|
602
|
-
onDone: {
|
|
603
|
-
target: "Watching",
|
|
604
|
-
actions: [
|
|
605
|
-
{
|
|
606
|
-
type: "setWorkspace",
|
|
607
|
-
params: ({ event }) => event,
|
|
608
|
-
},
|
|
609
|
-
],
|
|
610
|
-
},
|
|
611
|
-
onError: {
|
|
612
|
-
target: "Error",
|
|
613
|
-
actions: { type: "showActorError", params: ({ event }) => event },
|
|
614
|
-
},
|
|
615
|
-
},
|
|
616
|
-
},
|
|
617
|
-
},
|
|
618
|
-
entry: "printLogo",
|
|
619
|
-
});
|