attio 0.0.1-experimental.20240920 → 0.0.1-experimental.20240926
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/start-graphql-server.js +32 -0
- package/lib/build/server/generate-server-entry.js +36 -2
- package/lib/client/hooks/index.d.ts +1 -1
- package/lib/client/hooks/use-async-cache.d.ts +23 -0
- package/lib/commands/dev.js +3 -1
- package/lib/machines/dev-machine.js +57 -1
- package/lib/machines/js-machine.js +2 -0
- package/lib/schema.graphql +536 -0
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/util/find-available-port.js +21 -0
- package/package.json +8 -2
- package/schema.graphql +536 -0
- package/lib/client/hooks/use-loader.d.ts +0 -13
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { graphqlServer } from "@hono/graphql-server";
|
|
2
|
+
import { serve } from "@hono/node-server";
|
|
3
|
+
import fs from "fs";
|
|
4
|
+
import { buildSchema } from "graphql";
|
|
5
|
+
import { Hono } from "hono";
|
|
6
|
+
import path, { dirname } from "path";
|
|
7
|
+
import { fileURLToPath } from "url";
|
|
8
|
+
import { findAvailablePort } from "../util/find-available-port.js";
|
|
9
|
+
export function startGraphqlServer(sendBack) {
|
|
10
|
+
let server = null;
|
|
11
|
+
const startServer = async () => {
|
|
12
|
+
const currentFilePath = fileURLToPath(import.meta.url);
|
|
13
|
+
const currentDirPath = dirname(currentFilePath);
|
|
14
|
+
const schemaPath = path.resolve(currentDirPath, "..", "schema.graphql");
|
|
15
|
+
const schemaString = fs.readFileSync(schemaPath, "utf8");
|
|
16
|
+
const port = await findAvailablePort(8700);
|
|
17
|
+
const schema = buildSchema(schemaString);
|
|
18
|
+
const app = new Hono();
|
|
19
|
+
const rootResolver = () => {
|
|
20
|
+
return {};
|
|
21
|
+
};
|
|
22
|
+
app.use("/graphql", graphqlServer({ schema, rootResolver, graphiql: true }));
|
|
23
|
+
server = serve({ fetch: app.fetch, port });
|
|
24
|
+
sendBack({ type: "GraphQL Server Started", port });
|
|
25
|
+
};
|
|
26
|
+
startServer();
|
|
27
|
+
return () => {
|
|
28
|
+
if (!server)
|
|
29
|
+
return;
|
|
30
|
+
server.close();
|
|
31
|
+
};
|
|
32
|
+
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { glob } from "glob";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import { getModuleHash } from "../get-module-hash.js";
|
|
4
|
-
export async function generateServerEntry({ appDirAbsolute, webhooksDirAbsolute, log, }) {
|
|
5
|
-
const [serverFunctionConcretePaths, webhookConcretePaths] = await Promise.all([
|
|
4
|
+
export async function generateServerEntry({ appDirAbsolute, webhooksDirAbsolute, eventDirAbsolute, log, }) {
|
|
5
|
+
const [serverFunctionConcretePaths, webhookConcretePaths, eventConcretePaths] = await Promise.all([
|
|
6
6
|
Promise.all([
|
|
7
7
|
glob("**/*.server.ts", { nodir: true, cwd: appDirAbsolute }),
|
|
8
8
|
glob("**/*.server.js", { nodir: true, cwd: appDirAbsolute }),
|
|
@@ -11,6 +11,10 @@ export async function generateServerEntry({ appDirAbsolute, webhooksDirAbsolute,
|
|
|
11
11
|
glob("**/*.webhook.ts", { nodir: true, cwd: webhooksDirAbsolute }),
|
|
12
12
|
glob("**/*.webhook.js", { nodir: true, cwd: webhooksDirAbsolute }),
|
|
13
13
|
]).then((paths) => paths.flat()),
|
|
14
|
+
Promise.all([
|
|
15
|
+
glob("*.event.ts", { nodir: true, cwd: eventDirAbsolute }),
|
|
16
|
+
glob("*.event.js", { nodir: true, cwd: eventDirAbsolute }),
|
|
17
|
+
]).then((paths) => paths.flat()),
|
|
14
18
|
]);
|
|
15
19
|
const serverFunctionModules = serverFunctionConcretePaths.map((path) => ({
|
|
16
20
|
path,
|
|
@@ -20,6 +24,10 @@ export async function generateServerEntry({ appDirAbsolute, webhooksDirAbsolute,
|
|
|
20
24
|
path,
|
|
21
25
|
id: path.replace(/\.webhook\.(js|ts)$/, ""),
|
|
22
26
|
}));
|
|
27
|
+
const eventModules = eventConcretePaths.map((path) => ({
|
|
28
|
+
path,
|
|
29
|
+
id: path.replace(/\.event\.(js|ts)$/, ""),
|
|
30
|
+
}));
|
|
23
31
|
for (const module of serverFunctionModules) {
|
|
24
32
|
log?.(`🔎 Found server module "${module.path}"`);
|
|
25
33
|
}
|
|
@@ -27,6 +35,7 @@ export async function generateServerEntry({ appDirAbsolute, webhooksDirAbsolute,
|
|
|
27
35
|
|
|
28
36
|
const modules = new Map()
|
|
29
37
|
const webhookModules = new Map()
|
|
38
|
+
const eventModules = new Map()
|
|
30
39
|
|
|
31
40
|
|
|
32
41
|
${serverFunctionModules
|
|
@@ -37,8 +46,13 @@ export async function generateServerEntry({ appDirAbsolute, webhooksDirAbsolute,
|
|
|
37
46
|
.map((module) => `webhookModules.set("${module.id}", () => import(${JSON.stringify(path.join(webhooksDirAbsolute, module.path))}))`)
|
|
38
47
|
.join("\n")}
|
|
39
48
|
|
|
49
|
+
${eventModules
|
|
50
|
+
.map((module) => `eventModules.set("${module.id}", () => import(${JSON.stringify(path.join(eventDirAbsolute, module.path))}))`)
|
|
51
|
+
.join("\n")}
|
|
52
|
+
|
|
40
53
|
var stdin_default;
|
|
41
54
|
var stdin_webhooks_default;
|
|
55
|
+
var stdin_events_default;
|
|
42
56
|
function main() {
|
|
43
57
|
stdin_default = async function(moduleHash, args) {
|
|
44
58
|
const module = modules.get(moduleHash)
|
|
@@ -79,6 +93,26 @@ export async function generateServerEntry({ appDirAbsolute, webhooksDirAbsolute,
|
|
|
79
93
|
|
|
80
94
|
return func(...args)
|
|
81
95
|
}
|
|
96
|
+
|
|
97
|
+
stdin_events_default = async function(eventModuleId, args) {
|
|
98
|
+
const module = eventModules.get(eventModuleId)
|
|
99
|
+
|
|
100
|
+
if (!module) {
|
|
101
|
+
throw new Error("Event handler not found")
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const func = (await module()).default
|
|
105
|
+
|
|
106
|
+
if (!func) {
|
|
107
|
+
throw new Error(\`Default export not found in module \${eventModuleId}\`)
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (typeof func !== "function") {
|
|
111
|
+
throw new Error(\`\${eventModuleId} does not export a function\`)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
return func(...args)
|
|
115
|
+
}
|
|
82
116
|
}
|
|
83
117
|
|
|
84
118
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
export { useDialog } from "./use-dialog.js";
|
|
2
2
|
export { useGraphQL } from "./use-graphql.js";
|
|
3
|
-
export {
|
|
3
|
+
export { useAsyncCache, AsyncCacheConfig, AsyncFunction } from "./use-async-cache.js";
|
|
4
4
|
export { useQuery } from "./use-query.js";
|
|
5
5
|
export { useRecord } from "./use-record.js";
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An async function that returns data.
|
|
3
|
+
*/
|
|
4
|
+
export type AsyncFunction<Input extends Array<any>, Output> = (...args: Input) => Promise<Output>;
|
|
5
|
+
/**
|
|
6
|
+
* A type-safe configuration object for multiple loaders.
|
|
7
|
+
*
|
|
8
|
+
* The key is used to cache the result of the loader.
|
|
9
|
+
* The value is a loader that takes an array of arguments and returns a promise.
|
|
10
|
+
*/
|
|
11
|
+
export interface AsyncCacheConfig {
|
|
12
|
+
[K: string]: AsyncFunction<Array<any>, any> | [AsyncFunction<Array<any>, any>, ...Array<any>];
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* A hook that returns, and caches, the results of calling multiple async functions,
|
|
16
|
+
* typically to load data.
|
|
17
|
+
*/
|
|
18
|
+
export declare function useAsyncCache<Config extends AsyncCacheConfig>(config: Config): {
|
|
19
|
+
values: {
|
|
20
|
+
[K in keyof Config]: Config[K] extends AsyncFunction<any, infer Output> ? Output : Config[K] extends [AsyncFunction<infer Input, infer Output>, ...infer Args] ? Args extends Input ? Output : never : never;
|
|
21
|
+
};
|
|
22
|
+
invalidate: (name: Extract<keyof Config, string>) => void;
|
|
23
|
+
};
|
package/lib/commands/dev.js
CHANGED
|
@@ -88,7 +88,9 @@ export default function Dev({ options: { debug } }) {
|
|
|
88
88
|
React.createElement(Text, { color: "redBright" }, "\u274C Upload Error"),
|
|
89
89
|
React.createElement(Text, null,
|
|
90
90
|
"\u2013 ",
|
|
91
|
-
snapshot.context.uploadError?.message))))
|
|
91
|
+
snapshot.context.uploadError?.message)))),
|
|
92
|
+
snapshot.matches({ Watching: { Graphql: "Started" } }) && (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
93
|
+
React.createElement(Text, null, "Press \"o\" to open GraphQL Explorer")))),
|
|
92
94
|
hasErrors && (React.createElement(ScrollBox, { borderStyle: "round", padding: 1 },
|
|
93
95
|
jsError && renderBuildErrors(jsError),
|
|
94
96
|
tsErrors && renderTypeScriptErrors(tsErrors)))));
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import chokidar from "chokidar";
|
|
2
|
+
import open from "open";
|
|
3
|
+
import readline from "readline";
|
|
2
4
|
import { assign, fromCallback, setup, enqueueActions } from "xstate";
|
|
3
5
|
import { completeBundleUpload } from "../api/complete-bundle-upload.js";
|
|
4
6
|
import { createDevVersion } from "../api/create-dev-version.js";
|
|
7
|
+
import { startGraphqlServer } from "../api/start-graphql-server.js";
|
|
5
8
|
import { startUpload } from "../api/start-upload.js";
|
|
6
9
|
import { loadAppConfigFile } from "../util/app-config.js";
|
|
7
10
|
import { loadDeveloperConfig, loadInitialDeveloperConfig, } from "../util/load-developer-config.js";
|
|
@@ -30,6 +33,24 @@ export const devMachine = setup({
|
|
|
30
33
|
}
|
|
31
34
|
sendBack({ type: "Initialized", config });
|
|
32
35
|
}),
|
|
36
|
+
listenForGraphqlOpen: fromCallback(({ input }) => {
|
|
37
|
+
readline.emitKeypressEvents(process.stdin);
|
|
38
|
+
if (process.stdin.isTTY) {
|
|
39
|
+
process.stdin.setRawMode(true);
|
|
40
|
+
}
|
|
41
|
+
const handleKeyPress = (str, key) => {
|
|
42
|
+
if (key.name?.toLowerCase() === "o") {
|
|
43
|
+
open(`http://localhost:${input.graphqlPort}/graphql`);
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
process.stdin.on("keypress", handleKeyPress);
|
|
47
|
+
return () => {
|
|
48
|
+
process.stdin.removeListener("keypress", handleKeyPress);
|
|
49
|
+
if (process.stdin.isTTY) {
|
|
50
|
+
process.stdin.setRawMode(false);
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}),
|
|
33
54
|
prepareUpload: fromCallback(({ sendBack }) => {
|
|
34
55
|
const prepareUpload = async () => {
|
|
35
56
|
const config = await loadDeveloperConfig();
|
|
@@ -94,7 +115,13 @@ export const devMachine = setup({
|
|
|
94
115
|
upload().catch((error) => sendBack({ type: "Upload Error", error }));
|
|
95
116
|
}),
|
|
96
117
|
watch: fromCallback(({ sendBack }) => {
|
|
97
|
-
const watcher = chokidar.watch([
|
|
118
|
+
const watcher = chokidar.watch([
|
|
119
|
+
"src/app",
|
|
120
|
+
"src/assets",
|
|
121
|
+
"src/webhooks",
|
|
122
|
+
"src/events",
|
|
123
|
+
".env",
|
|
124
|
+
]);
|
|
98
125
|
watcher.on("ready", () => watcher.on("all", () => {
|
|
99
126
|
sendBack({ type: "Change" });
|
|
100
127
|
}));
|
|
@@ -102,6 +129,7 @@ export const devMachine = setup({
|
|
|
102
129
|
watcher.close();
|
|
103
130
|
};
|
|
104
131
|
}),
|
|
132
|
+
graphql: fromCallback(({ sendBack }) => startGraphqlServer(sendBack)),
|
|
105
133
|
},
|
|
106
134
|
actions: {
|
|
107
135
|
clearUploadError: assign({ uploadError: undefined }),
|
|
@@ -119,6 +147,9 @@ export const devMachine = setup({
|
|
|
119
147
|
setDevVersion: assign({
|
|
120
148
|
devVersion: (_, params) => params.devVersion,
|
|
121
149
|
}),
|
|
150
|
+
setGraphqlPort: assign({
|
|
151
|
+
graphqlPort: (_, params) => params.port,
|
|
152
|
+
}),
|
|
122
153
|
setSuccess: assign({
|
|
123
154
|
jsContents: (_, params) => params.contents,
|
|
124
155
|
lastSuccessfulJavaScriptBuild: (_, params) => params.time,
|
|
@@ -246,6 +277,31 @@ export const devMachine = setup({
|
|
|
246
277
|
},
|
|
247
278
|
initial: "Validating",
|
|
248
279
|
},
|
|
280
|
+
Graphql: {
|
|
281
|
+
invoke: {
|
|
282
|
+
src: "graphql",
|
|
283
|
+
},
|
|
284
|
+
states: {
|
|
285
|
+
"Not Started": {
|
|
286
|
+
on: {
|
|
287
|
+
"GraphQL Server Started": {
|
|
288
|
+
target: "Started",
|
|
289
|
+
actions: {
|
|
290
|
+
type: "setGraphqlPort",
|
|
291
|
+
params: ({ event }) => event,
|
|
292
|
+
},
|
|
293
|
+
},
|
|
294
|
+
},
|
|
295
|
+
},
|
|
296
|
+
"Started": {
|
|
297
|
+
invoke: {
|
|
298
|
+
src: "listenForGraphqlOpen",
|
|
299
|
+
input: ({ context }) => ({ graphqlPort: context.graphqlPort }),
|
|
300
|
+
},
|
|
301
|
+
},
|
|
302
|
+
},
|
|
303
|
+
initial: "Not Started",
|
|
304
|
+
},
|
|
249
305
|
},
|
|
250
306
|
type: "parallel",
|
|
251
307
|
},
|
|
@@ -24,6 +24,7 @@ export const jsMachine = setup({
|
|
|
24
24
|
const appDir = "src/app";
|
|
25
25
|
const assetsDir = "src/assets";
|
|
26
26
|
const webhooksDir = "src/webhooks";
|
|
27
|
+
const eventsDir = "src/events";
|
|
27
28
|
const log = (message) => {
|
|
28
29
|
sendBack({ type: "Log", message });
|
|
29
30
|
};
|
|
@@ -72,6 +73,7 @@ export const jsMachine = setup({
|
|
|
72
73
|
const js = await generateServerEntry({
|
|
73
74
|
appDirAbsolute: path.resolve(appDir),
|
|
74
75
|
webhooksDirAbsolute: path.resolve(webhooksDir),
|
|
76
|
+
eventDirAbsolute: path.resolve(eventsDir),
|
|
75
77
|
log,
|
|
76
78
|
});
|
|
77
79
|
if (js === lastJS) {
|