attio 0.0.1-experimental.20240911.2 → 0.0.1-experimental.20240913

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.
@@ -1,12 +1,8 @@
1
1
  import globalExternals from "@fal-works/esbuild-plugin-global-externals";
2
2
  import { proxyServerModulesPlugin } from "../proxy-server-modules-plugin.js";
3
- export function createClientBuildConfig({ appDir, contents, }) {
3
+ export function createClientBuildConfig({ appDir, entryPoint, }) {
4
4
  return {
5
- stdin: {
6
- contents,
7
- resolveDir: ".",
8
- loader: "tsx",
9
- },
5
+ entryPoints: [entryPoint],
10
6
  logLevel: "silent",
11
7
  bundle: true,
12
8
  platform: "browser",
@@ -14,7 +10,7 @@ export function createClientBuildConfig({ appDir, contents, }) {
14
10
  plugins: [
15
11
  globalExternals({
16
12
  "react": "React",
17
- "@attio/extension-sdk": {
13
+ "attio/client": {
18
14
  varName: "ATTIO_CLIENT_EXTENSION_SDK",
19
15
  type: "cjs",
20
16
  },
@@ -1,7 +1,9 @@
1
+ import fs from "fs/promises";
1
2
  import { glob } from "glob";
2
3
  import path from "path";
3
4
  const ROUTE_FILE_EXTENSIONS = ["tsx", "jsx", "ts", "js"];
4
- export async function generateClientEntry({ appDir, routes, log, }) {
5
+ const ASSET_FILE_EXTENSIONS = ["png"];
6
+ export async function generateClientEntry({ filePath, appDir, assetsDir, routes, log, }) {
5
7
  log?.(`💥 Found entry point at ${path.resolve(appDir)}`);
6
8
  const paths = routes.flatMap((route) => ROUTE_FILE_EXTENSIONS.map((fileExtension) => route + "." + fileExtension));
7
9
  const concreteRoutes = (await Promise.all(paths.map(async (path) => {
@@ -22,11 +24,36 @@ export async function generateClientEntry({ appDir, routes, log, }) {
22
24
  return [];
23
25
  }
24
26
  }))).flat();
27
+ let assetFiles;
28
+ try {
29
+ assetFiles = await fs.readdir(assetsDir, { recursive: true });
30
+ }
31
+ catch {
32
+ assetFiles = [];
33
+ }
34
+ const assets = assetFiles
35
+ .filter((relativeAssetPath) => ASSET_FILE_EXTENSIONS.some((extension) => relativeAssetPath.endsWith(extension)))
36
+ .map((relativeAssetPath) => ({
37
+ path: path.join(assetsDir, relativeAssetPath),
38
+ name: relativeAssetPath,
39
+ }));
25
40
  return `
26
41
  ${concreteRoutes
27
- .map((routeAndPath, index) => `import C${index} from ${JSON.stringify(`.${path.sep}${path.join(appDir, routeAndPath.path)}`)}`)
42
+ .map((routeAndPath, index) => `import C${index} from ${JSON.stringify(path.relative(filePath, path.join(appDir, routeAndPath.path)))}`)
28
43
  .join("\n")}
29
44
 
45
+ const assets = []
46
+
47
+ ${assets
48
+ .map((asset, index) => `
49
+ import A${index} from ${JSON.stringify(path.relative(filePath, asset.path))};
50
+
51
+ assets.push({name: ${JSON.stringify(asset.name)}, data: A${index}})
52
+ `)
53
+ .join("\n")}
54
+
55
+ registerAssets(assets)
56
+
30
57
  ${concreteRoutes
31
58
  .map((routeAndPath, index) => `registerExtensionComponent({route: ${JSON.stringify(routeAndPath.route)}, component: C${index}})`)
32
59
  .join("\n")}
@@ -1,17 +1,13 @@
1
1
  import globalExternals from "@fal-works/esbuild-plugin-global-externals";
2
- export function createServerBuildConfig(contents) {
2
+ export function createServerBuildConfig(entryPoint) {
3
3
  return {
4
- stdin: {
5
- contents,
6
- resolveDir: ".",
7
- loader: "ts",
8
- },
4
+ entryPoints: [entryPoint],
9
5
  bundle: true,
10
6
  platform: "browser",
11
7
  format: "esm",
12
8
  plugins: [
13
9
  globalExternals({
14
- "@attio/extension-sdk": {
10
+ "attio/client": {
15
11
  varName: "ATTIO_CLIENT_EXTENSION_SDK",
16
12
  type: "cjs",
17
13
  },
@@ -1,55 +1,89 @@
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({ appDir, log, }) {
5
- const concretePaths = (await Promise.all([
6
- glob("**/*.server.ts", { nodir: true, cwd: appDir }),
7
- glob("**/*.server.js", { nodir: true, cwd: appDir }),
8
- ])).flat();
9
- const modules = concretePaths.map((path) => ({
4
+ export async function generateServerEntry({ appDir, webhooksDir, filePath, log, }) {
5
+ const [serverFunctionConcretePaths, webhookConcretePaths] = await Promise.all([
6
+ Promise.all([
7
+ glob("**/*.server.ts", { nodir: true, cwd: appDir }),
8
+ glob("**/*.server.js", { nodir: true, cwd: appDir }),
9
+ ]).then((paths) => paths.flat()),
10
+ Promise.all([
11
+ glob("**/*.webhook.ts", { nodir: true, cwd: webhooksDir }),
12
+ glob("**/*.webhook.js", { nodir: true, cwd: webhooksDir }),
13
+ ]).then((paths) => paths.flat()),
14
+ ]);
15
+ const serverFunctionModules = serverFunctionConcretePaths.map((path) => ({
10
16
  path,
11
17
  hash: getModuleHash(path.replace(/\.(js|ts)$/, "")),
12
18
  }));
13
- for (const module of modules) {
19
+ const webhookModules = webhookConcretePaths.map((path) => ({
20
+ path,
21
+ id: path.replace(/\.webhook\.(js|ts)$/, ""),
22
+ }));
23
+ for (const module of serverFunctionModules) {
14
24
  log?.(`🔎 Found server module "${module.path}"`);
15
25
  }
16
26
  return `
17
27
 
18
28
  const modules = new Map()
19
-
29
+ const webhookModules = new Map()
20
30
 
21
31
 
22
- ${modules
23
- .map((module) => `modules.set("${module.hash}", () => import(${JSON.stringify(`.${path.sep}${path.join(appDir, module.path)}`)}))`)
32
+ ${serverFunctionModules
33
+ .map((module) => `modules.set("${module.hash}", () => import(${JSON.stringify(path.relative(filePath, path.join(appDir, module.path)))}))`)
24
34
  .join("\n")}
25
35
 
36
+ ${webhookModules
37
+ .map((module) => `webhookModules.set("${module.id}", () => import(${JSON.stringify(path.relative(filePath, path.join(webhooksDir, module.path)))}))`)
38
+ .join("\n")}
26
39
 
27
- var stdin_default;
28
- function main() {
29
- stdin_default = async function(moduleHash, args) {
40
+ var stdin_default;
41
+ var stdin_webhooks_default;
42
+ function main() {
43
+ stdin_default = async function(moduleHash, args) {
44
+ const module = modules.get(moduleHash)
30
45
 
46
+ if (!module) {
47
+ throw new Error(\`Module \${moduleHash} not found\`)
48
+ }
31
49
 
32
- const module = modules.get(moduleHash)
50
+ const func = (await module()).default
33
51
 
34
- if (!module) {
35
- throw new Error(\`Module \${moduleHash} not found\`)
36
- }
52
+ if (!func) {
53
+ throw new Error(\`Default export not found in module \${moduleHash}\`)
54
+ }
37
55
 
38
- const func = (await module()).default
56
+ if (typeof func !== "function") {
57
+ throw new Error(\`\${moduleHash} does not export a function\`)
58
+ }
39
59
 
40
- if (!func) {
41
- throw new Error(\`Default export not found in module \${moduleHash}\`)
42
- }
60
+ return func(...args)
61
+ }
62
+
63
+ stdin_webhooks_default = async function(webhookModuleId, args) {
64
+ const module = webhookModules.get(webhookModuleId)
43
65
 
44
- if (typeof func !== "function") {
45
- throw new Error(\`\${moduleHash} does not export a function\`)
66
+ if (!module) {
67
+ throw new Error(\`Webhook handler not found: \${webhookModuleId}\`)
68
+ }
69
+
70
+ const func = (await module()).default
71
+
72
+ if (!func) {
73
+ throw new Error(\`Default export not found in module \${webhookModuleId}\`)
74
+ }
75
+
76
+ if (typeof func !== "function") {
77
+ throw new Error(\`\${webhookModuleId} does not export a function\`)
78
+ }
79
+
80
+ return func(...args)
81
+ }
46
82
  }
47
83
 
48
- return func(...args)
49
- }
50
- }
51
84
 
52
- main()
85
+
86
+ main()
53
87
 
54
88
  `;
55
89
  }
@@ -93,7 +93,7 @@ export const devMachine = setup({
93
93
  upload().catch((error) => sendBack({ type: "Upload Error", error }));
94
94
  }),
95
95
  watch: fromCallback(({ sendBack }) => {
96
- const watcher = chokidar.watch("src/app");
96
+ const watcher = chokidar.watch(["src/app", "src/assets", "src/webhooks"]);
97
97
  watcher.on("ready", () => watcher.on("all", () => {
98
98
  sendBack({ type: "Change" });
99
99
  }));
@@ -1,5 +1,7 @@
1
1
  import * as esbuild from "esbuild";
2
+ import fs from "fs/promises";
2
3
  import path from "path";
4
+ import tmp from "tmp-promise";
3
5
  import { assign, setup, fromCallback, sendTo } from "xstate";
4
6
  import { ROUTES, errorSchema } from "../build.js";
5
7
  import { createClientBuildConfig } from "../build/client/create-client-build-config.js";
@@ -20,20 +22,85 @@ export const jsMachine = setup({
20
22
  let buildContexts;
21
23
  const prepare = async () => {
22
24
  const appDir = "src/app";
25
+ const assetsDir = "src/assets";
26
+ const webhooksDir = "src/webhooks";
23
27
  const log = (message) => {
24
28
  sendBack({ type: "Log", message });
25
29
  };
26
30
  buildContexts = (await Promise.all([
27
- generateClientEntry({ appDir, routes: ROUTES, log }).then(async (contents) => await esbuild.context({
28
- ...createClientBuildConfig({ contents, appDir }),
29
- write,
30
- outfile: path.resolve("dist", "index.js"),
31
- })),
32
- generateServerEntry({ appDir, log }).then(async (contents) => await esbuild.context({
33
- ...createServerBuildConfig(contents),
34
- write,
35
- outfile: path.resolve("dist", "server.js"),
36
- })),
31
+ tmp.file({ postfix: ".js" }).then(async (tempFile) => {
32
+ let lastJS;
33
+ const updateTempFile = async () => {
34
+ const js = await generateClientEntry({
35
+ filePath: tempFile.path,
36
+ appDir,
37
+ assetsDir,
38
+ routes: ROUTES,
39
+ log,
40
+ });
41
+ if (js === lastJS) {
42
+ return;
43
+ }
44
+ lastJS = js;
45
+ await fs.writeFile(tempFile.path, js);
46
+ };
47
+ await updateTempFile();
48
+ const esbuildContext = await esbuild.context({
49
+ ...createClientBuildConfig({
50
+ entryPoint: tempFile.path,
51
+ appDir,
52
+ }),
53
+ write,
54
+ outfile: path.resolve("dist", "index.js"),
55
+ loader: { ".png": "dataurl" },
56
+ });
57
+ return {
58
+ rebuild: async () => {
59
+ await updateTempFile();
60
+ return esbuildContext.rebuild();
61
+ },
62
+ dispose: async () => {
63
+ await Promise.all([
64
+ esbuildContext.dispose(),
65
+ tempFile.cleanup(),
66
+ ]);
67
+ },
68
+ };
69
+ }),
70
+ tmp.file({ postfix: ".js" }).then(async (tempFile) => {
71
+ let lastJS;
72
+ const updateTempFile = async () => {
73
+ const js = await generateServerEntry({
74
+ filePath: tempFile.path,
75
+ appDir,
76
+ webhooksDir,
77
+ log,
78
+ });
79
+ if (js === lastJS) {
80
+ return;
81
+ }
82
+ lastJS = js;
83
+ await fs.writeFile(tempFile.path, js);
84
+ };
85
+ await updateTempFile();
86
+ const esbuildContext = await esbuild.context({
87
+ ...createServerBuildConfig(tempFile.path),
88
+ write,
89
+ outfile: path.resolve("dist", "server.js"),
90
+ });
91
+ return {
92
+ rebuild: async () => {
93
+ await updateTempFile();
94
+ return esbuildContext.rebuild();
95
+ },
96
+ dispose: async () => {
97
+ await Promise.all([
98
+ esbuildContext.dispose(),
99
+ tempFile.cleanup(),
100
+ ]);
101
+ },
102
+ };
103
+ }),
37
104
  ])).flat();
38
105
  sendBack({
39
106
  type: "Build Contexts Prepared",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "attio",
3
- "version": "0.0.1-experimental.20240911.2",
3
+ "version": "0.0.1-experimental.20240913",
4
4
  "files": [
5
5
  "lib",
6
6
  "attio.js",