attio 0.0.1-experimental.20250408 → 0.0.1-experimental.20250414
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/api.js +99 -31
- package/lib/api/fetcher.js +31 -27
- package/lib/api/schemas.js +6 -0
- package/lib/auth/auth.js +135 -97
- package/lib/auth/keychain.js +86 -43
- package/lib/build/server/generate-server-entry.js +6 -6
- package/lib/build.js +2 -2
- package/lib/commands/build/build-javascript.js +1 -1
- package/lib/commands/build/validate-typescript.js +1 -1
- package/lib/commands/build.js +2 -2
- package/lib/commands/dev/boot.js +9 -9
- package/lib/commands/dev/bundle-javascript.js +1 -1
- package/lib/commands/dev/onboarding.js +3 -3
- package/lib/commands/dev/prepare-build-contexts.js +1 -1
- package/lib/commands/dev/upload.js +4 -21
- package/lib/commands/dev.js +5 -4
- package/lib/commands/init/create-project.js +7 -39
- package/lib/commands/init.js +6 -14
- package/lib/commands/login.js +8 -2
- package/lib/commands/logout.js +8 -2
- package/lib/commands/version/create/bundle-javascript.js +3 -3
- package/lib/commands/version/create.js +14 -14
- package/lib/commands/version/list.js +4 -4
- package/lib/commands/whoami.js +7 -8
- package/lib/errors.js +1 -0
- package/lib/print-errors.js +165 -0
- package/lib/spinners/determine-workspace.spinner.js +3 -26
- package/lib/spinners/get-app-info.spinner.js +2 -2
- package/lib/spinners/get-app-slug-from-package-json.js +1 -20
- package/lib/spinners/get-versions.spinner.js +2 -2
- package/lib/util/copy-with-replace.js +2 -2
- package/lib/util/create-directory.js +1 -1
- package/lib/util/load-attio-cli-version.js +1 -26
- package/lib/util/upload-bundle.js +1 -1
- package/package.json +1 -1
- package/lib/auth/ensure-authed.js +0 -14
- package/lib/commands/dev/start-graphql-server.js +0 -32
- package/lib/commands/init/ask-language.js +0 -11
- package/lib/templates/javascript/.env +0 -12
- package/lib/templates/javascript/eslint.config.js +0 -48
- package/lib/templates/javascript/package.json +0 -26
- package/lib/templates/javascript/src/assets/icon.png +0 -0
- package/lib/templates/javascript/src/cat-fact.jsx +0 -16
- package/lib/templates/javascript/src/get-cat-fact.server.js +0 -6
- package/lib/templates/javascript/src/hello-world-action.jsx +0 -17
- package/lib/templates/javascript/src/hello-world-dialog.jsx +0 -26
- package/lib/templates/typescript/.eslintignore +0 -3
- package/lib/templates/typescript/src/assets/icon.png +0 -0
- /package/lib/{templates/typescript → template}/.env +0 -0
- /package/lib/{templates/javascript → template}/.eslintignore +0 -0
- /package/lib/{templates/common → template}/README.md +0 -0
- /package/lib/{templates/typescript → template}/eslint.config.js +0 -0
- /package/lib/{templates/typescript → template}/package.json +0 -0
- /package/lib/{templates/common → template}/src/assets/icon.png +0 -0
- /package/lib/{templates/typescript → template}/src/cat-fact.tsx +0 -0
- /package/lib/{templates/typescript → template}/src/get-cat-fact.server.ts +0 -0
- /package/lib/{templates/typescript → template}/src/hello-world-action.tsx +0 -0
- /package/lib/{templates/typescript → template}/src/hello-world-dialog.tsx +0 -0
- /package/lib/{templates/typescript → template}/tsconfig.json +0 -0
package/lib/auth/keychain.js
CHANGED
|
@@ -1,52 +1,95 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { complete, errored, isErrored } from "@attio/fetchable";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
const authTokenSchema = z.object({
|
|
4
|
+
access_token: z.string(),
|
|
5
|
+
refresh_token: z.string(),
|
|
6
|
+
token_type: z.literal("Bearer"),
|
|
7
|
+
expires_at: z.number(),
|
|
8
|
+
});
|
|
9
|
+
class KeytarKeychain {
|
|
10
|
+
SERVICE_NAME = "attio-cli";
|
|
11
|
+
ACCOUNT_NAME = "developer";
|
|
12
|
+
keytarPromise;
|
|
13
|
+
constructor() {
|
|
14
|
+
this.keytarPromise = import("keytar").then((module) => module.default);
|
|
7
15
|
}
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
return hardExit(error instanceof Error ? error.message : "Failed to save token to keychain");
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
export async function loadAuthToken() {
|
|
26
|
-
if (process.env.NODE_ENV === "test") {
|
|
27
|
-
return TEST_TOKEN;
|
|
28
|
-
}
|
|
29
|
-
if (!keytar) {
|
|
30
|
-
return hardExit("Keychain functionality not available");
|
|
16
|
+
async save(token) {
|
|
17
|
+
const keytar = await this.keytarPromise;
|
|
18
|
+
try {
|
|
19
|
+
await keytar.setPassword(this.SERVICE_NAME, this.ACCOUNT_NAME, JSON.stringify(token));
|
|
20
|
+
return complete(undefined);
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
return errored({
|
|
24
|
+
code: "SAVE_KEYCHAIN_ERROR",
|
|
25
|
+
error,
|
|
26
|
+
});
|
|
27
|
+
}
|
|
31
28
|
}
|
|
32
|
-
|
|
33
|
-
|
|
29
|
+
async load() {
|
|
30
|
+
const keytar = await this.keytarPromise;
|
|
31
|
+
let unparsedToken = null;
|
|
32
|
+
try {
|
|
33
|
+
unparsedToken = await keytar.getPassword(this.SERVICE_NAME, this.ACCOUNT_NAME);
|
|
34
|
+
}
|
|
35
|
+
catch (error) {
|
|
36
|
+
return errored({ code: "LOAD_KEYCHAIN_ERROR", error });
|
|
37
|
+
}
|
|
38
|
+
if (unparsedToken === null) {
|
|
39
|
+
return complete(null);
|
|
40
|
+
}
|
|
41
|
+
let jsonToken;
|
|
42
|
+
try {
|
|
43
|
+
jsonToken = JSON.parse(unparsedToken);
|
|
44
|
+
}
|
|
45
|
+
catch (error) {
|
|
46
|
+
const deleteResult = await this.delete();
|
|
47
|
+
if (isErrored(deleteResult)) {
|
|
48
|
+
return errored({ code: "LOAD_KEYCHAIN_ERROR", error: deleteResult.error });
|
|
49
|
+
}
|
|
50
|
+
console.debug("Wiped keychain entry due to invalid JSON");
|
|
51
|
+
return complete(null);
|
|
52
|
+
}
|
|
53
|
+
const parsedToken = authTokenSchema.safeParse(jsonToken);
|
|
54
|
+
if (!parsedToken.success) {
|
|
55
|
+
const deleteResult = await this.delete();
|
|
56
|
+
if (isErrored(deleteResult)) {
|
|
57
|
+
return errored({ code: "LOAD_KEYCHAIN_ERROR", error: deleteResult.error });
|
|
58
|
+
}
|
|
59
|
+
console.debug("Wiped keychain entry due to schema mismatch");
|
|
60
|
+
return complete(null);
|
|
61
|
+
}
|
|
62
|
+
return complete(parsedToken.data);
|
|
34
63
|
}
|
|
35
|
-
|
|
36
|
-
|
|
64
|
+
async delete() {
|
|
65
|
+
const keytar = await this.keytarPromise;
|
|
66
|
+
try {
|
|
67
|
+
await keytar.deletePassword(this.SERVICE_NAME, this.ACCOUNT_NAME);
|
|
68
|
+
return complete(undefined);
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
return errored({
|
|
72
|
+
code: "DELETE_KEYCHAIN_ERROR",
|
|
73
|
+
error,
|
|
74
|
+
});
|
|
75
|
+
}
|
|
37
76
|
}
|
|
38
77
|
}
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
78
|
+
class TestKeychain {
|
|
79
|
+
TEST_TOKEN = {
|
|
80
|
+
access_token: "TEST",
|
|
81
|
+
refresh_token: "TEST",
|
|
82
|
+
token_type: "Bearer",
|
|
83
|
+
expires_at: Number.MAX_SAFE_INTEGER,
|
|
84
|
+
};
|
|
85
|
+
async save(_token) {
|
|
86
|
+
return complete(undefined);
|
|
45
87
|
}
|
|
46
|
-
|
|
47
|
-
|
|
88
|
+
async load() {
|
|
89
|
+
return complete(this.TEST_TOKEN);
|
|
48
90
|
}
|
|
49
|
-
|
|
50
|
-
return
|
|
91
|
+
async delete() {
|
|
92
|
+
return complete(undefined);
|
|
51
93
|
}
|
|
52
94
|
}
|
|
95
|
+
export const keychain = process.env.NODE_ENV === "test" ? new TestKeychain() : new KeytarKeychain();
|
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
import { glob } from "glob";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { errored, complete, isErrored, combineAsync } from "@attio/fetchable";
|
|
4
|
-
async function
|
|
4
|
+
async function findServerFunctionModules(cwd, pattern) {
|
|
5
5
|
try {
|
|
6
|
-
return complete(await glob(`**/*.${pattern}.
|
|
6
|
+
return complete(await glob(`**/*.${pattern}.{js,ts}`, { nodir: true, cwd }));
|
|
7
7
|
}
|
|
8
8
|
catch (error) {
|
|
9
9
|
return errored({
|
|
10
10
|
code: "ERROR_FINDING_SERVER_FUNCTION_MODULES",
|
|
11
|
-
|
|
11
|
+
path: cwd,
|
|
12
12
|
pattern,
|
|
13
13
|
error,
|
|
14
14
|
});
|
|
@@ -16,9 +16,9 @@ async function findPaths(dir, pattern) {
|
|
|
16
16
|
}
|
|
17
17
|
export async function generateServerEntry({ srcDirAbsolute, webhooksDirAbsolute, eventDirAbsolute, log, }) {
|
|
18
18
|
const pathsResult = await combineAsync([
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
19
|
+
findServerFunctionModules(srcDirAbsolute, "server"),
|
|
20
|
+
findServerFunctionModules(webhooksDirAbsolute, "webhook"),
|
|
21
|
+
findServerFunctionModules(eventDirAbsolute, "event"),
|
|
22
22
|
]);
|
|
23
23
|
if (isErrored(pathsResult)) {
|
|
24
24
|
return pathsResult;
|
package/lib/build.js
CHANGED
|
@@ -13,6 +13,6 @@ const jsErrorSchema = z.object({
|
|
|
13
13
|
}),
|
|
14
14
|
});
|
|
15
15
|
export const errorsAndWarningsSchema = z.object({
|
|
16
|
-
errors: z.array(jsErrorSchema).optional(),
|
|
17
|
-
warnings: z.array(jsErrorSchema).optional(),
|
|
16
|
+
errors: z.array(jsErrorSchema).optional().readonly(),
|
|
17
|
+
warnings: z.array(jsErrorSchema).optional().readonly(),
|
|
18
18
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { prepareBuildContexts, } from "../dev/prepare-build-contexts.js";
|
|
2
1
|
import { combineAsync, complete, isErrored } from "@attio/fetchable";
|
|
2
|
+
import { prepareBuildContexts } from "../dev/prepare-build-contexts.js";
|
|
3
3
|
export async function buildJavaScript() {
|
|
4
4
|
const buildContextsResult = await prepareBuildContexts("write-to-disk");
|
|
5
5
|
if (isErrored(buildContextsResult)) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import path from "path";
|
|
2
|
-
import { getDiagnostics, readConfig, typeScriptErrorSchema
|
|
2
|
+
import { getDiagnostics, readConfig, typeScriptErrorSchema } from "../../util/typescript.js";
|
|
3
3
|
import { complete, errored } from "@attio/fetchable";
|
|
4
4
|
export async function validateTypeScript() {
|
|
5
5
|
try {
|
package/lib/commands/build.js
CHANGED
|
@@ -24,8 +24,8 @@ export const build = new Command("build")
|
|
|
24
24
|
if (isErrored(jsResult)) {
|
|
25
25
|
if (jsResult.error.code === "BUILD_JAVASCRIPT_ERROR") {
|
|
26
26
|
const { errors, warnings } = jsResult.error;
|
|
27
|
-
errors
|
|
28
|
-
warnings
|
|
27
|
+
errors?.forEach((error) => printJsError(error, "error"));
|
|
28
|
+
warnings?.forEach((warning) => printJsError(warning, "warning"));
|
|
29
29
|
process.stderr.write("JavaScript build failed");
|
|
30
30
|
process.exit(1);
|
|
31
31
|
}
|
package/lib/commands/dev/boot.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { isErrored } from "@attio/fetchable";
|
|
2
|
+
import { printFetcherError, printCliVersionError, printPackageJsonError, printDetermineWorkspaceError, } from "../../print-errors.js";
|
|
3
|
+
import { getAppSlugFromPackageJson } from "../../spinners/get-app-slug-from-package-json.js";
|
|
4
|
+
import { determineWorkspace } from "../../spinners/determine-workspace.spinner.js";
|
|
5
|
+
import { loadAttioCliVersion } from "../../util/load-attio-cli-version.js";
|
|
6
|
+
import { api } from "../../api/api.js";
|
|
3
7
|
import { getAppInfo } from "../../spinners/get-app-info.spinner.js";
|
|
4
8
|
import { loadEnv } from "../../util/load-env.js";
|
|
5
|
-
import { printFetcherError } from "../../api/fetcher.js";
|
|
6
|
-
import { isErrored } from "@attio/fetchable";
|
|
7
|
-
import { loadAttioCliVersion, printCliVersionError } from "../../util/load-attio-cli-version.js";
|
|
8
|
-
import { API } from "../../api/api.js";
|
|
9
9
|
export async function boot({ workspaceSlug }) {
|
|
10
10
|
const appSlugResult = await getAppSlugFromPackageJson();
|
|
11
11
|
if (isErrored(appSlugResult)) {
|
|
@@ -15,7 +15,7 @@ export async function boot({ workspaceSlug }) {
|
|
|
15
15
|
const appSlug = appSlugResult.value;
|
|
16
16
|
const appInfoResult = await getAppInfo(appSlug);
|
|
17
17
|
if (isErrored(appInfoResult)) {
|
|
18
|
-
printFetcherError("Error loading app info", appInfoResult.error
|
|
18
|
+
printFetcherError("Error loading app info", appInfoResult.error);
|
|
19
19
|
process.exit(1);
|
|
20
20
|
}
|
|
21
21
|
const appInfo = appInfoResult.value;
|
|
@@ -32,14 +32,14 @@ export async function boot({ workspaceSlug }) {
|
|
|
32
32
|
process.exit(1);
|
|
33
33
|
}
|
|
34
34
|
const cliVersion = cliVersionResult.value;
|
|
35
|
-
const devVersionResult = await
|
|
35
|
+
const devVersionResult = await api.createDevVersion({
|
|
36
36
|
appId: appInfo.app_id,
|
|
37
37
|
cliVersion,
|
|
38
38
|
targetWorkspaceId: workspace.workspace_id,
|
|
39
39
|
environmentVariables,
|
|
40
40
|
});
|
|
41
41
|
if (isErrored(devVersionResult)) {
|
|
42
|
-
printFetcherError("Error creating dev version", devVersionResult.error
|
|
42
|
+
printFetcherError("Error creating dev version", devVersionResult.error);
|
|
43
43
|
process.exit(1);
|
|
44
44
|
}
|
|
45
45
|
const devVersion = devVersionResult.value;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import chokidar from "chokidar";
|
|
2
|
-
import { prepareBuildContexts, } from "./prepare-build-contexts.js";
|
|
3
2
|
import { combineAsync, complete, isErrored } from "@attio/fetchable";
|
|
3
|
+
import { prepareBuildContexts } from "./prepare-build-contexts.js";
|
|
4
4
|
export function bundleJavaScript(onSuccess, onError) {
|
|
5
5
|
const watcher = chokidar.watch(["./src/**/*.{js,jsx,ts,tsx}"], {
|
|
6
6
|
ignored: ["**/node_modules/**", "**/dist/**", "**/*.graphql.d.ts", "**/*.gql.d.ts"],
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { listenForKey } from "../../util/listen-for-key.js";
|
|
2
2
|
import open from "open";
|
|
3
3
|
import { APP } from "../../env.js";
|
|
4
|
-
import {
|
|
4
|
+
import { api } from "../../api/api.js";
|
|
5
5
|
import { isErrored } from "@attio/fetchable";
|
|
6
6
|
function prompt() {
|
|
7
7
|
process.stdout.write(`🚨 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`);
|
|
@@ -11,7 +11,7 @@ export function onboarding({ appId, appSlug, workspace, }) {
|
|
|
11
11
|
open(`${APP}/${workspace.slug}/settings/apps/${appSlug}`);
|
|
12
12
|
});
|
|
13
13
|
const poll = async () => {
|
|
14
|
-
const installationResult = await
|
|
14
|
+
const installationResult = await api.fetchInstallation({
|
|
15
15
|
appId,
|
|
16
16
|
workspaceId: workspace.workspace_id,
|
|
17
17
|
});
|
|
@@ -23,7 +23,7 @@ export function onboarding({ appId, appSlug, workspace, }) {
|
|
|
23
23
|
prompt();
|
|
24
24
|
while (!installation) {
|
|
25
25
|
await new Promise((resolve) => setTimeout(resolve, 60_000));
|
|
26
|
-
const installationResult = await
|
|
26
|
+
const installationResult = await api.fetchInstallation({
|
|
27
27
|
appId,
|
|
28
28
|
workspaceId: workspace.workspace_id,
|
|
29
29
|
});
|
|
@@ -3,7 +3,7 @@ import tmp from "tmp-promise";
|
|
|
3
3
|
import fs from "fs/promises";
|
|
4
4
|
import path from "path";
|
|
5
5
|
import { createClientBuildConfig } from "../../build/client/create-client-build-config.js";
|
|
6
|
-
import { generateClientEntry
|
|
6
|
+
import { generateClientEntry } from "../../build/client/generate-client-entry.js";
|
|
7
7
|
import { createServerBuildConfig } from "../../build/server/create-server-build-config.js";
|
|
8
8
|
import { generateServerEntry } from "../../build/server/generate-server-entry.js";
|
|
9
9
|
import { errored, isErrored, complete, combineAsync } from "@attio/fetchable";
|
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
import notifier from "node-notifier";
|
|
2
2
|
import { spinnerify } from "../../util/spinner.js";
|
|
3
3
|
import { uploadBundle } from "../../util/upload-bundle.js";
|
|
4
|
-
import {
|
|
5
|
-
import { isErrored,
|
|
6
|
-
import { printFetcherError } from "../../api/fetcher.js";
|
|
7
|
-
import chalk from "chalk";
|
|
4
|
+
import { api } from "../../api/api.js";
|
|
5
|
+
import { isErrored, complete, combineAsync } from "@attio/fetchable";
|
|
8
6
|
export async function upload({ contents, devVersionId, appId, }) {
|
|
9
7
|
return await spinnerify("Uploading...", () => `Upload complete at ${new Date().toLocaleTimeString()}`, async () => {
|
|
10
|
-
const startUploadResult = await
|
|
8
|
+
const startUploadResult = await api.startUpload({
|
|
11
9
|
appId,
|
|
12
10
|
devVersionId,
|
|
13
11
|
});
|
|
@@ -23,7 +21,7 @@ export async function upload({ contents, devVersionId, appId, }) {
|
|
|
23
21
|
if (isErrored(uploadResults)) {
|
|
24
22
|
return uploadResults;
|
|
25
23
|
}
|
|
26
|
-
const completeBundleUploadResult = await
|
|
24
|
+
const completeBundleUploadResult = await api.completeBundleUpload({
|
|
27
25
|
appId,
|
|
28
26
|
devVersionId,
|
|
29
27
|
bundleId,
|
|
@@ -38,18 +36,3 @@ export async function upload({ contents, devVersionId, appId, }) {
|
|
|
38
36
|
return complete(undefined);
|
|
39
37
|
});
|
|
40
38
|
}
|
|
41
|
-
export function printUploadError(error) {
|
|
42
|
-
switch (error.code) {
|
|
43
|
-
case "BUNDLE_UPLOAD_ERROR":
|
|
44
|
-
process.stderr.write(chalk.red(`Error uploading bundle: ${error.error}\n`));
|
|
45
|
-
break;
|
|
46
|
-
case "START_UPLOAD_ERROR":
|
|
47
|
-
printFetcherError("Error starting upload", error.fetcherError);
|
|
48
|
-
break;
|
|
49
|
-
case "COMPLETE_BUNDLE_UPLOAD_ERROR":
|
|
50
|
-
printFetcherError("Error completing bundle upload", error.fetcherError);
|
|
51
|
-
break;
|
|
52
|
-
default:
|
|
53
|
-
return error;
|
|
54
|
-
}
|
|
55
|
-
}
|
package/lib/commands/dev.js
CHANGED
|
@@ -8,10 +8,11 @@ import { bundleJavaScript } from "./dev/bundle-javascript.js";
|
|
|
8
8
|
import { boot } from "./dev/boot.js";
|
|
9
9
|
import { onboarding } from "./dev/onboarding.js";
|
|
10
10
|
import { graphqlServer } from "./dev/graphql-server.js";
|
|
11
|
-
import { printUploadError
|
|
11
|
+
import { printUploadError } from "../print-errors.js";
|
|
12
|
+
import { upload } from "./dev/upload.js";
|
|
12
13
|
import { isErrored } from "@attio/fetchable";
|
|
13
14
|
import { printJsError, printTsError } from "../util/typescript.js";
|
|
14
|
-
import { printBuildContextError
|
|
15
|
+
import { printBuildContextError } from "./dev/prepare-build-contexts.js";
|
|
15
16
|
import notifier from "node-notifier";
|
|
16
17
|
const notifyTsErrors = (errors) => {
|
|
17
18
|
notifier.notify({
|
|
@@ -92,8 +93,8 @@ export const dev = new Command("dev")
|
|
|
92
93
|
if (error.code === "BUILD_JAVASCRIPT_ERROR") {
|
|
93
94
|
notifyJsErrors(error);
|
|
94
95
|
const { errors, warnings } = error;
|
|
95
|
-
errors
|
|
96
|
-
warnings
|
|
96
|
+
errors?.forEach((error) => printJsError(error, "error"));
|
|
97
|
+
warnings?.forEach((warning) => printJsError(warning, "warning"));
|
|
97
98
|
}
|
|
98
99
|
else {
|
|
99
100
|
printBuildContextError(error);
|
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import { fileURLToPath } from "url";
|
|
2
2
|
import path from "path";
|
|
3
|
-
import chalk from "chalk";
|
|
4
3
|
import { existsSync } from "fs";
|
|
5
4
|
import { createDirectory } from "../../util/create-directory.js";
|
|
6
5
|
import { canWrite } from "../../util/can-write.js";
|
|
7
6
|
import { copyWithTransform } from "../../util/copy-with-replace.js";
|
|
8
7
|
import { spinnerify } from "../../util/spinner.js";
|
|
9
|
-
import {
|
|
10
|
-
export async function createProject({ appSlug,
|
|
8
|
+
import { complete, errored, isErrored } from "@attio/fetchable";
|
|
9
|
+
export async function createProject({ appSlug, appInfo, }) {
|
|
11
10
|
return await spinnerify("Creating project...", "Project created", async () => {
|
|
12
11
|
const cwd = process.cwd();
|
|
13
12
|
const projectPath = path.join(cwd, appSlug);
|
|
@@ -15,7 +14,7 @@ export async function createProject({ appSlug, language, appInfo, }) {
|
|
|
15
14
|
return errored({ code: "DIRECTORY_ALREADY_EXISTS", path: projectPath });
|
|
16
15
|
}
|
|
17
16
|
if (!canWrite(cwd)) {
|
|
18
|
-
return errored({ code: "WRITE_ACCESS_DENIED",
|
|
17
|
+
return errored({ code: "WRITE_ACCESS_DENIED", path: cwd });
|
|
19
18
|
}
|
|
20
19
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
21
20
|
const projectDirResult = await createDirectory(appSlug);
|
|
@@ -23,46 +22,15 @@ export async function createProject({ appSlug, language, appInfo, }) {
|
|
|
23
22
|
return projectDirResult;
|
|
24
23
|
}
|
|
25
24
|
const projectDir = projectDirResult.value;
|
|
26
|
-
const
|
|
27
|
-
const commonDir = path.resolve(__dirname, "../../templates", "common");
|
|
25
|
+
const templateDir = path.resolve(__dirname, "../../template");
|
|
28
26
|
const transform = (contents) => contents
|
|
29
27
|
.replaceAll("title-to-be-replaced", appInfo.title)
|
|
30
28
|
.replaceAll("id-to-be-replaced", appInfo.app_id)
|
|
31
29
|
.replaceAll("slug-to-be-replaced", appSlug);
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
]);
|
|
36
|
-
if (isErrored(results)) {
|
|
37
|
-
return results;
|
|
30
|
+
const result = await copyWithTransform(templateDir, projectDir, transform);
|
|
31
|
+
if (isErrored(result)) {
|
|
32
|
+
return result;
|
|
38
33
|
}
|
|
39
34
|
return complete(undefined);
|
|
40
35
|
});
|
|
41
36
|
}
|
|
42
|
-
export function printCreateProjectError(error) {
|
|
43
|
-
switch (error.code) {
|
|
44
|
-
case "DIRECTORY_ALREADY_EXISTS":
|
|
45
|
-
process.stderr.write(chalk.red(`Directory ${error.path} already exists`));
|
|
46
|
-
break;
|
|
47
|
-
case "WRITE_ACCESS_DENIED":
|
|
48
|
-
process.stderr.write(chalk.red(`Write access denied to ${error.directory}`));
|
|
49
|
-
break;
|
|
50
|
-
case "FAILED_TO_CREATE_DIRECTORY":
|
|
51
|
-
process.stderr.write(chalk.red(`Failed to create directory ${error.directory}`));
|
|
52
|
-
break;
|
|
53
|
-
case "FAILED_TO_COPY_FILE":
|
|
54
|
-
process.stderr.write(chalk.red(`Failed to copy file ${error.src} to ${error.dest}`));
|
|
55
|
-
break;
|
|
56
|
-
case "FAILED_TO_LIST_FILES":
|
|
57
|
-
process.stderr.write(chalk.red(`Failed to list files in ${error.directory}`));
|
|
58
|
-
break;
|
|
59
|
-
case "FAILED_TO_READ_FILE":
|
|
60
|
-
process.stderr.write(chalk.red(`Failed to read file ${error.path}`));
|
|
61
|
-
break;
|
|
62
|
-
case "FAILED_TO_WRITE_FILE":
|
|
63
|
-
process.stderr.write(chalk.red(`Failed to write file ${error.path}`));
|
|
64
|
-
break;
|
|
65
|
-
default:
|
|
66
|
-
return error;
|
|
67
|
-
}
|
|
68
|
-
}
|
package/lib/commands/init.js
CHANGED
|
@@ -1,36 +1,28 @@
|
|
|
1
|
-
import { Argument, Command
|
|
1
|
+
import { Argument, Command } from "commander";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
-
import { createProject
|
|
5
|
-
import {
|
|
4
|
+
import { createProject } from "./init/create-project.js";
|
|
5
|
+
import { printCreateProjectError, printFetcherError } from "../print-errors.js";
|
|
6
6
|
import { printLogo } from "../util/print-logo.js";
|
|
7
7
|
import { getAppInfo } from "../spinners/get-app-info.spinner.js";
|
|
8
|
-
import { printFetcherError } from "../api/fetcher.js";
|
|
9
8
|
import { isErrored } from "@attio/fetchable";
|
|
10
9
|
import boxen from "boxen";
|
|
11
10
|
export const argsSchema = z.string();
|
|
12
|
-
export const optionsSchema = z.object({
|
|
13
|
-
language: z.enum(["javascript", "typescript"]).optional(),
|
|
14
|
-
});
|
|
15
11
|
export const init = new Command("init")
|
|
16
12
|
.description("Initialize a new Attio app")
|
|
17
13
|
.addArgument(new Argument("<app-slug>", "The app slug, chosen in the developer dashboard"))
|
|
18
|
-
.
|
|
19
|
-
.action(async (unparsedArgs, unparsedOptions) => {
|
|
14
|
+
.action(async (unparsedArgs) => {
|
|
20
15
|
try {
|
|
21
16
|
printLogo();
|
|
22
17
|
const appSlug = argsSchema.parse(unparsedArgs);
|
|
23
|
-
const { language: cliLanguage } = optionsSchema.parse(unparsedOptions);
|
|
24
18
|
const appInfoResult = await getAppInfo(appSlug);
|
|
25
19
|
if (isErrored(appInfoResult)) {
|
|
26
|
-
printFetcherError("Failed to fetch app info", appInfoResult.error
|
|
20
|
+
printFetcherError("Failed to fetch app info", appInfoResult.error);
|
|
27
21
|
process.exit(1);
|
|
28
22
|
}
|
|
29
23
|
const appInfo = appInfoResult.value;
|
|
30
|
-
const language = cliLanguage ?? (await askLanguage());
|
|
31
24
|
const result = await createProject({
|
|
32
25
|
appSlug,
|
|
33
|
-
language,
|
|
34
26
|
appInfo,
|
|
35
27
|
});
|
|
36
28
|
if (isErrored(result)) {
|
|
@@ -52,7 +44,7 @@ ${boxen(`cd ${appSlug}\nnpm install\nnpm run dev`, {
|
|
|
52
44
|
process.exit(0);
|
|
53
45
|
}
|
|
54
46
|
catch (error) {
|
|
55
|
-
process.stderr.write(chalk.red(
|
|
47
|
+
process.stderr.write(`${chalk.red(`✖ ${error}`)}\n`);
|
|
56
48
|
process.exit(1);
|
|
57
49
|
}
|
|
58
50
|
});
|
package/lib/commands/login.js
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
2
|
+
import { authenticator } from "../auth/auth.js";
|
|
3
|
+
import { isErrored } from "@attio/fetchable";
|
|
4
|
+
import { printAuthenticationError } from "../print-errors.js";
|
|
3
5
|
export const login = new Command("login")
|
|
4
6
|
.description("Authenticate with Attio")
|
|
5
7
|
.action(async () => {
|
|
6
|
-
await
|
|
8
|
+
const result = await authenticator.authenticate();
|
|
9
|
+
if (isErrored(result)) {
|
|
10
|
+
printAuthenticationError(result.error);
|
|
11
|
+
process.exit(1);
|
|
12
|
+
}
|
|
7
13
|
process.stdout.write("🔓 Successfully authenticated.\n");
|
|
8
14
|
process.exit(0);
|
|
9
15
|
});
|
package/lib/commands/logout.js
CHANGED
|
@@ -1,7 +1,13 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
|
-
import {
|
|
2
|
+
import { keychain } from "../auth/keychain.js";
|
|
3
|
+
import { printKeychainError } from "../print-errors.js";
|
|
4
|
+
import { isErrored } from "@attio/fetchable";
|
|
3
5
|
export const logout = new Command("logout").description("Log out from Attio").action(async () => {
|
|
4
|
-
await
|
|
6
|
+
const result = await keychain.delete();
|
|
7
|
+
if (isErrored(result)) {
|
|
8
|
+
printKeychainError(result.error);
|
|
9
|
+
process.exit(1);
|
|
10
|
+
}
|
|
5
11
|
process.stdout.write("🔒 Successfully logged out.\n");
|
|
6
12
|
process.exit(0);
|
|
7
13
|
});
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import { complete, isErrored, errored, combineAsync } from "@attio/fetchable";
|
|
2
|
-
import { prepareBuildContexts
|
|
2
|
+
import { prepareBuildContexts } from "../../dev/prepare-build-contexts.js";
|
|
3
3
|
export async function bundleJavaScript() {
|
|
4
4
|
const buildContextsResult = await prepareBuildContexts("in-memory");
|
|
5
5
|
if (isErrored(buildContextsResult)) {
|
|
6
6
|
return errored({
|
|
7
7
|
code: "ERROR_PREPARING_BUILD_CONTEXT",
|
|
8
|
-
|
|
8
|
+
error: buildContextsResult.error,
|
|
9
9
|
});
|
|
10
10
|
}
|
|
11
11
|
const buildContexts = buildContextsResult.value;
|
|
@@ -21,7 +21,7 @@ export async function bundleJavaScript() {
|
|
|
21
21
|
if (isErrored(disposeResults)) {
|
|
22
22
|
return errored({
|
|
23
23
|
code: "ERROR_DISPOSING_BUILD_CONTEXT",
|
|
24
|
-
|
|
24
|
+
error: disposeResults.error,
|
|
25
25
|
});
|
|
26
26
|
}
|
|
27
27
|
return complete(bundles);
|