attio 0.0.1-experimental.20241001 → 0.0.1-experimental.20241002.1

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.
@@ -11,7 +11,7 @@ export function startGraphqlServer(sendBack) {
11
11
  const startServer = async () => {
12
12
  const currentFilePath = fileURLToPath(import.meta.url);
13
13
  const currentDirPath = dirname(currentFilePath);
14
- const schemaPath = path.resolve(currentDirPath, "..", "schema.graphql");
14
+ const schemaPath = path.resolve(currentDirPath, "..", "..", "schema.graphql");
15
15
  const schemaString = fs.readFileSync(schemaPath, "utf8");
16
16
  const port = await findAvailablePort(8700);
17
17
  const schema = buildSchema(schemaString);
@@ -1,5 +1,4 @@
1
1
  export { useDialog } from "./use-dialog.js";
2
- export { runQuery } from "./run-query.js";
3
2
  export { useAsyncCache, AsyncCacheConfig, AsyncFunction } from "./use-async-cache.js";
4
3
  export { useQuery } from "./use-query.js";
5
4
  export { useRecord } from "./use-record.js";
@@ -1,5 +1,6 @@
1
- export declare function useQuery<T>(
1
+ import { Query } from "../run-query";
2
+ export declare function useQuery<Variables extends Record<string, any>, Result>(
2
3
  /** GraphQL query */
3
- query: string,
4
+ query: Query<Variables, Result>,
4
5
  /** GraphQL query variables */
5
- variableValues?: Record<string, any>): T;
6
+ variableValues?: Variables): Result;
@@ -1,2 +1,2 @@
1
1
  export type Icon = AttioIcon | `${string}.png`;
2
- export type AttioIcon = "Activity" | "AlignArrowUp" | "AlignBlockCenter" | "AlignBlockLeft" | "AlignBlockRight" | "AlignCenter" | "AlignLeft" | "AlignRight" | "Apps" | "Archive" | "ArrowDown" | "ArrowDownLeft" | "ArrowDownRight" | "ArrowLeft" | "ArrowLeftRight" | "ArrowRight" | "ArrowsRefresh" | "ArrowUp" | "ArrowUpDown" | "ArrowUpLeft" | "ArrowUpRight" | "ArrowUpRightCircle" | "At" | "Attachment" | "Attributes" | "AttributesPlus" | "Automation" | "Bank" | "BarChart" | "Bold" | "BracketsCross" | "Breakpoint" | "BrowserExtension" | "BulletList" | "CalendarArrowLeft" | "CalendarArrowRight" | "CalendarClock" | "Calendar" | "CalendarArrowLeft" | "CalendarArrowRight" | "CalendarClock" | "CalendarDate" | "CalendarMinus" | "CalendarPlus" | "Caption" | "Change" | "Changelog" | "Check" | "CheckCircle" | "CheckDouble" | "CheckSquare" | "CheckSquarePlus" | "ChevronDown" | "ChevronDownChevronUp" | "ChevronLeft" | "ChevronRight" | "ChevronRightDouble" | "ChevronUp" | "ChevronUpChevronDown" | "CircleEmpty" | "CircleFilled" | "ClassifyRecord" | "ClassifyText" | "Clock" | "ClockDelay" | "Code" | "Collapse" | "Collapsed" | "CollapsedHover" | "CollapseHover" | "CollapseScreen" | "Collection" | "CollectionPlus" | "ColumnPlus" | "Columns" | "ColumnsPlus" | "Command" | "Comment" | "CommentPlus" | "Comments" | "Companies" | "Company" | "CompanyPlus" | "ConditionDown" | "Confetti" | "Copy" | "CreditCard" | "Cross" | "CrossCircle" | "Currency" | "CustomForms" | "CustomFormsCog" | "CustomFormsEdit" | "CustomFormsPlus" | "Dashboard" | "DashboardPlus" | "Data" | "DataText" | "Deal" | "Description" | "Desktop" | "Dollar" | "DollarArrowUp" | "DotsHorizontal" | "DotsVertical" | "Download" | "DragHandle" | "Duplicate" | "Edit" | "EditDisabled" | "Education" | "Email" | "EmailArrowLeft" | "EmailArrowRight" | "EmailDraft" | "EmailMass" | "EmailMassPlus" | "EmailMinus" | "EmailPlus" | "EmailTemplate" | "EmailTemplatePlus" | "EmojiAdd" | "EmptyCircle" | "Enter" | "Equals" | "EstimatedArr" | "Euro" | "EverythingBagel" | "ExpandScreen" | "Eye" | "EyeHide" | "Feedback" | "FileCsv" | "Filter" | "FilterAdd" | "FilterFunnel" | "Flash" | "Folder" | "FolderPlus" | "Forbid" | "FunnelChart" | "FunnelStepsChart" | "GalleryView" | "Gbp" | "GitBranch" | "Globe" | "GlobeHemisphereEast" | "Grid" | "Growth" | "Hand" | "Handshake" | "Hashtag" | "HelpCircle" | "Image" | "ImportExport" | "Inbox" | "InboxCross" | "InboxNotification" | "InfoCircle" | "Input" | "Italic" | "Key" | "KeyboardKey" | "LineChart" | "Link" | "List" | "ListArrowLeft" | "ListEdit" | "ListFlash" | "ListMinus" | "ListPlus" | "ListSearch" | "ListView" | "Location" | "Locked" | "LogOut" | "MapChart" | "Maximise" | "Merge" | "Message" | "Minimise" | "Minus" | "MinusSquare" | "MobilePhone" | "Moon" | "More" | "Move" | "MultiSelect" | "Name" | "None" | "Note" | "NotePlus" | "NoteTemplate" | "NoteTemplateArrowRight" | "NoteTemplatePlus" | "NumberList" | "ObjectIcon" | "ObjectPlaceholder" | "ObjectPlus" | "PanelLeft" | "PanelRight" | "Paste" | "Pause" | "Percentage" | "Phone" | "PhoneArrowLeft" | "PhoneArrowRight" | "PhoneBusy" | "PhoneCancel" | "PhoneCheck" | "PhoneCross" | "PhoneMissed" | "PhoneNoAnswer" | "PieChart" | "Pin" | "Pinned" | "Placeholder" | "Play" | "Plus" | "PlusSquare" | "Pointer" | "PromptCompletion" | "Record" | "RecordDetailEdit" | "RecordEdit" | "RecordFlash" | "RecordPlus" | "RecordSearch" | "Reference" | "RelationshipConnect" | "RemoveCircle" | "Rename" | "Repeat" | "Replace" | "Reply" | "Report" | "ReportPlus" | "Reset" | "RingBell" | "Robot" | "Rocket" | "Rows" | "RowsPlus" | "Sales" | "Search" | "SearchMinus" | "SearchPlus" | "SectionArrowDown" | "SectionArrowUp" | "Select" | "SelfServed" | "Send" | "SendAdd" | "SendEdit" | "ServiceBell" | "Sessions" | "Settings" | "SettingsArrowRight" | "SettingsCheck" | "SettingsMinus" | "Share" | "ShieldLocker" | "Shortcut" | "ShortcutMinus" | "ShortcutPlus" | "ShowRightSidebar" | "Signature" | "SingleMetricChart" | "SnapGrid" | "SortAscending" | "SortDescending" | "SortPlus" | "Spreadsheet" | "SquareEmpty" | "SquareFilled" | "SquareStrength" | "StageChanged" | "Star" | "StarCross" | "Status" | "Strength" | "Subscription" | "SummariseRecord" | "Sun" | "Switch" | "Table" | "Tag" | "Templates" | "TestTube" | "Text" | "TextIndentLeft" | "TextIndentRight" | "ThumbsDown" | "ThumbsUp" | "TidyUp" | "TimeChart" | "TimeInStatus" | "Timestamp" | "Trash" | "Trigger" | "TriggerPlus" | "Undo" | "Unlink" | "Upload" | "User" | "UserArrowLeft" | "UserArrowRight" | "UserFlash" | "UserHeart" | "UserKey" | "UserMinus" | "UserPhone" | "UserPlaceholder" | "UserPlus" | "Users" | "VideoCamera" | "View" | "ViewPlus" | "Voicemail" | "WarningCircle" | "WarningTriangle" | "Webhooks" | "WorkflowCredit" | "WorkflowRun" | "WorkflowsEdit" | "WorkflowsPlaceholder" | "WorkflowsPlus" | "Workspace";
2
+ export type AttioIcon = "Activity" | "AlignArrowUp" | "AlignBlockCenter" | "AlignBlockLeft" | "AlignBlockRight" | "AlignCenter" | "AlignLeft" | "AlignRight" | "Apps" | "Archive" | "ArrowDown" | "ArrowDownLeft" | "ArrowDownRight" | "ArrowLeft" | "ArrowLeftRight" | "ArrowRight" | "ArrowsRefresh" | "ArrowUp" | "ArrowUpDown" | "ArrowUpLeft" | "ArrowUpRight" | "ArrowUpRightCircle" | "At" | "Attachment" | "Attributes" | "AttributesPlus" | "Automation" | "Bank" | "BarChart" | "Bold" | "BracketsCross" | "Breakpoint" | "BrowserExtension" | "BulletList" | "CalendarArrowLeft" | "CalendarArrowRight" | "CalendarClock" | "Calendar" | "CalendarArrowLeft" | "CalendarArrowRight" | "CalendarClock" | "CalendarDate" | "CalendarMinus" | "CalendarPlus" | "Caption" | "Change" | "Changelog" | "Check" | "CheckCircle" | "CheckDouble" | "CheckSquare" | "CheckSquarePlus" | "ChevronDown" | "ChevronDownChevronUp" | "ChevronLeft" | "ChevronRight" | "ChevronRightDouble" | "ChevronUp" | "ChevronUpChevronDown" | "CircleEmpty" | "CircleFilled" | "ClassifyRecord" | "ClassifyText" | "Clock" | "ClockDelay" | "Code" | "Collapse" | "Collapsed" | "CollapsedHover" | "CollapseHover" | "CollapseScreen" | "Collection" | "CollectionPlus" | "ColumnPlus" | "Columns" | "ColumnsPlus" | "Command" | "Comment" | "CommentPlus" | "Comments" | "Companies" | "Company" | "CompanyPlus" | "ConditionDown" | "Confetti" | "Copy" | "CreditCard" | "Cross" | "CrossCircle" | "Currency" | "CustomTemplates" | "TemplatesCog" | "TemplatesEdit" | "TemplatesPlus" | "Dashboard" | "DashboardPlus" | "Data" | "DataText" | "Deal" | "Description" | "Desktop" | "Dollar" | "DollarArrowUp" | "DotsHorizontal" | "DotsVertical" | "Download" | "DragHandle" | "Duplicate" | "Edit" | "EditDisabled" | "Education" | "Email" | "EmailArrowLeft" | "EmailArrowRight" | "EmailDraft" | "EmailMass" | "EmailMassPlus" | "EmailMinus" | "EmailPlus" | "EmailTemplate" | "EmailTemplatePlus" | "EmojiAdd" | "EmptyCircle" | "Enter" | "Equals" | "EstimatedArr" | "Euro" | "EverythingBagel" | "ExpandScreen" | "Eye" | "EyeHide" | "Feedback" | "FileCsv" | "Filter" | "FilterAdd" | "FilterFunnel" | "Flash" | "Folder" | "FolderPlus" | "Forbid" | "FunnelChart" | "FunnelStepsChart" | "GalleryView" | "Gbp" | "GitBranch" | "Globe" | "GlobeHemisphereEast" | "Grid" | "Growth" | "Hand" | "Handshake" | "Hashtag" | "HelpCircle" | "Image" | "ImportExport" | "Inbox" | "InboxCross" | "InboxNotification" | "InfoCircle" | "Input" | "Italic" | "Key" | "KeyboardKey" | "LineChart" | "Link" | "List" | "ListArrowLeft" | "ListEdit" | "ListFlash" | "ListMinus" | "ListPlus" | "ListSearch" | "ListView" | "Location" | "Locked" | "LogOut" | "MapChart" | "Maximise" | "Merge" | "Message" | "Minimise" | "Minus" | "MinusSquare" | "MobilePhone" | "Moon" | "More" | "Move" | "MultiSelect" | "Name" | "None" | "Note" | "NotePlus" | "NoteTemplate" | "NoteTemplateArrowRight" | "NoteTemplatePlus" | "NumberList" | "ObjectIcon" | "ObjectPlaceholder" | "ObjectPlus" | "PanelLeft" | "PanelRight" | "Paste" | "Pause" | "Percentage" | "Phone" | "PhoneArrowLeft" | "PhoneArrowRight" | "PhoneBusy" | "PhoneCancel" | "PhoneCheck" | "PhoneCross" | "PhoneMissed" | "PhoneNoAnswer" | "PieChart" | "Pin" | "Pinned" | "Placeholder" | "Play" | "Plus" | "PlusSquare" | "Pointer" | "PromptCompletion" | "Record" | "RecordDetailEdit" | "RecordEdit" | "RecordFlash" | "RecordPlus" | "RecordSearch" | "Reference" | "RelationshipConnect" | "RemoveCircle" | "Rename" | "Repeat" | "Replace" | "Reply" | "Report" | "ReportPlus" | "Reset" | "RingBell" | "Robot" | "Rocket" | "Rows" | "RowsPlus" | "Sales" | "Search" | "SearchMinus" | "SearchPlus" | "SectionArrowDown" | "SectionArrowUp" | "Select" | "SelfServed" | "Send" | "SendAdd" | "SendEdit" | "ServiceBell" | "Sessions" | "Settings" | "SettingsArrowRight" | "SettingsCheck" | "SettingsMinus" | "Share" | "ShieldLocker" | "Shortcut" | "ShortcutMinus" | "ShortcutPlus" | "ShowRightSidebar" | "Signature" | "SingleMetricChart" | "SnapGrid" | "SortAscending" | "SortDescending" | "SortPlus" | "Spreadsheet" | "SquareEmpty" | "SquareFilled" | "SquareStrength" | "StageChanged" | "Star" | "StarCross" | "Status" | "Strength" | "Subscription" | "SummariseRecord" | "Sun" | "Switch" | "Table" | "Tag" | "Templates" | "TestTube" | "Text" | "TextIndentLeft" | "TextIndentRight" | "ThumbsDown" | "ThumbsUp" | "TidyUp" | "TimeChart" | "TimeInStatus" | "Timestamp" | "Trash" | "Trigger" | "TriggerPlus" | "Undo" | "Unlink" | "Upload" | "User" | "UserArrowLeft" | "UserArrowRight" | "UserFlash" | "UserHeart" | "UserKey" | "UserMinus" | "UserPhone" | "UserPlaceholder" | "UserPlus" | "Users" | "VideoCamera" | "View" | "ViewPlus" | "Voicemail" | "WarningCircle" | "WarningTriangle" | "Webhooks" | "WorkflowCredit" | "WorkflowRun" | "WorkflowsEdit" | "WorkflowsPlaceholder" | "WorkflowsPlus" | "Workspace";
@@ -1,5 +1,6 @@
1
1
  export * from "./hooks/index.js";
2
2
  export * from "./components/index.js";
3
+ export { runQuery, Query } from "./run-query.js";
3
4
  export * as Forms from "./forms/index.js";
4
5
  export { FormArray } from "./forms/array.js";
5
6
  export { FormNumber } from "./forms/number.js";
@@ -2,3 +2,7 @@
2
2
  * Imperatively runs a GraphQL query.
3
3
  */
4
4
  export declare function runQuery(query: string, variableValues?: Record<string, any>): Promise<any>;
5
+ export type Query<Variables extends Record<string, any>, Result> = {
6
+ __data: Result;
7
+ __variables: Variables;
8
+ } & string;
@@ -102,7 +102,7 @@ export default function AddConnection({ options: { slug, label, description, typ
102
102
  React.createElement(Select, { items: connectionTypes, onSelect: (connectionType) => send({ type: "Choose Connection Type", connectionType }) })))),
103
103
  snapshot.matches("Ask for global") && (React.createElement(React.Fragment, null,
104
104
  React.createElement(Box, null,
105
- React.createElement(Text, null, "Should users of your app be allowed to add multiple connections?")),
105
+ React.createElement(Text, null, "Is this a global connection?")),
106
106
  React.createElement(Box, null,
107
107
  React.createElement(Select, { items: [
108
108
  { value: true, label: "Yes" },
@@ -53,6 +53,14 @@ export default function Dev({ options: { debug } }) {
53
53
  React.createElement(Text, null,
54
54
  "Env: ",
55
55
  JSON.stringify(snapshot.children.env?.getSnapshot().value))),
56
+ React.createElement(Box, null,
57
+ React.createElement(Text, null,
58
+ "Code Gen:",
59
+ " ",
60
+ JSON.stringify(snapshot.children["code-gen"]?.getSnapshot()?.value),
61
+ " ",
62
+ snapshot.children["code-gen"]?.getSnapshot().context.error
63
+ ?.message)),
56
64
  snapshot.context.devVersion?.app_id && (React.createElement(Box, null,
57
65
  React.createElement(Text, null,
58
66
  "App ID: ",
@@ -0,0 +1,146 @@
1
+ import fs from "fs";
2
+ import { parse, visit, validate, print, OperationTypeNode, isObjectType, isListType, isNonNullType, isInputObjectType, isEnumType, getNamedType, validateSchema, } from "graphql";
3
+ import path from "path";
4
+ import { format } from "prettier";
5
+ function findGraphQLFiles(dir) {
6
+ const files = [];
7
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
8
+ for (const entry of entries) {
9
+ const fullPath = path.join(dir, entry.name);
10
+ if (entry.isDirectory()) {
11
+ files.push(...findGraphQLFiles(fullPath));
12
+ }
13
+ else if (entry.isFile() && path.extname(entry.name) === ".graphql") {
14
+ files.push(fullPath);
15
+ }
16
+ }
17
+ return files;
18
+ }
19
+ function generateEnumTypeDefinition(type) {
20
+ const values = type
21
+ .getValues()
22
+ .map((v) => `'${v.name}'`)
23
+ .join(" | ");
24
+ return values;
25
+ }
26
+ function generateTypeDefinition(type) {
27
+ if (isNonNullType(type)) {
28
+ return generateTypeDefinition(type.ofType);
29
+ }
30
+ if (isListType(type)) {
31
+ return `${generateTypeDefinition(type.ofType)}`;
32
+ }
33
+ if (isObjectType(type) || isInputObjectType(type)) {
34
+ return type.name;
35
+ }
36
+ if (isEnumType(type)) {
37
+ return generateEnumTypeDefinition(type);
38
+ }
39
+ switch (type.name) {
40
+ case "Int":
41
+ case "Float":
42
+ return "number";
43
+ case "String":
44
+ case "ID":
45
+ return "string";
46
+ case "Boolean":
47
+ return "boolean";
48
+ default:
49
+ return "any";
50
+ }
51
+ }
52
+ function generateInputObjectTypeDefinition(type) {
53
+ const fields = Object.values(type.getFields())
54
+ .map((field) => `${field.name}: ${generateTypeDefinition(field.type)}`)
55
+ .join(", ");
56
+ return `{ ${fields} }`;
57
+ }
58
+ function generateTypeDefinitionFromSelectionSet(selectionSet, parentType) {
59
+ const fieldDefinitions = selectionSet.selections
60
+ .map((selection) => {
61
+ if (selection.kind === "Field") {
62
+ const field = parentType.getFields()[selection.name.value];
63
+ if (!field) {
64
+ throw new Error(`Field ${selection.name.value} not found in type ${parentType.name}`);
65
+ }
66
+ let fieldType = field.type;
67
+ let fieldTypeDefinition = "";
68
+ if (selection.selectionSet) {
69
+ if (isObjectType(getNamedType(fieldType))) {
70
+ fieldTypeDefinition = generateTypeDefinitionFromSelectionSet(selection.selectionSet, getNamedType(fieldType));
71
+ }
72
+ }
73
+ else {
74
+ fieldTypeDefinition = generateTypeDefinition(fieldType);
75
+ }
76
+ return `${selection.name.value}: ${fieldTypeDefinition}${isListType(fieldType) ? "[]" : ""}`;
77
+ }
78
+ return "";
79
+ })
80
+ .filter(Boolean);
81
+ return `{${fieldDefinitions.join(",")}}`;
82
+ }
83
+ function generateReturnType(selectionSet, schema) {
84
+ const queryType = schema.getQueryType();
85
+ if (!queryType) {
86
+ throw new Error("Query type not found in schema");
87
+ }
88
+ return generateTypeDefinitionFromSelectionSet(selectionSet, queryType);
89
+ }
90
+ function generateOperationFunction(graphqlFileName, operationName, variableDefinitions, schema, selectionSet) {
91
+ const typeName = `${operationName.charAt(0).toUpperCase() + operationName.slice(1)}`;
92
+ const returnType = generateReturnType(selectionSet, schema);
93
+ let input;
94
+ const variables = variableDefinitions
95
+ .map((def) => {
96
+ const varType = schema.getType(print(def.type).replace(/[!]/g, ""));
97
+ const varTypeString = isInputObjectType(varType)
98
+ ? generateInputObjectTypeDefinition(varType)
99
+ : generateTypeDefinition(varType);
100
+ return `${def.variable.name.value}: ${varTypeString}`;
101
+ })
102
+ .join(", ");
103
+ input = `export interface ${typeName}Variables { ${variables} }`;
104
+ return `
105
+ declare module "./${graphqlFileName}" {
106
+ ${input}
107
+
108
+ export interface ${typeName}Result ${returnType}
109
+
110
+ const value: Query<${typeName}Variables, ${typeName}Result>
111
+ export default value
112
+ }
113
+ `;
114
+ }
115
+ export async function generateOperationFromQuery(graphqlFileName, query, schema) {
116
+ const ast = parse(query);
117
+ let operations = "";
118
+ const schemaErrors = validateSchema(schema);
119
+ if (schemaErrors.length > 0) {
120
+ throw new Error(`Schema validation errors: ${schemaErrors.map((e) => e.message).join("\n")}`);
121
+ }
122
+ const validationErrors = validate(schema, ast);
123
+ const filteredErrors = validationErrors.filter((error) => !error.message.includes("is not executable"));
124
+ if (filteredErrors.length > 0) {
125
+ throw new Error(`Validation errors: ${filteredErrors.map((e) => e.message).join("\n")}`);
126
+ }
127
+ visit(ast, {
128
+ OperationDefinition(node) {
129
+ if (node.name && node.operation === OperationTypeNode.QUERY) {
130
+ const operationName = node.name.value;
131
+ const variableDefinitions = node.variableDefinitions || [];
132
+ operations += generateOperationFunction(graphqlFileName, operationName, variableDefinitions, schema, node.selectionSet);
133
+ }
134
+ },
135
+ });
136
+ return format(operations, { parser: "typescript" });
137
+ }
138
+ export async function generateOperations(rootDir, schema) {
139
+ const graphqlFiles = findGraphQLFiles(rootDir);
140
+ let operations = "";
141
+ for (const file of graphqlFiles) {
142
+ const content = fs.readFileSync(file, "utf-8");
143
+ operations += await generateOperationFromQuery(path.basename(file), content, schema);
144
+ }
145
+ return format(operations, { parser: "typescript" });
146
+ }
@@ -0,0 +1,65 @@
1
+ import { codeFrameColumns } from "@babel/code-frame";
2
+ import fs from "fs";
3
+ import { parse, validateSchema, buildASTSchema } from "graphql";
4
+ import { validateSDL } from "graphql/validation/validate.js";
5
+ function formatGraphQlError(e, source, filename) {
6
+ if (e.locations?.length) {
7
+ return `${e.message}\n\n${filename}:${e.locations[0].line}\n\n${e.locations
8
+ .map(({ line, column }) => codeFrameColumns(source, {
9
+ start: { line, column },
10
+ }))
11
+ .join(`\n\n`)}\n`;
12
+ }
13
+ else {
14
+ throw e;
15
+ }
16
+ }
17
+ export function parseSchemaString(schemaString, schemaPath) {
18
+ let parsedSchema;
19
+ try {
20
+ parsedSchema = parse(schemaString);
21
+ }
22
+ catch (ex) {
23
+ throw Object.assign(new Error(formatGraphQlError(ex, schemaString, schemaPath)), {
24
+ code: `GRAPHQL_SYNTAX_ERROR`,
25
+ });
26
+ }
27
+ parsedSchema = {
28
+ ...parsedSchema,
29
+ definitions: parsedSchema.definitions
30
+ .slice()
31
+ .sort((a, b) => {
32
+ if (!a.name)
33
+ return -1;
34
+ if (!b.name)
35
+ return 1;
36
+ if (a.name.value.toLowerCase() > b.name.value.toLowerCase())
37
+ return 1;
38
+ if (a.name.value.toLowerCase() < b.name.value.toLowerCase())
39
+ return -1;
40
+ return 0;
41
+ }),
42
+ };
43
+ const sdlValidationErrors = validateSDL(parsedSchema);
44
+ if (sdlValidationErrors.length) {
45
+ throw Object.assign(new Error(formatGraphQlError(sdlValidationErrors[0], schemaString, schemaPath)), {
46
+ code: `GRAPHQL_SCHEMA_ERROR`,
47
+ });
48
+ }
49
+ const schema = buildASTSchema(parsedSchema, { assumeValid: false, assumeValidSDL: false });
50
+ const schemaValidationErrors = validateSchema(schema);
51
+ if (schemaValidationErrors.length > 0) {
52
+ throw Object.assign(new Error(formatGraphQlError(schemaValidationErrors[0], schemaString, schemaPath)), {
53
+ code: `GRAPHQL_SCHEMA_ERROR`,
54
+ });
55
+ }
56
+ return {
57
+ source: schemaString,
58
+ documentNode: parsedSchema,
59
+ schema,
60
+ };
61
+ }
62
+ export function parseSchema(schemaPath) {
63
+ const schemaString = fs.readFileSync(schemaPath, "utf-8");
64
+ return parseSchemaString(schemaString, schemaPath);
65
+ }
@@ -0,0 +1,4 @@
1
+ declare module "*.graphql" {
2
+ const value: string
3
+ export default value
4
+ }
@@ -1,4 +1,5 @@
1
1
  import { assign, setup } from "xstate";
2
+ import { codeGenMachine } from "./code-gen-machine.js";
2
3
  import { jsMachine } from "./js-machine.js";
3
4
  import { tsMachine } from "./ts-machine.js";
4
5
  const processes = ["javascript", "typescript"];
@@ -9,8 +10,9 @@ export const buildMachine = setup({
9
10
  children: {},
10
11
  },
11
12
  actors: {
12
- typescript: tsMachine,
13
- javascript: jsMachine,
13
+ "typescript": tsMachine,
14
+ "javascript": jsMachine,
15
+ "code-gen": codeGenMachine,
14
16
  },
15
17
  guards: {
16
18
  "all done": ({ context }) => context.failed.length + context.successful.length === processes.length,
@@ -68,10 +70,15 @@ export const buildMachine = setup({
68
70
  },
69
71
  },
70
72
  initial: "Building",
73
+ invoke: {
74
+ src: "javascript",
75
+ id: "javascript",
76
+ input: ({ self }) => ({ parentRef: self, write: true }),
77
+ },
71
78
  },
72
79
  TypeScript: {
73
80
  states: {
74
- Validating: {
81
+ "Validating": {
75
82
  on: {
76
83
  "TypeScript Success": {
77
84
  target: "Waiting",
@@ -88,15 +95,38 @@ export const buildMachine = setup({
88
95
  },
89
96
  },
90
97
  },
98
+ invoke: {
99
+ src: "typescript",
100
+ id: "typescript",
101
+ input: ({ self }) => ({ parentRef: self }),
102
+ },
91
103
  },
92
- Waiting: {
104
+ "Waiting": {
93
105
  always: {
94
106
  target: "#Build Machine.All Done",
95
107
  guard: "all done",
96
108
  },
97
109
  },
110
+ "Code Generating": {
111
+ on: {
112
+ "Code Generation Done": "Validating",
113
+ "Error": {
114
+ target: "Waiting",
115
+ actions: {
116
+ type: "setError",
117
+ params: { process: "typescript" },
118
+ },
119
+ reenter: true,
120
+ },
121
+ },
122
+ invoke: {
123
+ src: "code-gen",
124
+ id: "code-gen",
125
+ input: ({ self }) => ({ parentRef: self }),
126
+ },
127
+ },
98
128
  },
99
- initial: "Validating",
129
+ initial: "Code Generating",
100
130
  },
101
131
  },
102
132
  },
@@ -114,16 +144,4 @@ export const buildMachine = setup({
114
144
  },
115
145
  },
116
146
  initial: "Building",
117
- invoke: [
118
- {
119
- src: "javascript",
120
- id: "javascript",
121
- input: ({ self }) => ({ parentRef: self, write: true }),
122
- },
123
- {
124
- src: "typescript",
125
- id: "typescript",
126
- input: ({ self }) => ({ parentRef: self }),
127
- },
128
- ],
129
147
  });
@@ -0,0 +1,92 @@
1
+ import { sendTo, assign, setup, fromCallback } from "xstate";
2
+ import { parseSchema } from "../graphql/parse-schema.js";
3
+ import { findNodeModulesPath } from "../util/find-node-modules-path.js";
4
+ import { updateOperationTypes } from "../util/update-operation-types.js";
5
+ export const codeGenMachine = setup({
6
+ types: {
7
+ context: {},
8
+ events: {},
9
+ input: {},
10
+ },
11
+ actors: {
12
+ loadSchema: fromCallback(({ sendBack }) => {
13
+ const loadSchema = async () => {
14
+ try {
15
+ const path = await findNodeModulesPath(["schema.graphql"]);
16
+ if (!path) {
17
+ sendBack({ type: "Error", error: new Error("No schema found") });
18
+ return;
19
+ }
20
+ const { schema } = parseSchema(path);
21
+ sendBack({ type: "Schema Loaded", schema });
22
+ }
23
+ catch (error) {
24
+ sendBack({ type: "Error", error });
25
+ }
26
+ };
27
+ loadSchema();
28
+ }),
29
+ generateOperations: fromCallback(({ sendBack, input }) => {
30
+ updateOperationTypes(input.schema)
31
+ .then(() => sendBack({ type: "Done" }))
32
+ .catch((error) => sendBack({ type: "Error", error }));
33
+ }),
34
+ },
35
+ actions: {
36
+ setError: assign({
37
+ error: (_, params) => params.error,
38
+ }),
39
+ setSchema: assign({
40
+ schema: (_, params) => params.schema,
41
+ }),
42
+ raiseDone: sendTo(({ context }) => context.parentRef, { type: "Code Generation Done" }),
43
+ },
44
+ }).createMachine({
45
+ id: "GraphQL Code Generation Machine",
46
+ context: ({ input }) => ({
47
+ schema: null,
48
+ parentRef: input.parentRef,
49
+ }),
50
+ states: {
51
+ "Load Schema": {
52
+ on: {
53
+ "Error": {
54
+ target: "Errored",
55
+ actions: { type: "setError", params: ({ event }) => event },
56
+ },
57
+ "Schema Loaded": {
58
+ target: "Generating Operations",
59
+ actions: { type: "setSchema", params: ({ event }) => event },
60
+ reenter: true,
61
+ },
62
+ },
63
+ invoke: {
64
+ src: "loadSchema",
65
+ },
66
+ },
67
+ "Done": {
68
+ entry: "raiseDone",
69
+ },
70
+ "Errored": {},
71
+ "Generating Operations": {
72
+ on: {
73
+ Done: {
74
+ target: "Done",
75
+ },
76
+ Error: {
77
+ target: "Errored",
78
+ actions: { type: "setError", params: ({ event }) => event },
79
+ },
80
+ },
81
+ invoke: {
82
+ src: "generateOperations",
83
+ input: ({ context }) => ({ schema: context.schema }),
84
+ },
85
+ },
86
+ },
87
+ initial: "Load Schema",
88
+ on: {
89
+ Change: ".Load Schema",
90
+ },
91
+ description: `Generates operations typescript types`,
92
+ });
@@ -9,6 +9,7 @@ import { startUpload } from "../api/start-upload.js";
9
9
  import { loadAppConfigFile } from "../util/app-config.js";
10
10
  import { loadDeveloperConfig, loadInitialDeveloperConfig, } from "../util/load-developer-config.js";
11
11
  import { loadEnv } from "../util/load-env.js";
12
+ import { codeGenMachine } from "./code-gen-machine.js";
12
13
  import { envMachine } from "./env-machine.js";
13
14
  import { jsMachine } from "./js-machine.js";
14
15
  import { tsMachine } from "./ts-machine.js";
@@ -22,10 +23,11 @@ export const devMachine = setup({
22
23
  "have dev version": ({ context }) => Boolean(context.devVersion),
23
24
  },
24
25
  actors: {
25
- javascript: jsMachine,
26
- typescript: tsMachine,
27
- env: envMachine,
28
- loadConfig: fromCallback(({ sendBack }) => {
26
+ "javascript": jsMachine,
27
+ "typescript": tsMachine,
28
+ "env": envMachine,
29
+ "code-gen": codeGenMachine,
30
+ "loadConfig": fromCallback(({ sendBack }) => {
29
31
  const config = loadInitialDeveloperConfig();
30
32
  if (typeof config === "string") {
31
33
  sendBack({ type: "Initialization Error", error: config });
@@ -33,12 +35,12 @@ export const devMachine = setup({
33
35
  }
34
36
  sendBack({ type: "Initialized", config });
35
37
  }),
36
- listenForGraphqlOpen: fromCallback(({ input }) => {
38
+ "listenForGraphqlOpen": fromCallback(({ input }) => {
37
39
  readline.emitKeypressEvents(process.stdin);
38
40
  if (process.stdin.isTTY) {
39
41
  process.stdin.setRawMode(true);
40
42
  }
41
- const handleKeyPress = (str, key) => {
43
+ const handleKeyPress = (_, key) => {
42
44
  if (key.name?.toLowerCase() === "o") {
43
45
  open(`http://localhost:${input.graphqlPort}/graphql`);
44
46
  }
@@ -51,7 +53,7 @@ export const devMachine = setup({
51
53
  }
52
54
  };
53
55
  }),
54
- prepareUpload: fromCallback(({ sendBack }) => {
56
+ "prepareUpload": fromCallback(({ sendBack }) => {
55
57
  const prepareUpload = async () => {
56
58
  const config = await loadDeveloperConfig();
57
59
  if (typeof config === "string")
@@ -76,7 +78,7 @@ export const devMachine = setup({
76
78
  error: typeof error === "string" ? new Error(error) : error,
77
79
  }));
78
80
  }),
79
- upload: fromCallback(({ sendBack, input: { config: { token, developer_slug: developerSlug }, contents, devVersion: { app_id: appId, app_dev_version_id: devVersionId }, }, }) => {
81
+ "upload": fromCallback(({ sendBack, input: { config: { token, developer_slug: developerSlug }, contents, devVersion: { app_id: appId, app_dev_version_id: devVersionId }, }, }) => {
80
82
  const upload = async () => {
81
83
  const { client_bundle_upload_url, server_bundle_upload_url, app_dev_version_bundle_id: bundleId, } = await startUpload({
82
84
  token,
@@ -114,7 +116,7 @@ export const devMachine = setup({
114
116
  };
115
117
  upload().catch((error) => sendBack({ type: "Upload Error", error }));
116
118
  }),
117
- watch: fromCallback(({ sendBack }) => {
119
+ "watch": fromCallback(({ sendBack }) => {
118
120
  const watcher = chokidar.watch([
119
121
  "src/app",
120
122
  "src/assets",
@@ -129,7 +131,7 @@ export const devMachine = setup({
129
131
  watcher.close();
130
132
  };
131
133
  }),
132
- graphql: fromCallback(({ sendBack }) => startGraphqlServer(sendBack)),
134
+ "graphql": fromCallback(({ sendBack }) => startGraphqlServer(sendBack)),
133
135
  },
134
136
  actions: {
135
137
  clearUploadError: assign({ uploadError: undefined }),
@@ -137,6 +139,10 @@ export const devMachine = setup({
137
139
  enqueue.sendTo("javascript", event);
138
140
  enqueue.sendTo("typescript", event);
139
141
  enqueue.sendTo("env", event);
142
+ enqueue.sendTo("code-gen", event);
143
+ }),
144
+ sendCodeGenerationDone: enqueueActions(({ enqueue }) => {
145
+ enqueue.sendTo("typescript", { type: "Change" });
140
146
  }),
141
147
  setConfig: assign({
142
148
  config: (_, params) => params.config,
@@ -180,6 +186,11 @@ export const devMachine = setup({
180
186
  id: "env",
181
187
  input: ({ self }) => ({ parentRef: self }),
182
188
  },
189
+ {
190
+ src: "code-gen",
191
+ id: "code-gen",
192
+ input: ({ self }) => ({ parentRef: self }),
193
+ },
183
194
  { src: "watch" },
184
195
  ],
185
196
  states: {
@@ -276,6 +287,17 @@ export const devMachine = setup({
276
287
  Watching: {},
277
288
  },
278
289
  initial: "Validating",
290
+ on: {
291
+ "Change": {
292
+ target: "TypeScript",
293
+ actions: "sendChange",
294
+ },
295
+ "Code Generation Done": {
296
+ target: "TypeScript",
297
+ description: `Re-enter TypeScript build when code generation is complete`,
298
+ actions: "sendCodeGenerationDone",
299
+ },
300
+ },
279
301
  },
280
302
  Graphql: {
281
303
  invoke: {
@@ -52,7 +52,7 @@ export const jsMachine = setup({
52
52
  }),
53
53
  write,
54
54
  outfile: path.resolve("dist", "index.js"),
55
- loader: { ".png": "dataurl" },
55
+ loader: { ".png": "dataurl", ".graphql": "text" },
56
56
  });
57
57
  return {
58
58
  rebuild: async () => {
@@ -33,6 +33,7 @@ module.exports = [
33
33
  "react-hooks/rules-of-hooks": "error",
34
34
  "react-hooks/exhaustive-deps": "error",
35
35
  "attio/attio-client-import": "error",
36
+ "attio/server-default-export": "error",
36
37
  },
37
38
  settings: {
38
39
  react: {
@@ -39,7 +39,9 @@ module.exports = [
39
39
  "react-hooks/rules-of-hooks": "error",
40
40
  "react-hooks/exhaustive-deps": "error",
41
41
  "@typescript-eslint/no-unused-vars": "error",
42
+ "@typescript-eslint/ban-ts-comment": "off",
42
43
  "attio/attio-client-import": "error",
44
+ "attio/server-default-export": "error",
43
45
  },
44
46
  settings: {
45
47
  react: {
@@ -0,0 +1,6 @@
1
+ /// <reference types="attio/graphql" />
2
+
3
+ declare module "*.graphql" {
4
+ const content: string
5
+ export default content;
6
+ }