attio 0.0.1-experimental.20241219 → 0.0.1-experimental.20250101

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.
Files changed (54) hide show
  1. package/lib/api/create-version.js +2 -2
  2. package/lib/attio-logo.js +24 -0
  3. package/lib/attio.js +17 -8
  4. package/lib/commands/build.js +18 -35
  5. package/lib/commands/connection/add.js +51 -168
  6. package/lib/commands/connection/index.js +9 -1
  7. package/lib/commands/connection/list.js +17 -47
  8. package/lib/commands/connection/remove.js +17 -65
  9. package/lib/commands/create.js +27 -126
  10. package/lib/commands/dev.js +17 -106
  11. package/lib/commands/version/create.js +17 -68
  12. package/lib/commands/version/index.js +11 -1
  13. package/lib/commands/version/invite.js +40 -84
  14. package/lib/commands/version/list.js +18 -40
  15. package/lib/commands/version/publish.js +40 -64
  16. package/lib/machines/actions.js +7 -0
  17. package/lib/machines/actors.js +7 -1
  18. package/lib/machines/add-connection-machine.js +169 -201
  19. package/lib/machines/build-machine.js +35 -4
  20. package/lib/machines/create-machine.js +95 -70
  21. package/lib/machines/create-version-machine.js +121 -4
  22. package/lib/machines/dev-machine.js +173 -53
  23. package/lib/machines/generate-invite-machine.js +64 -10
  24. package/lib/machines/list-connections-machine.js +57 -2
  25. package/lib/machines/list-versions-machine.js +33 -0
  26. package/lib/machines/publish-version-machine.js +45 -16
  27. package/lib/machines/remove-connection-machine.js +64 -17
  28. package/lib/machines/ts-machine.js +4 -1
  29. package/lib/schema.js +2 -2
  30. package/lib/util/clear-terminal.js +4 -0
  31. package/lib/util/load-developer-config.js +2 -2
  32. package/lib/util/print-install-instructions.js +32 -0
  33. package/lib/util/print-message.js +9 -0
  34. package/lib/util/set-terminal-title.js +8 -0
  35. package/lib/util/text-gradient.js +28 -0
  36. package/lib/util/typescript.js +25 -0
  37. package/package.json +13 -12
  38. package/schema.graphql +8 -1
  39. package/lib/components/BuildError.js +0 -46
  40. package/lib/components/BuildLog.js +0 -6
  41. package/lib/components/CodeGenErrors.js +0 -22
  42. package/lib/components/Disclaimer.js +0 -9
  43. package/lib/components/InitialInstructions.js +0 -69
  44. package/lib/components/Log.js +0 -69
  45. package/lib/components/Logo.js +0 -10
  46. package/lib/components/MultiSelect.js +0 -65
  47. package/lib/components/ScrollBox.js +0 -87
  48. package/lib/components/ScrollBox.store.js +0 -36
  49. package/lib/components/ScrollBox.util.js +0 -27
  50. package/lib/components/Select.js +0 -6
  51. package/lib/components/Table.js +0 -33
  52. package/lib/components/TypeScriptErrors.js +0 -38
  53. package/lib/hooks/useFullScreen.js +0 -22
  54. package/lib/hooks/useTerminalTitle.js +0 -11
@@ -2,6 +2,12 @@ import { assign, setup, fromCallback } from "xstate";
2
2
  import { fetchVersions } from "../api/fetch-versions.js";
3
3
  import { emptyConfig } from "../schema.js";
4
4
  import { loadAppConfig, loadDeveloperConfig } from "./actors.js";
5
+ import formatDate from "date-fns/format/index.js";
6
+ import { showError, printLogo } from "./actions.js";
7
+ import { printInstallInstructions } from "../util/print-install-instructions.js";
8
+ import Spinner from "tiny-spinner";
9
+ import Table from "cli-table3";
10
+ import chalk from "chalk";
5
11
  export const connectionTypes = [
6
12
  { value: "secret", label: "Secret" },
7
13
  { value: "oauth2-code", label: "OAuth2" },
@@ -17,12 +23,15 @@ export const listVersionsMachine = setup({
17
23
  },
18
24
  actors: {
19
25
  fetchVersions: fromCallback(({ sendBack, input: { developer: { token, slug: devSlug }, config, }, }) => {
26
+ const spinner = new Spinner();
27
+ spinner.start("Loading versions...");
20
28
  const getVersions = async () => {
21
29
  const versions = await fetchVersions({
22
30
  token,
23
31
  devSlug,
24
32
  appId: config.id,
25
33
  });
34
+ spinner.success("Versions loaded");
26
35
  sendBack({ type: "Versions Fetched", versions });
27
36
  };
28
37
  getVersions();
@@ -31,6 +40,26 @@ export const listVersionsMachine = setup({
31
40
  loadAppConfig,
32
41
  },
33
42
  actions: {
43
+ printLogo,
44
+ showError,
45
+ showConfigInstructions: ({ context }) => printInstallInstructions(context.configError),
46
+ showVersions: ({ context }) => {
47
+ const table = new Table({
48
+ head: ["Version", "Published", "Installations", "Created"].map((h) => chalk.bold(h)),
49
+ style: {
50
+ head: [],
51
+ border: [],
52
+ },
53
+ colAligns: ["center", "center", "right", "left"],
54
+ });
55
+ table.push(...context.versions.map((version) => [
56
+ `${version.major}.${version.minor}`,
57
+ version.is_published ? "Yes" : "No",
58
+ version.num_installations.toLocaleString(),
59
+ formatDate(new Date(version.created_at), "MMMM d, yyyy, HH:mm"),
60
+ ]));
61
+ process.stdout.write(table.toString());
62
+ },
34
63
  clearError: assign({
35
64
  error: () => undefined,
36
65
  }),
@@ -75,6 +104,7 @@ export const listVersionsMachine = setup({
75
104
  },
76
105
  "Show config instructions": {
77
106
  type: "final",
107
+ entry: "showConfigInstructions",
78
108
  },
79
109
  "Loading App Config": {
80
110
  invoke: {
@@ -94,6 +124,7 @@ export const listVersionsMachine = setup({
94
124
  },
95
125
  "Error": {
96
126
  type: "final",
127
+ entry: { type: "showError", params: ({ context }) => ({ error: context.error }) },
97
128
  },
98
129
  "Fetching Versions": {
99
130
  on: {
@@ -112,7 +143,9 @@ export const listVersionsMachine = setup({
112
143
  },
113
144
  "Display Versions": {
114
145
  type: "final",
146
+ entry: "showVersions",
115
147
  },
116
148
  },
117
149
  initial: "Loading Developer Config",
150
+ entry: "printLogo",
118
151
  });
@@ -2,7 +2,10 @@ import { assign, setup, fromCallback } from "xstate";
2
2
  import { fetchPublishableVersions } from "../api/fetch-versions.js";
3
3
  import { publishVersion } from "../api/publish-version.js";
4
4
  import { emptyConfig } from "../schema.js";
5
- import { loadAppConfig, loadDeveloperConfig } from "./actors.js";
5
+ import { askWithTypedChoices, loadAppConfig, loadDeveloperConfig } from "./actors.js";
6
+ import { printInstallInstructions } from "../util/print-install-instructions.js";
7
+ import { printLogo, showError } from "./actions.js";
8
+ import Spinner from "tiny-spinner";
6
9
  export const connectionTypes = [
7
10
  { value: "secret", label: "Secret" },
8
11
  { value: "oauth2-code", label: "OAuth2" },
@@ -18,30 +21,41 @@ export const publishVersionMachine = setup({
18
21
  input: {},
19
22
  },
20
23
  actors: {
24
+ askForVersion: askWithTypedChoices(),
21
25
  fetchVersions: fromCallback(({ sendBack, input: { developer: { token, slug: devSlug }, config, }, }) => {
22
26
  const getVersions = async () => {
27
+ const spinner = new Spinner();
28
+ spinner.start("Loading versions...");
23
29
  const versions = await fetchPublishableVersions({
24
30
  token,
25
31
  devSlug,
26
32
  appId: config.id,
27
33
  });
34
+ spinner.success("Versions loaded");
28
35
  sendBack({ type: "Versions Fetched", versions });
29
36
  };
30
37
  getVersions();
31
38
  }),
32
- publishVersion: fromCallback(({ sendBack, input: { developer: { token, slug: devSlug }, config, }, }) => {
39
+ publishVersion: fromCallback(({ sendBack, input: { developer: { token, slug: devSlug }, config, major, minor, }, }) => {
33
40
  const add = async () => {
41
+ const spinner = new Spinner();
34
42
  try {
43
+ spinner.start(`Publishing version ${major}.${minor}...`);
44
+ if (major === undefined || minor === undefined) {
45
+ throw new Error("Major and minor must be provided");
46
+ }
35
47
  await publishVersion({
36
48
  token,
37
49
  devSlug,
38
50
  appId: config.id,
39
- major: config.major,
40
- minor: config.minor,
51
+ major,
52
+ minor,
41
53
  });
54
+ spinner.success(`Version ${major}.${minor} published`);
42
55
  sendBack({ type: "Success" });
43
56
  }
44
57
  catch (error) {
58
+ spinner.error("Error publishing version");
45
59
  sendBack({ type: "Error", error: error.message });
46
60
  }
47
61
  };
@@ -51,6 +65,12 @@ export const publishVersionMachine = setup({
51
65
  loadAppConfig,
52
66
  },
53
67
  actions: {
68
+ printLogo,
69
+ showError,
70
+ showConfigInstructions: ({ context }) => printInstallInstructions(context.configError),
71
+ showNoUnpublishedVersions: () => {
72
+ process.stdout.write("No unpublished versions found.\n");
73
+ },
54
74
  clearError: assign({
55
75
  error: () => undefined,
56
76
  }),
@@ -67,8 +87,8 @@ export const publishVersionMachine = setup({
67
87
  config: (_, params) => params.config,
68
88
  }),
69
89
  setVersion: assign({
70
- major: (_, params) => params.version.major,
71
- minor: (_, params) => params.version.minor,
90
+ major: (_, params) => params.output.major,
91
+ minor: (_, params) => params.output.minor,
72
92
  }),
73
93
  setVersions: assign({
74
94
  versions: (_, params) => params.versions,
@@ -105,6 +125,7 @@ export const publishVersionMachine = setup({
105
125
  },
106
126
  "Show config instructions": {
107
127
  type: "final",
128
+ entry: "showConfigInstructions",
108
129
  },
109
130
  "Loading App Config": {
110
131
  invoke: {
@@ -124,6 +145,7 @@ export const publishVersionMachine = setup({
124
145
  },
125
146
  "Error": {
126
147
  type: "final",
148
+ entry: { type: "showError", params: ({ context }) => ({ error: context.error }) },
127
149
  },
128
150
  "Success": {
129
151
  type: "final",
@@ -161,10 +183,7 @@ export const publishVersionMachine = setup({
161
183
  "Publishing": {
162
184
  invoke: {
163
185
  src: "publishVersion",
164
- input: ({ context }) => ({
165
- developer: context.developer,
166
- config: context.config,
167
- }),
186
+ input: ({ context }) => context,
168
187
  },
169
188
  on: {
170
189
  Success: {
@@ -179,12 +198,6 @@ export const publishVersionMachine = setup({
179
198
  },
180
199
  },
181
200
  "Ask for version": {
182
- on: {
183
- "Select Version": {
184
- target: "Publishing",
185
- actions: { type: "setVersion", params: ({ event }) => event },
186
- },
187
- },
188
201
  always: {
189
202
  target: "No unpublished versions",
190
203
  guard: {
@@ -192,10 +205,26 @@ export const publishVersionMachine = setup({
192
205
  params: ({ context }) => context,
193
206
  },
194
207
  },
208
+ invoke: {
209
+ src: "askForVersion",
210
+ input: ({ context }) => ({
211
+ message: "Select a version to publish:",
212
+ choices: context.versions.map((version) => ({
213
+ name: `${version.major}.${version.minor}`,
214
+ value: version,
215
+ })),
216
+ }),
217
+ onDone: {
218
+ target: "Publishing",
219
+ actions: { type: "setVersion", params: ({ event }) => event },
220
+ },
221
+ },
195
222
  },
196
223
  "No unpublished versions": {
197
224
  type: "final",
225
+ entry: "showNoUnpublishedVersions",
198
226
  },
199
227
  },
200
228
  initial: "Loading Developer Config",
229
+ entry: "printLogo",
201
230
  });
@@ -2,16 +2,23 @@ import { assign, setup, fromCallback } from "xstate";
2
2
  import { fetchConnections } from "../api/fetch-connections.js";
3
3
  import { removeConnectionDefinition } from "../api/remove-connection-definition.js";
4
4
  import { emptyConfig } from "../schema.js";
5
- import { loadAppConfig, loadDeveloperConfig } from "./actors.js";
5
+ import { askWithChoices, loadAppConfig, loadDeveloperConfig, confirm } from "./actors.js";
6
+ import { printInstallInstructions } from "../util/print-install-instructions.js";
7
+ import { showError, printLogo } from "./actions.js";
8
+ import Spinner from "tiny-spinner";
6
9
  export const removeConnectionMachine = setup({
7
10
  types: {
8
11
  context: {},
9
12
  events: {},
10
13
  },
11
14
  actors: {
15
+ askWithChoices,
16
+ confirm,
12
17
  removeConnectionDefinition: fromCallback(({ sendBack, input }) => {
13
18
  const add = async () => {
19
+ const spinner = new Spinner();
14
20
  try {
21
+ spinner.start("Removing connection...");
15
22
  await removeConnectionDefinition({
16
23
  token: input.developer.token,
17
24
  devSlug: input.developer.slug,
@@ -19,9 +26,11 @@ export const removeConnectionMachine = setup({
19
26
  global: input.global,
20
27
  major: input.config.major,
21
28
  });
29
+ spinner.success("Connection removed");
22
30
  sendBack({ type: "Success" });
23
31
  }
24
32
  catch (error) {
33
+ spinner.error("Error removing connection");
25
34
  sendBack({ type: "Error", error: error.message });
26
35
  }
27
36
  };
@@ -30,17 +39,34 @@ export const removeConnectionMachine = setup({
30
39
  loadDeveloperConfig,
31
40
  loadAppConfig,
32
41
  loadConnections: fromCallback(({ sendBack, input }) => {
42
+ const spinner = new Spinner();
43
+ spinner.start("Loading connections...");
33
44
  fetchConnections({
34
45
  token: input.developer.token,
35
46
  devSlug: input.developer.slug,
36
47
  appId: input.config.id,
37
48
  major: input.config.major,
38
49
  })
39
- .then((connections) => sendBack({ type: "Connections Loaded", connections }))
40
- .catch((error) => sendBack({ type: "Error", error: error.message }));
50
+ .then((connections) => {
51
+ spinner.success("Connections loaded");
52
+ sendBack({ type: "Connections Loaded", connections });
53
+ })
54
+ .catch((error) => {
55
+ spinner.error("Error loading connections");
56
+ sendBack({ type: "Error", error: error.message });
57
+ });
41
58
  }),
42
59
  },
43
60
  actions: {
61
+ printLogo,
62
+ showError,
63
+ showConfigInstructions: ({ context }) => printInstallInstructions(context.configError),
64
+ showCanceled: () => {
65
+ process.stdout.write("No connection removed.\n\n");
66
+ },
67
+ showNoConnections: () => {
68
+ process.stdout.write("This app has no connections to remove.\n\n");
69
+ },
44
70
  clearError: assign({
45
71
  error: () => undefined,
46
72
  }),
@@ -60,7 +86,7 @@ export const removeConnectionMachine = setup({
60
86
  connections: (_, params) => params.connections,
61
87
  }),
62
88
  setGlobal: assign({
63
- global: (_, params) => params.global,
89
+ global: (_, params) => params.output === "true",
64
90
  }),
65
91
  setOnlyConnection: assign({
66
92
  global: (_, params) => params.connections[0].global,
@@ -69,6 +95,7 @@ export const removeConnectionMachine = setup({
69
95
  guards: {
70
96
  "have more than one connection": (_, params) => Boolean(params.connections && params.connections.length > 1),
71
97
  "have only one connection": (_, params) => Boolean(params.connections && params.connections.length === 1),
98
+ "confirmed": (_, params) => params.output,
72
99
  },
73
100
  }).createMachine({
74
101
  context: ({ input }) => ({
@@ -95,6 +122,7 @@ export const removeConnectionMachine = setup({
95
122
  },
96
123
  "Show config instructions": {
97
124
  type: "final",
125
+ entry: "showConfigInstructions",
98
126
  },
99
127
  "Loading App Config": {
100
128
  invoke: {
@@ -113,6 +141,7 @@ export const removeConnectionMachine = setup({
113
141
  },
114
142
  "Error": {
115
143
  type: "final",
144
+ entry: { type: "showError", params: ({ context }) => ({ error: context.error }) },
116
145
  },
117
146
  "Removing connection definition": {
118
147
  invoke: {
@@ -177,30 +206,48 @@ export const removeConnectionMachine = setup({
177
206
  },
178
207
  "No Connections": {
179
208
  type: "final",
209
+ entry: "showNoConnections",
180
210
  },
181
211
  "Choosing a Connection": {
182
- on: {
183
- "Connection Chosen": {
212
+ invoke: {
213
+ src: "askWithChoices",
214
+ input: ({ context }) => ({
215
+ message: "Which connection would you like to remove?",
216
+ choices: context.connections.map((connection) => ({
217
+ name: `${connection.label} (${connection.global ? "workspace" : "user"})`,
218
+ value: connection.global ? "true" : "false",
219
+ })),
220
+ required: true,
221
+ }),
222
+ onDone: {
184
223
  target: "Confirm Removal",
185
- actions: {
186
- type: "setGlobal",
187
- params: ({ event }) => ({
188
- global: event.global,
189
- }),
190
- },
191
- reenter: true,
224
+ actions: { type: "setGlobal", params: ({ event }) => event },
192
225
  },
193
226
  },
194
227
  },
195
228
  "Confirm Removal": {
196
- on: {
197
- Confirm: "Removing connection definition",
198
- Cancel: "Cancelled",
229
+ invoke: {
230
+ src: "confirm",
231
+ input: ({ context }) => ({
232
+ message: `Are you sure you want to remove "${context.connections[0].label}"?`,
233
+ }),
234
+ onDone: [
235
+ {
236
+ target: "Removing connection definition",
237
+ guard: { type: "confirmed", params: ({ event }) => event },
238
+ },
239
+ {
240
+ target: "Canceled",
241
+ reenter: true,
242
+ },
243
+ ],
199
244
  },
200
245
  },
201
- "Cancelled": {
246
+ "Canceled": {
202
247
  type: "final",
248
+ entry: "showCanceled",
203
249
  },
204
250
  },
205
251
  initial: "Loading Developer Config",
252
+ entry: "printLogo",
206
253
  });
@@ -47,7 +47,10 @@ export const tsMachine = setup({
47
47
  clearTime: assign({
48
48
  time: () => undefined,
49
49
  }),
50
- raiseErrored: sendTo(({ context }) => context.parentRef, { type: "TypeScript Error" }),
50
+ raiseErrored: sendTo(({ context }) => context.parentRef, ({ context }) => ({
51
+ type: "TypeScript Error",
52
+ errors: context.errors,
53
+ })),
51
54
  raiseSuccess: sendTo(({ context }) => context.parentRef, { type: "TypeScript Success" }),
52
55
  setErrors: assign({
53
56
  errors: (_, params) => params.errors,
package/lib/schema.js CHANGED
@@ -4,7 +4,7 @@ import { fromError } from "zod-validation-error";
4
4
  const configSchema = z.object({
5
5
  slug: z.string().describe("A unique slug for the app"),
6
6
  id: z.string().uuid().describe("A unique ID for the app"),
7
- major: z.number().int().min(1).default(1).describe("The major version of the app"),
7
+ major: z.number().int().min(0).default(0).describe("The major version of the app"),
8
8
  minor: z.number().int().min(0).default(0).describe("The minor version of the app"),
9
9
  connection: z
10
10
  .object({
@@ -20,7 +20,7 @@ const configSchema = z.object({
20
20
  export const emptyConfig = {
21
21
  slug: "",
22
22
  id: "",
23
- major: 1,
23
+ major: 0,
24
24
  minor: 0,
25
25
  connection: null,
26
26
  };
@@ -0,0 +1,4 @@
1
+ export function clearTerminal() {
2
+ process.stdout.write("\x1b[2J");
3
+ process.stdout.write("\x1b[H");
4
+ }
@@ -13,14 +13,14 @@ const developerConfigSchema = initialDeveloperConfigSchema.extend({
13
13
  developer_account_id: z.string(),
14
14
  developer_account_member_id: z.string(),
15
15
  });
16
- export const configFileName = ".attiorc";
16
+ export const configFileName = process.env.NODE_ENV === "development" ? ".attiorc.dev" : ".attiorc";
17
17
  function determineDeveloperConfigPath() {
18
18
  if (process.env.NODE_ENV === "development") {
19
19
  const path = `${homedir()}/.attiorc.dev`;
20
20
  if (existsSync(path))
21
21
  return path;
22
22
  }
23
- const path = `${homedir()}/${configFileName}`;
23
+ const path = `${homedir()}/.attiorc`;
24
24
  if (existsSync(path))
25
25
  return path;
26
26
  return "No config file";
@@ -0,0 +1,32 @@
1
+ import { stringify } from "ini";
2
+ import chalk from "chalk";
3
+ import { APP, APP_NO_PROTOCOL, IS_DEV } from "../env.js";
4
+ import { configFileName } from "../util/load-developer-config.js";
5
+ const exampleConfig = {
6
+ token: "YOUR TOKEN HERE",
7
+ developer_slug: "ANY UNIQUE SLUG YOU WANT",
8
+ target_workspace_id: "YOUR WORKSPACE ID HERE",
9
+ };
10
+ export function printInstallInstructions(reason) {
11
+ const write = (str = "") => process.stdout.write(str + "\n");
12
+ if (reason) {
13
+ write("Failed to load config file.");
14
+ write(chalk.red(reason));
15
+ write();
16
+ }
17
+ write("You will need to:");
18
+ write();
19
+ write(` 1. Log into${IS_DEV ? ` ${chalk.italic("DEVELOPMENT")}` : ""} Attio web app: ${APP}`);
20
+ write(` 2. Open Dev Tools > Application > Cookies > ${APP_NO_PROTOCOL}`);
21
+ write(` 3. Copy the value for attio-session. That's your "token".`);
22
+ write(` 4. Create a file in your home directory called ~/${configFileName}`);
23
+ write(` 5. It should have the following [INI] format:`);
24
+ write();
25
+ write(" " + stringify(exampleConfig).trim().replace(/\n/g, "\n "));
26
+ write();
27
+ write(" 6. Run this command again.");
28
+ write();
29
+ write(" ...");
30
+ write();
31
+ write(" N. PROFIT!");
32
+ }
@@ -0,0 +1,9 @@
1
+ import Blocker from "stdin-blocker";
2
+ import Cursor from "tiny-cursor";
3
+ export function printMessage(message) {
4
+ Blocker.block();
5
+ Cursor.hide();
6
+ process.stdout.cursorTo(0);
7
+ process.stdout.write(message.trim());
8
+ process.stdout.clearLine(1);
9
+ }
@@ -0,0 +1,8 @@
1
+ export function setTerminalTitle(title) {
2
+ if (process.platform === "win32") {
3
+ process.title = title;
4
+ }
5
+ else {
6
+ process.stdout.write(`\x1B]2;${title}\x1B\x5C`);
7
+ }
8
+ }
@@ -0,0 +1,28 @@
1
+ import chalk from "chalk";
2
+ function hexToRgb(hex) {
3
+ const bigint = parseInt(hex.replace("#", ""), 16);
4
+ return [(bigint >> 16) & 255, (bigint >> 8) & 255, bigint & 255];
5
+ }
6
+ function interpolateColor(start, end, factor) {
7
+ return [
8
+ Math.round(start[0] + (end[0] - start[0]) * factor),
9
+ Math.round(start[1] + (end[1] - start[1]) * factor),
10
+ Math.round(start[2] + (end[2] - start[2]) * factor),
11
+ ];
12
+ }
13
+ export function textGradient(input, startHex, endHex) {
14
+ const lines = input.split("\n");
15
+ const startColor = hexToRgb(startHex);
16
+ const endColor = hexToRgb(endHex);
17
+ const gradientLines = lines.map((line) => {
18
+ const length = line.length;
19
+ return Array.from(line)
20
+ .map((char, index) => {
21
+ const factor = length > 1 ? index / (length - 1) : 0;
22
+ const [r, g, b] = interpolateColor(startColor, endColor, factor);
23
+ return chalk.rgb(r, g, b)(char);
24
+ })
25
+ .join("");
26
+ });
27
+ return gradientLines.join("\n");
28
+ }
@@ -1,5 +1,6 @@
1
1
  import { existsSync, createReadStream } from "fs";
2
2
  import { createInterface } from "readline";
3
+ import chalk from "chalk";
3
4
  import { default as ts } from "typescript";
4
5
  import { z } from "zod";
5
6
  async function readLine(path, line) {
@@ -32,6 +33,7 @@ export const typeScriptErrorSchema = z.object({
32
33
  lineText: z.string(),
33
34
  line: z.number(),
34
35
  character: z.number(),
36
+ endCharacter: z.number(),
35
37
  })
36
38
  .optional(),
37
39
  });
@@ -58,6 +60,8 @@ export const getDiagnostics = async (program) => {
58
60
  const text = ts.flattenDiagnosticMessageText(diagnostic.messageText, "\n");
59
61
  if (diagnostic.file) {
60
62
  const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start);
63
+ const endPosition = diagnostic.start + diagnostic.length;
64
+ const { character: endCharacter } = diagnostic.file.getLineAndCharacterOfPosition(endPosition);
61
65
  const lineText = await readLine(diagnostic.file.fileName, line);
62
66
  return typeScriptErrorSchema.parse({
63
67
  text,
@@ -65,6 +69,7 @@ export const getDiagnostics = async (program) => {
65
69
  file: diagnostic.file.fileName,
66
70
  line,
67
71
  character,
72
+ endCharacter,
68
73
  lineText,
69
74
  },
70
75
  });
@@ -75,3 +80,23 @@ export const getDiagnostics = async (program) => {
75
80
  }));
76
81
  return errors;
77
82
  };
83
+ export function printError(error) {
84
+ if (!error.location) {
85
+ process.stderr.write(`${chalk.red("×")} – ${error.text}\n`);
86
+ return;
87
+ }
88
+ const lineString = error.location.line.toLocaleString().padStart(3, " ");
89
+ const lineStringLength = lineString.length;
90
+ const emptyLine = " ".repeat(lineStringLength);
91
+ const lineText = error.location.lineText;
92
+ const leadingSpaces = lineText.match(/^\s*/)?.[0].length || 0;
93
+ const trimmedLineText = lineText.trimLeft();
94
+ const beforeError = trimmedLineText.slice(0, error.location.character - leadingSpaces);
95
+ const errorText = trimmedLineText.slice(error.location.character - leadingSpaces, error.location.endCharacter - leadingSpaces);
96
+ const afterError = trimmedLineText.slice(error.location.endCharacter - leadingSpaces);
97
+ process.stderr.write(`${" ".repeat(lineStringLength - 2)} ${chalk.red("×")} ${error.text}\n\n`);
98
+ process.stderr.write(`${emptyLine} ╭─── ${chalk.bold(error.location.file)}\n`);
99
+ process.stderr.write(`${lineString} │ ${beforeError}${chalk.red(errorText)}${afterError}\n`);
100
+ process.stderr.write(`${emptyLine} │ ${" ".repeat(Math.max(0, error.location.character - leadingSpaces))}${chalk.red("~".repeat(error.location.endCharacter - error.location.character))}\n`);
101
+ process.stderr.write(`${emptyLine} ╰───\n\n`);
102
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attio",
3
- "version": "0.0.1-experimental.20241219",
3
+ "version": "0.0.1-experimental.20250101",
4
4
  "bin": "lib/attio.js",
5
5
  "type": "module",
6
6
  "files": [
@@ -33,37 +33,38 @@
33
33
  "@graphql-codegen/typescript-operations": "4.3.0",
34
34
  "@hono/graphql-server": "0.5.1",
35
35
  "@hono/node-server": "1.13.1",
36
+ "@inquirer/prompts": "7.2.0",
36
37
  "@swc/core": "^1.3.58",
37
38
  "@xstate/react": "4.1.1",
38
39
  "@xstate/store": "1.0.0",
40
+ "boxen": "^8.0.1",
41
+ "chalk": "^5.4.1",
39
42
  "chokidar": "^3.6.0",
43
+ "cli-table3": "^0.6.5",
44
+ "clipboardy": "^4.0.0",
45
+ "commander": "^13.0.0",
40
46
  "date-fns": "^2.21.1",
41
47
  "dotenv": "^16.4.5",
42
48
  "esbuild": "^0.20.2",
43
- "figures": "^6.1.0",
44
49
  "glob": "10.3.16",
45
50
  "graphql": "16.9.0",
46
51
  "hono": "4.6.3",
47
52
  "immer": "^5.0.0",
48
53
  "ini": "^4.1.3",
49
- "ink": "^5.0.1",
50
- "ink-big-text": "^2.0.0",
51
- "ink-gradient": "^3.0.0",
52
- "ink-link": "^4.1.0",
53
- "ink-select-input": "^6.0.0",
54
- "ink-spinner": "^5.0.0",
55
- "ink-text-input": "^6.0.0",
54
+ "inquirer": "^12.3.0",
56
55
  "murmurhash-js": "^1.0.0",
56
+ "node-notifier": "^10.0.1",
57
57
  "open": "^7.0.0",
58
- "pastel": "^3.0.0",
59
58
  "prettier": "^3.4.1",
60
59
  "react": "18.3.1",
60
+ "stdin-blocker": "^2.0.0",
61
+ "tiny-cursor": "^2.0.0",
62
+ "tiny-spinner": "^2.0.4",
61
63
  "tmp-promise": "^3.0.3",
62
64
  "ts-morph": "^24.0.0",
63
65
  "typescript": "5.6.3",
64
66
  "uuid": "^9.0.1",
65
- "xstate": "5.15.0",
66
- "yargs": "^17.7.2",
67
+ "xstate": "5.19.1",
67
68
  "zod": "^3.22.3",
68
69
  "zod-to-json-schema": "3.23.1",
69
70
  "zod-validation-error": "3.3.0"