@confect/cli 1.0.0 → 1.0.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,14 @@
1
1
  # @confect/cli
2
2
 
3
+ ## 1.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - d3ecdc7: Fix codegen failure in projects without `"type": "module"` in their `package.json`. Also dramatically improve codegen performance.
8
+ - Updated dependencies [00b12a0]
9
+ - @confect/core@1.0.1
10
+ - @confect/server@1.0.1
11
+
3
12
  ## 1.0.0
4
13
 
5
14
  ### Major Changes
package/README.md ADDED
@@ -0,0 +1,11 @@
1
+ # Confect 🧁
2
+
3
+ Confect is a framework that deeply integrates Effect with Convex. It's more than just Effect bindings! Confect allows you to:
4
+
5
+ - Define your Convex database schema using Effect schemas.
6
+ - Write Convex function args and returns validators using Effect's schema library.
7
+ - Use Confect functions to automatically decode and encode your data according to your Effect schema definitions for end-to-end rich types, from client to function to database (and back).
8
+ - Use Effect's HTTP API modules to define your HTTP API(s). Includes interactive OpenAPI documentation powered by [Scalar](https://github.com/scalar/scalar).
9
+ - Access Convex platform capabilities via Effect services.
10
+
11
+ Want to learn more? Read the [docs](https://confect.dev)!
@@ -2,13 +2,12 @@ import { logFileAdded, logFileModified, logFileRemoved, logPending, logSuccess }
2
2
  import { ConvexDirectory } from "../services/ConvexDirectory.mjs";
3
3
  import { ConfectDirectory } from "../services/ConfectDirectory.mjs";
4
4
  import { api, nodeApi, nodeRegisteredFunctions, refs, registeredFunctions, schema, services } from "../templates.mjs";
5
- import { generateAuthConfig, generateConvexConfig, generateCrons, generateFunctions, generateHttp, removePathExtension, writeFileStringAndLog } from "../utils.mjs";
5
+ import { bundleAndImport, generateAuthConfig, generateConvexConfig, generateCrons, generateFunctions, generateHttp, removePathExtension, writeFileStringAndLog } from "../utils.mjs";
6
6
  import { Effect, Match, Option } from "effect";
7
7
  import { Command } from "@effect/cli";
8
8
  import { Spec } from "@confect/core";
9
9
  import { DatabaseSchema } from "@confect/server";
10
10
  import { FileSystem, Path } from "@effect/platform";
11
- import * as tsx from "tsx/esm/api";
12
11
 
13
12
  //#region src/confect/codegen.ts
14
13
  const getNodeSpecPath = Effect.gen(function* () {
@@ -18,11 +17,9 @@ const getNodeSpecPath = Effect.gen(function* () {
18
17
  });
19
18
  const loadNodeSpec = Effect.gen(function* () {
20
19
  const fs = yield* FileSystem.FileSystem;
21
- const path = yield* Path.Path;
22
20
  const nodeSpecPath = yield* getNodeSpecPath;
23
21
  if (!(yield* fs.exists(nodeSpecPath))) return Option.none();
24
- const nodeSpecPathUrl = yield* path.toFileUrl(nodeSpecPath);
25
- const nodeSpec = (yield* Effect.promise(() => tsx.tsImport(nodeSpecPathUrl.href, import.meta.url))).default;
22
+ const nodeSpec = (yield* bundleAndImport(nodeSpecPath)).default;
26
23
  if (!Spec.isNodeSpec(nodeSpec)) return yield* Effect.die("nodeSpec.ts does not export a valid Node Spec");
27
24
  return Option.some(nodeSpec);
28
25
  });
@@ -97,9 +94,7 @@ const generateFunctionModules = Effect.gen(function* () {
97
94
  const fs = yield* FileSystem.FileSystem;
98
95
  const path = yield* Path.Path;
99
96
  const confectDirectory = yield* ConfectDirectory.get;
100
- const specPath = path.join(confectDirectory, "spec.ts");
101
- const specPathUrl = yield* path.toFileUrl(specPath);
102
- const spec = (yield* Effect.promise(() => tsx.tsImport(specPathUrl.href, import.meta.url))).default;
97
+ const spec = (yield* bundleAndImport(path.join(confectDirectory, "spec.ts"))).default;
103
98
  if (!Spec.isConvexSpec(spec)) return yield* Effect.die("spec.ts does not export a valid Convex Spec");
104
99
  const nodeImplPath = path.join(confectDirectory, "nodeImpl.ts");
105
100
  const nodeImplExists = yield* fs.exists(nodeImplPath);
@@ -114,8 +109,7 @@ const generateSchema = Effect.gen(function* () {
114
109
  const confectDirectory = yield* ConfectDirectory.get;
115
110
  const convexDirectory = yield* ConvexDirectory.get;
116
111
  const confectSchemaPath = path.join(confectDirectory, "schema.ts");
117
- const confectSchemaUrl = yield* path.toFileUrl(confectSchemaPath);
118
- yield* Effect.promise(() => tsx.tsImport(confectSchemaUrl.href, import.meta.url)).pipe(Effect.andThen((schemaModule) => {
112
+ yield* bundleAndImport(confectSchemaPath).pipe(Effect.andThen((schemaModule) => {
119
113
  const defaultExport = schemaModule.default;
120
114
  return DatabaseSchema.isSchema(defaultExport) ? Effect.succeed(defaultExport) : Effect.die("Invalid schema module");
121
115
  }));
@@ -1 +1 @@
1
- {"version":3,"file":"codegen.mjs","names":["templates.api","templates.nodeApi","templates.schema","templates.services","templates.registeredFunctions","templates.nodeRegisteredFunctions","templates.refs"],"sources":["../../src/confect/codegen.ts"],"sourcesContent":["import { Spec } from \"@confect/core\";\nimport { DatabaseSchema } from \"@confect/server\";\nimport { Command } from \"@effect/cli\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Effect, Match, Option } from \"effect\";\nimport * as tsx from \"tsx/esm/api\";\nimport {\n logFileAdded,\n logFileModified,\n logFileRemoved,\n logPending,\n logSuccess,\n} from \"../log\";\nimport { ConfectDirectory } from \"../services/ConfectDirectory\";\nimport { ConvexDirectory } from \"../services/ConvexDirectory\";\nimport * as templates from \"../templates\";\nimport {\n generateAuthConfig,\n generateConvexConfig,\n generateCrons,\n generateFunctions,\n generateHttp,\n removePathExtension,\n writeFileStringAndLog,\n} from \"../utils\";\n\nconst getNodeSpecPath = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n return path.join(confectDirectory, \"nodeSpec.ts\");\n});\n\nconst loadNodeSpec = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const nodeSpecPath = yield* getNodeSpecPath;\n\n if (!(yield* fs.exists(nodeSpecPath))) {\n return Option.none<Spec.AnyWithPropsWithRuntime<\"Node\">>();\n }\n\n const nodeSpecPathUrl = yield* path.toFileUrl(nodeSpecPath);\n const nodeSpecModule = yield* Effect.promise(() =>\n tsx.tsImport(nodeSpecPathUrl.href, import.meta.url),\n );\n const nodeSpec = nodeSpecModule.default;\n\n if (!Spec.isNodeSpec(nodeSpec)) {\n return yield* Effect.die(\"nodeSpec.ts does not export a valid Node Spec\");\n }\n\n return Option.some(nodeSpec);\n});\n\nexport const codegen = Command.make(\"codegen\", {}, () =>\n Effect.gen(function* () {\n yield* logPending(\"Performing initial sync…\");\n yield* codegenHandler;\n yield* logSuccess(\"Generated files are up-to-date\");\n }),\n).pipe(\n Command.withDescription(\n \"Generate `confect/_generated` files and the contents of the `convex` directory (except `tsconfig.json`)\",\n ),\n);\n\nexport const codegenHandler = Effect.gen(function* () {\n yield* generateConfectGeneratedDirectory;\n yield* Effect.all(\n [\n generateApi,\n generateRefs,\n generateRegisteredFunctions,\n generateNodeApi,\n generateNodeRegisteredFunctions,\n generateServices,\n ],\n { concurrency: \"unbounded\" },\n );\n const [functionPaths] = yield* Effect.all(\n [\n generateFunctionModules,\n generateSchema,\n logGenerated(generateHttp),\n logGenerated(generateConvexConfig),\n logGenerated(generateCrons),\n logGenerated(generateAuthConfig),\n ],\n { concurrency: \"unbounded\" },\n );\n return functionPaths;\n});\n\nconst generateConfectGeneratedDirectory = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n if (!(yield* fs.exists(path.join(confectDirectory, \"_generated\")))) {\n yield* fs.makeDirectory(path.join(confectDirectory, \"_generated\"), {\n recursive: true,\n });\n yield* logFileAdded(path.join(confectDirectory, \"_generated\") + \"/\");\n }\n});\n\nconst generateApi = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const apiPath = path.join(confectDirectory, \"_generated\", \"api.ts\");\n const apiDir = path.dirname(apiPath);\n\n const schemaImportPath = yield* removePathExtension(\n path.relative(apiDir, path.join(confectDirectory, \"schema.ts\")),\n );\n\n const specImportPath = yield* removePathExtension(\n path.relative(apiDir, path.join(confectDirectory, \"spec.ts\")),\n );\n\n const apiContents = yield* templates.api({\n schemaImportPath,\n specImportPath,\n });\n\n yield* writeFileStringAndLog(apiPath, apiContents);\n});\n\nexport const generateNodeApi = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeApiPath = path.join(confectDirectory, \"_generated\", \"nodeApi.ts\");\n\n if (!(yield* fs.exists(nodeSpecPath))) {\n if (yield* fs.exists(nodeApiPath)) {\n yield* fs.remove(nodeApiPath);\n yield* logFileRemoved(nodeApiPath);\n }\n return;\n }\n\n const nodeApiDir = path.dirname(nodeApiPath);\n\n const schemaImportPath = yield* removePathExtension(\n path.relative(nodeApiDir, path.join(confectDirectory, \"schema.ts\")),\n );\n\n const nodeSpecImportPath = yield* removePathExtension(\n path.relative(nodeApiDir, nodeSpecPath),\n );\n\n const nodeApiContents = yield* templates.nodeApi({\n schemaImportPath,\n nodeSpecImportPath,\n });\n\n yield* writeFileStringAndLog(nodeApiPath, nodeApiContents);\n});\n\nconst generateFunctionModules = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const specPath = path.join(confectDirectory, \"spec.ts\");\n const specPathUrl = yield* path.toFileUrl(specPath);\n\n const specModule = yield* Effect.promise(() =>\n tsx.tsImport(specPathUrl.href, import.meta.url),\n );\n const spec = specModule.default;\n\n if (!Spec.isConvexSpec(spec)) {\n return yield* Effect.die(\"spec.ts does not export a valid Convex Spec\");\n }\n\n const nodeImplPath = path.join(confectDirectory, \"nodeImpl.ts\");\n const nodeImplExists = yield* fs.exists(nodeImplPath);\n const nodeSpecOption = yield* loadNodeSpec;\n\n const mergedSpec = Option.match(nodeSpecOption, {\n onNone: () => spec,\n onSome: (nodeSpec) => (nodeImplExists ? Spec.merge(spec, nodeSpec) : spec),\n });\n\n return yield* generateFunctions(mergedSpec);\n});\n\nconst generateSchema = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const confectSchemaPath = path.join(confectDirectory, \"schema.ts\");\n const confectSchemaUrl = yield* path.toFileUrl(confectSchemaPath);\n\n yield* Effect.promise(() =>\n tsx.tsImport(confectSchemaUrl.href, import.meta.url),\n ).pipe(\n Effect.andThen((schemaModule) => {\n const defaultExport = schemaModule.default;\n\n return DatabaseSchema.isSchema(defaultExport)\n ? Effect.succeed(defaultExport)\n : Effect.die(\"Invalid schema module\");\n }),\n );\n\n const convexSchemaPath = path.join(convexDirectory, \"schema.ts\");\n\n const relativeImportPath = path.relative(\n path.dirname(convexSchemaPath),\n confectSchemaPath,\n );\n const importPathWithoutExt = yield* removePathExtension(relativeImportPath);\n const schemaContents = yield* templates.schema({\n schemaImportPath: importPathWithoutExt,\n });\n\n yield* writeFileStringAndLog(convexSchemaPath, schemaContents);\n});\n\nconst generateServices = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n\n const servicesPath = path.join(confectGeneratedDirectory, \"services.ts\");\n const schemaImportPath = path.relative(\n path.dirname(servicesPath),\n path.join(confectDirectory, \"schema\"),\n );\n\n const servicesContentsString = yield* templates.services({\n schemaImportPath,\n });\n\n yield* writeFileStringAndLog(servicesPath, servicesContentsString);\n});\n\nconst generateRegisteredFunctions = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n\n const registeredFunctionsPath = path.join(\n confectGeneratedDirectory,\n \"registeredFunctions.ts\",\n );\n const implImportPath = yield* removePathExtension(\n path.relative(\n path.dirname(registeredFunctionsPath),\n path.join(confectDirectory, \"impl.ts\"),\n ),\n );\n\n const registeredFunctionsContents = yield* templates.registeredFunctions({\n implImportPath,\n });\n\n yield* writeFileStringAndLog(\n registeredFunctionsPath,\n registeredFunctionsContents,\n );\n});\n\nexport const generateNodeRegisteredFunctions = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const nodeImplPath = path.join(confectDirectory, \"nodeImpl.ts\");\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeRegisteredFunctionsPath = path.join(\n confectDirectory,\n \"_generated\",\n \"nodeRegisteredFunctions.ts\",\n );\n\n const nodeImplExists = yield* fs.exists(nodeImplPath);\n const nodeSpecExists = yield* fs.exists(nodeSpecPath);\n\n if (!nodeImplExists || !nodeSpecExists) {\n if (yield* fs.exists(nodeRegisteredFunctionsPath)) {\n yield* fs.remove(nodeRegisteredFunctionsPath);\n yield* logFileRemoved(nodeRegisteredFunctionsPath);\n }\n return;\n }\n\n const nodeImplImportPath = yield* removePathExtension(\n path.relative(path.dirname(nodeRegisteredFunctionsPath), nodeImplPath),\n );\n\n const nodeRegisteredFunctionsContents =\n yield* templates.nodeRegisteredFunctions({\n nodeImplImportPath,\n });\n\n yield* writeFileStringAndLog(\n nodeRegisteredFunctionsPath,\n nodeRegisteredFunctionsContents,\n );\n});\n\nconst generateRefs = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n const refsPath = path.join(confectGeneratedDirectory, \"refs.ts\");\n const refsDir = path.dirname(refsPath);\n\n const specImportPath = yield* removePathExtension(\n path.relative(refsDir, path.join(confectDirectory, \"spec.ts\")),\n );\n\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeSpecExists = yield* fs.exists(nodeSpecPath);\n const nodeSpecImportPath = nodeSpecExists\n ? yield* removePathExtension(path.relative(refsDir, nodeSpecPath))\n : null;\n\n const refsContents = yield* templates.refs({\n specImportPath,\n ...(nodeSpecImportPath === null ? {} : { nodeSpecImportPath }),\n });\n\n yield* writeFileStringAndLog(refsPath, refsContents);\n});\n\nconst logGenerated = (effect: typeof generateHttp) =>\n effect.pipe(\n Effect.tap(\n Option.match({\n onNone: () => Effect.void,\n onSome: ({ change, convexFilePath }) =>\n Match.value(change).pipe(\n Match.when(\"Added\", () => logFileAdded(convexFilePath)),\n Match.when(\"Modified\", () => logFileModified(convexFilePath)),\n Match.when(\"Unchanged\", () => Effect.void),\n Match.exhaustive,\n ),\n }),\n ),\n );\n"],"mappings":"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"}
1
+ {"version":3,"file":"codegen.mjs","names":["templates.api","templates.nodeApi","templates.schema","templates.services","templates.registeredFunctions","templates.nodeRegisteredFunctions","templates.refs"],"sources":["../../src/confect/codegen.ts"],"sourcesContent":["import { Spec } from \"@confect/core\";\nimport { DatabaseSchema } from \"@confect/server\";\nimport { Command } from \"@effect/cli\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Effect, Match, Option } from \"effect\";\nimport {\n logFileAdded,\n logFileModified,\n logFileRemoved,\n logPending,\n logSuccess,\n} from \"../log\";\nimport { ConfectDirectory } from \"../services/ConfectDirectory\";\nimport { ConvexDirectory } from \"../services/ConvexDirectory\";\nimport * as templates from \"../templates\";\nimport {\n bundleAndImport,\n generateAuthConfig,\n generateConvexConfig,\n generateCrons,\n generateFunctions,\n generateHttp,\n removePathExtension,\n writeFileStringAndLog,\n} from \"../utils\";\n\nconst getNodeSpecPath = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n return path.join(confectDirectory, \"nodeSpec.ts\");\n});\n\nconst loadNodeSpec = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const nodeSpecPath = yield* getNodeSpecPath;\n\n if (!(yield* fs.exists(nodeSpecPath))) {\n return Option.none<Spec.AnyWithPropsWithRuntime<\"Node\">>();\n }\n\n const nodeSpecModule = yield* bundleAndImport(nodeSpecPath);\n const nodeSpec = nodeSpecModule.default;\n\n if (!Spec.isNodeSpec(nodeSpec)) {\n return yield* Effect.die(\"nodeSpec.ts does not export a valid Node Spec\");\n }\n\n return Option.some(nodeSpec);\n});\n\nexport const codegen = Command.make(\"codegen\", {}, () =>\n Effect.gen(function* () {\n yield* logPending(\"Performing initial sync…\");\n yield* codegenHandler;\n yield* logSuccess(\"Generated files are up-to-date\");\n }),\n).pipe(\n Command.withDescription(\n \"Generate `confect/_generated` files and the contents of the `convex` directory (except `tsconfig.json`)\",\n ),\n);\n\nexport const codegenHandler = Effect.gen(function* () {\n yield* generateConfectGeneratedDirectory;\n yield* Effect.all(\n [\n generateApi,\n generateRefs,\n generateRegisteredFunctions,\n generateNodeApi,\n generateNodeRegisteredFunctions,\n generateServices,\n ],\n { concurrency: \"unbounded\" },\n );\n const [functionPaths] = yield* Effect.all(\n [\n generateFunctionModules,\n generateSchema,\n logGenerated(generateHttp),\n logGenerated(generateConvexConfig),\n logGenerated(generateCrons),\n logGenerated(generateAuthConfig),\n ],\n { concurrency: \"unbounded\" },\n );\n return functionPaths;\n});\n\nconst generateConfectGeneratedDirectory = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n if (!(yield* fs.exists(path.join(confectDirectory, \"_generated\")))) {\n yield* fs.makeDirectory(path.join(confectDirectory, \"_generated\"), {\n recursive: true,\n });\n yield* logFileAdded(path.join(confectDirectory, \"_generated\") + \"/\");\n }\n});\n\nconst generateApi = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const apiPath = path.join(confectDirectory, \"_generated\", \"api.ts\");\n const apiDir = path.dirname(apiPath);\n\n const schemaImportPath = yield* removePathExtension(\n path.relative(apiDir, path.join(confectDirectory, \"schema.ts\")),\n );\n\n const specImportPath = yield* removePathExtension(\n path.relative(apiDir, path.join(confectDirectory, \"spec.ts\")),\n );\n\n const apiContents = yield* templates.api({\n schemaImportPath,\n specImportPath,\n });\n\n yield* writeFileStringAndLog(apiPath, apiContents);\n});\n\nexport const generateNodeApi = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeApiPath = path.join(confectDirectory, \"_generated\", \"nodeApi.ts\");\n\n if (!(yield* fs.exists(nodeSpecPath))) {\n if (yield* fs.exists(nodeApiPath)) {\n yield* fs.remove(nodeApiPath);\n yield* logFileRemoved(nodeApiPath);\n }\n return;\n }\n\n const nodeApiDir = path.dirname(nodeApiPath);\n\n const schemaImportPath = yield* removePathExtension(\n path.relative(nodeApiDir, path.join(confectDirectory, \"schema.ts\")),\n );\n\n const nodeSpecImportPath = yield* removePathExtension(\n path.relative(nodeApiDir, nodeSpecPath),\n );\n\n const nodeApiContents = yield* templates.nodeApi({\n schemaImportPath,\n nodeSpecImportPath,\n });\n\n yield* writeFileStringAndLog(nodeApiPath, nodeApiContents);\n});\n\nconst generateFunctionModules = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const specPath = path.join(confectDirectory, \"spec.ts\");\n\n const specModule = yield* bundleAndImport(specPath);\n const spec = specModule.default;\n\n if (!Spec.isConvexSpec(spec)) {\n return yield* Effect.die(\"spec.ts does not export a valid Convex Spec\");\n }\n\n const nodeImplPath = path.join(confectDirectory, \"nodeImpl.ts\");\n const nodeImplExists = yield* fs.exists(nodeImplPath);\n const nodeSpecOption = yield* loadNodeSpec;\n\n const mergedSpec = Option.match(nodeSpecOption, {\n onNone: () => spec,\n onSome: (nodeSpec) => (nodeImplExists ? Spec.merge(spec, nodeSpec) : spec),\n });\n\n return yield* generateFunctions(mergedSpec);\n});\n\nconst generateSchema = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const confectSchemaPath = path.join(confectDirectory, \"schema.ts\");\n\n yield* bundleAndImport(confectSchemaPath).pipe(\n Effect.andThen((schemaModule) => {\n const defaultExport = schemaModule.default;\n\n return DatabaseSchema.isSchema(defaultExport)\n ? Effect.succeed(defaultExport)\n : Effect.die(\"Invalid schema module\");\n }),\n );\n\n const convexSchemaPath = path.join(convexDirectory, \"schema.ts\");\n\n const relativeImportPath = path.relative(\n path.dirname(convexSchemaPath),\n confectSchemaPath,\n );\n const importPathWithoutExt = yield* removePathExtension(relativeImportPath);\n const schemaContents = yield* templates.schema({\n schemaImportPath: importPathWithoutExt,\n });\n\n yield* writeFileStringAndLog(convexSchemaPath, schemaContents);\n});\n\nconst generateServices = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n\n const servicesPath = path.join(confectGeneratedDirectory, \"services.ts\");\n const schemaImportPath = path.relative(\n path.dirname(servicesPath),\n path.join(confectDirectory, \"schema\"),\n );\n\n const servicesContentsString = yield* templates.services({\n schemaImportPath,\n });\n\n yield* writeFileStringAndLog(servicesPath, servicesContentsString);\n});\n\nconst generateRegisteredFunctions = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n\n const registeredFunctionsPath = path.join(\n confectGeneratedDirectory,\n \"registeredFunctions.ts\",\n );\n const implImportPath = yield* removePathExtension(\n path.relative(\n path.dirname(registeredFunctionsPath),\n path.join(confectDirectory, \"impl.ts\"),\n ),\n );\n\n const registeredFunctionsContents = yield* templates.registeredFunctions({\n implImportPath,\n });\n\n yield* writeFileStringAndLog(\n registeredFunctionsPath,\n registeredFunctionsContents,\n );\n});\n\nexport const generateNodeRegisteredFunctions = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const nodeImplPath = path.join(confectDirectory, \"nodeImpl.ts\");\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeRegisteredFunctionsPath = path.join(\n confectDirectory,\n \"_generated\",\n \"nodeRegisteredFunctions.ts\",\n );\n\n const nodeImplExists = yield* fs.exists(nodeImplPath);\n const nodeSpecExists = yield* fs.exists(nodeSpecPath);\n\n if (!nodeImplExists || !nodeSpecExists) {\n if (yield* fs.exists(nodeRegisteredFunctionsPath)) {\n yield* fs.remove(nodeRegisteredFunctionsPath);\n yield* logFileRemoved(nodeRegisteredFunctionsPath);\n }\n return;\n }\n\n const nodeImplImportPath = yield* removePathExtension(\n path.relative(path.dirname(nodeRegisteredFunctionsPath), nodeImplPath),\n );\n\n const nodeRegisteredFunctionsContents =\n yield* templates.nodeRegisteredFunctions({\n nodeImplImportPath,\n });\n\n yield* writeFileStringAndLog(\n nodeRegisteredFunctionsPath,\n nodeRegisteredFunctionsContents,\n );\n});\n\nconst generateRefs = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n const refsPath = path.join(confectGeneratedDirectory, \"refs.ts\");\n const refsDir = path.dirname(refsPath);\n\n const specImportPath = yield* removePathExtension(\n path.relative(refsDir, path.join(confectDirectory, \"spec.ts\")),\n );\n\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeSpecExists = yield* fs.exists(nodeSpecPath);\n const nodeSpecImportPath = nodeSpecExists\n ? yield* removePathExtension(path.relative(refsDir, nodeSpecPath))\n : null;\n\n const refsContents = yield* templates.refs({\n specImportPath,\n ...(nodeSpecImportPath === null ? {} : { nodeSpecImportPath }),\n });\n\n yield* writeFileStringAndLog(refsPath, refsContents);\n});\n\nconst logGenerated = (effect: typeof generateHttp) =>\n effect.pipe(\n Effect.tap(\n Option.match({\n onNone: () => Effect.void,\n onSome: ({ change, convexFilePath }) =>\n Match.value(change).pipe(\n Match.when(\"Added\", () => logFileAdded(convexFilePath)),\n Match.when(\"Modified\", () => logFileModified(convexFilePath)),\n Match.when(\"Unchanged\", () => Effect.void),\n Match.exhaustive,\n ),\n }),\n ),\n );\n"],"mappings":"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"}
@@ -4,13 +4,12 @@ import { logFailure, logPending, logSuccess } from "../log.mjs";
4
4
  import { ConvexDirectory } from "../services/ConvexDirectory.mjs";
5
5
  import { ConfectDirectory } from "../services/ConfectDirectory.mjs";
6
6
  import { diff, make } from "../FunctionPaths.mjs";
7
- import { generateAuthConfig, generateConvexConfig, generateCrons, generateHttp, removeGroups, writeGroups } from "../utils.mjs";
7
+ import { EXTERNAL_PACKAGES, bundleAndImport, generateAuthConfig, generateConvexConfig, generateCrons, generateHttp, removeGroups, writeGroups } from "../utils.mjs";
8
8
  import { codegenHandler, generateNodeApi, generateNodeRegisteredFunctions } from "./codegen.mjs";
9
9
  import { Array, Console, Deferred, Duration, Effect, Equal, HashSet, Match, Option, Queue, Ref, Schema, Stream, String, pipe } from "effect";
10
10
  import { Command } from "@effect/cli";
11
11
  import { Spec } from "@confect/core";
12
12
  import { FileSystem, Path } from "@effect/platform";
13
- import * as tsx from "tsx/esm/api";
14
13
  import { Ansi, AnsiDoc } from "@effect/printer-ansi";
15
14
  import * as esbuild from "esbuild";
16
15
 
@@ -116,11 +115,7 @@ const loadSpec = Effect.gen(function* () {
116
115
  const fs = yield* FileSystem.FileSystem;
117
116
  const path = yield* Path.Path;
118
117
  const confectDirectory = yield* ConfectDirectory.get;
119
- const specPathUrl = yield* path.toFileUrl(yield* getSpecPath);
120
- const spec = (yield* Effect.tryPromise({
121
- try: () => tsx.tsImport(specPathUrl.href, import.meta.url),
122
- catch: (error) => new SpecImportFailedError({ error })
123
- })).default;
118
+ const spec = (yield* bundleAndImport(yield* getSpecPath).pipe(Effect.mapError((error) => new SpecImportFailedError({ error })))).default;
124
119
  if (!Spec.isConvexSpec(spec)) return yield* new SpecFileDoesNotExportSpecError();
125
120
  const nodeImplPath = path.join(confectDirectory, "nodeImpl.ts");
126
121
  const nodeImplExists = yield* fs.exists(nodeImplPath);
@@ -142,14 +137,9 @@ const getNodeSpecPath = Effect.gen(function* () {
142
137
  });
143
138
  const loadNodeSpec = Effect.gen(function* () {
144
139
  const fs = yield* FileSystem.FileSystem;
145
- const path = yield* Path.Path;
146
140
  const nodeSpecPath = yield* getNodeSpecPath;
147
141
  if (!(yield* fs.exists(nodeSpecPath))) return Option.none();
148
- const nodeSpecPathUrl = yield* path.toFileUrl(nodeSpecPath);
149
- const nodeSpec = (yield* Effect.tryPromise({
150
- try: () => tsx.tsImport(nodeSpecPathUrl.href, import.meta.url),
151
- catch: (error) => new SpecImportFailedError({ error })
152
- })).default;
142
+ const nodeSpec = (yield* bundleAndImport(nodeSpecPath).pipe(Effect.mapError((error) => new SpecImportFailedError({ error })))).default;
153
143
  if (!Spec.isNodeSpec(nodeSpec)) return yield* new NodeSpecFileDoesNotExportSpecError();
154
144
  return Option.some(nodeSpec);
155
145
  });
@@ -161,12 +151,7 @@ const esbuildOptions = (entryPoint) => ({
161
151
  platform: "node",
162
152
  format: "esm",
163
153
  logLevel: "silent",
164
- external: [
165
- "@confect/core",
166
- "@confect/server",
167
- "effect",
168
- "@effect/*"
169
- ],
154
+ external: EXTERNAL_PACKAGES,
170
155
  plugins: [{
171
156
  name: "notify-rebuild",
172
157
  setup(build) {
@@ -1 +1 @@
1
- {"version":3,"file":"dev.mjs","names":["GroupPath.toString","FunctionPaths.make","FunctionPaths.diff","GroupPath.modulePath"],"sources":["../../src/confect/dev.ts"],"sourcesContent":["import { Spec } from \"@confect/core\";\nimport { Command } from \"@effect/cli\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Ansi, AnsiDoc } from \"@effect/printer-ansi\";\nimport {\n Array,\n Console,\n Deferred,\n Duration,\n Effect,\n Equal,\n HashSet,\n Match,\n Option,\n pipe,\n Queue,\n Ref,\n Schema,\n Stream,\n String,\n} from \"effect\";\nimport type { ReadonlyRecord } from \"effect/Record\";\nimport * as esbuild from \"esbuild\";\nimport * as tsx from \"tsx/esm/api\";\nimport type * as FunctionPath from \"../FunctionPath\";\nimport * as FunctionPaths from \"../FunctionPaths\";\nimport * as GroupPath from \"../GroupPath\";\nimport { logFailure, logPending, logSuccess } from \"../log\";\nimport { ConfectDirectory } from \"../services/ConfectDirectory\";\nimport { ConvexDirectory } from \"../services/ConvexDirectory\";\nimport { ProjectRoot } from \"../services/ProjectRoot\";\nimport {\n generateAuthConfig,\n generateConvexConfig,\n generateCrons,\n generateHttp,\n removeGroups,\n writeGroups,\n} from \"../utils\";\nimport {\n codegenHandler,\n generateNodeApi,\n generateNodeRegisteredFunctions,\n} from \"./codegen\";\n\ntype Pending = {\n readonly specDirty: boolean;\n readonly nodeImplDirty: boolean;\n readonly httpDirty: boolean;\n readonly appDirty: boolean;\n readonly cronsDirty: boolean;\n readonly authDirty: boolean;\n};\n\nconst pendingInit: Pending = {\n specDirty: false,\n nodeImplDirty: false,\n httpDirty: false,\n appDirty: false,\n cronsDirty: false,\n authDirty: false,\n};\n\nconst changeChar = (change: \"Added\" | \"Removed\" | \"Modified\") =>\n Match.value(change).pipe(\n Match.when(\"Added\", () => ({ char: \"+\", color: Ansi.green })),\n Match.when(\"Removed\", () => ({ char: \"-\", color: Ansi.red })),\n Match.when(\"Modified\", () => ({ char: \"~\", color: Ansi.yellow })),\n Match.exhaustive,\n );\n\nconst logFileChangeIndented = (\n change: \"Added\" | \"Removed\" | \"Modified\",\n fullPath: string,\n) =>\n Effect.gen(function* () {\n const projectRoot = yield* ProjectRoot.get;\n const path = yield* Path.Path;\n\n const prefix = projectRoot + path.sep;\n const suffix = pipe(fullPath, String.startsWith(prefix))\n ? pipe(fullPath, String.slice(prefix.length))\n : fullPath;\n\n const { char, color } = changeChar(change);\n\n yield* Console.log(\n pipe(\n AnsiDoc.char(char),\n AnsiDoc.annotate(color),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(AnsiDoc.text(prefix), AnsiDoc.annotate(Ansi.blackBright)),\n pipe(AnsiDoc.text(suffix), AnsiDoc.annotate(color)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n });\n\nconst logFunctionAddedIndented = (functionPath: FunctionPath.FunctionPath) =>\n Console.log(\n pipe(\n AnsiDoc.text(\" \"),\n AnsiDoc.cat(pipe(AnsiDoc.char(\"+\"), AnsiDoc.annotate(Ansi.green))),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(\n AnsiDoc.text(GroupPath.toString(functionPath.groupPath) + \".\"),\n AnsiDoc.annotate(Ansi.blackBright),\n ),\n pipe(AnsiDoc.text(functionPath.name), AnsiDoc.annotate(Ansi.green)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n\nconst logFunctionRemovedIndented = (functionPath: FunctionPath.FunctionPath) =>\n Console.log(\n pipe(\n AnsiDoc.text(\" \"),\n AnsiDoc.cat(pipe(AnsiDoc.char(\"-\"), AnsiDoc.annotate(Ansi.red))),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(\n AnsiDoc.text(GroupPath.toString(functionPath.groupPath) + \".\"),\n AnsiDoc.annotate(Ansi.blackBright),\n ),\n pipe(AnsiDoc.text(functionPath.name), AnsiDoc.annotate(Ansi.red)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n\nexport const dev = Command.make(\"dev\", {}, () =>\n Effect.gen(function* () {\n yield* logPending(\"Performing initial sync…\");\n const initialFunctionPaths = yield* codegenHandler;\n\n const pendingRef = yield* Ref.make<Pending>(pendingInit);\n const signal = yield* Queue.sliding<void>(1);\n const specWatcherRestartQueue = yield* Queue.sliding<void>(1);\n\n yield* Effect.all(\n [\n specFileWatcher(signal, pendingRef, specWatcherRestartQueue),\n confectDirectoryWatcher(signal, pendingRef, specWatcherRestartQueue),\n syncLoop(signal, pendingRef, initialFunctionPaths),\n ],\n { concurrency: \"unbounded\" },\n );\n }),\n).pipe(Command.withDescription(\"Start the Confect development server\"));\n\nconst syncLoop = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n initialFunctionPaths: FunctionPaths.FunctionPaths,\n) =>\n Effect.gen(function* () {\n const functionPathsRef = yield* Ref.make(initialFunctionPaths);\n const initialSyncDone = yield* Deferred.make<void>();\n\n return yield* Effect.forever(\n Effect.gen(function* () {\n yield* Effect.logDebug(\"Running sync loop...\");\n yield* Queue.take(signal);\n\n const isDone = yield* Deferred.isDone(initialSyncDone);\n yield* Effect.when(\n logPending(\"Dependencies changed, reloading…\"),\n () => isDone,\n );\n yield* Deferred.succeed(initialSyncDone, undefined);\n\n const pending = yield* Ref.getAndSet(pendingRef, pendingInit);\n\n if (pending.specDirty || pending.nodeImplDirty) {\n yield* generateNodeApi;\n yield* generateNodeRegisteredFunctions;\n }\n\n const specResult: Option.Option<void> = yield* Effect.if(\n pending.specDirty,\n {\n onTrue: () =>\n loadSpec.pipe(\n Effect.andThen(\n Effect.fn(function* (spec) {\n yield* Effect.logDebug(\"Spec loaded\");\n\n const previous = yield* Ref.get(functionPathsRef);\n\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const current = FunctionPaths.make(spec);\n const {\n functionsAdded,\n functionsRemoved,\n groupsRemoved,\n groupsAdded,\n groupsChanged,\n } = FunctionPaths.diff(previous, current);\n\n // Removed groups\n yield* removeGroups(groupsRemoved);\n yield* Effect.forEach(groupsRemoved, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath =\n yield* GroupPath.modulePath(gp);\n const filePath = path.join(\n convexDirectory,\n relativeModulePath,\n );\n yield* logFileChangeIndented(\"Removed\", filePath);\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsRemoved, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionRemovedIndented,\n );\n }),\n );\n\n // Added groups\n yield* writeGroups(spec, groupsAdded);\n yield* Effect.forEach(groupsAdded, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath =\n yield* GroupPath.modulePath(gp);\n const filePath = path.join(\n convexDirectory,\n relativeModulePath,\n );\n yield* logFileChangeIndented(\"Added\", filePath);\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsAdded, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionAddedIndented,\n );\n }),\n );\n\n // Changed groups\n yield* writeGroups(spec, groupsChanged);\n yield* Effect.forEach(groupsChanged, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath =\n yield* GroupPath.modulePath(gp);\n const filePath = path.join(\n convexDirectory,\n relativeModulePath,\n );\n yield* logFileChangeIndented(\"Modified\", filePath);\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsAdded, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionAddedIndented,\n );\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsRemoved, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionRemovedIndented,\n );\n }),\n );\n\n yield* Ref.set(functionPathsRef, current);\n\n return Option.some(undefined);\n }),\n ),\n Effect.catchTag(\"SpecImportFailedError\", () =>\n logFailure(\"Spec import failed\").pipe(\n Effect.as(Option.none()),\n ),\n ),\n Effect.catchTag(\"SpecFileDoesNotExportSpecError\", () =>\n logFailure(\n \"Spec file does not default export a Convex spec\",\n ).pipe(Effect.as(Option.none())),\n ),\n Effect.catchTag(\"NodeSpecFileDoesNotExportSpecError\", () =>\n logFailure(\n \"Node spec file does not default export a Node spec\",\n ).pipe(Effect.as(Option.none())),\n ),\n ),\n onFalse: () => Effect.succeed(Option.some(undefined)),\n },\n );\n\n const dirtyOptionalFiles = [\n ...(pending.httpDirty\n ? [syncOptionalFile(generateHttp, \"http.ts\")]\n : []),\n ...(pending.appDirty\n ? [syncOptionalFile(generateConvexConfig, \"convex.config.ts\")]\n : []),\n ...(pending.cronsDirty\n ? [syncOptionalFile(generateCrons, \"crons.ts\")]\n : []),\n ...(pending.authDirty\n ? [syncOptionalFile(generateAuthConfig, \"auth.config.ts\")]\n : []),\n ];\n\n yield* Array.isNonEmptyReadonlyArray(dirtyOptionalFiles)\n ? Effect.all(dirtyOptionalFiles, { concurrency: \"unbounded\" })\n : Effect.void;\n\n yield* Option.match(specResult, {\n onSome: () => logSuccess(\"Generated files are up-to-date\"),\n onNone: () => Effect.void,\n });\n }),\n );\n });\n\nconst loadSpec = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const specPathUrl = yield* path.toFileUrl(yield* getSpecPath);\n const specModule = yield* Effect.tryPromise({\n try: () => tsx.tsImport(specPathUrl.href, import.meta.url),\n catch: (error) => new SpecImportFailedError({ error }),\n });\n const spec = specModule.default;\n\n if (!Spec.isConvexSpec(spec)) {\n return yield* new SpecFileDoesNotExportSpecError();\n }\n\n const nodeImplPath = path.join(confectDirectory, \"nodeImpl.ts\");\n const nodeImplExists = yield* fs.exists(nodeImplPath);\n const nodeSpecOption = yield* loadNodeSpec;\n const mergedSpec = Option.match(nodeSpecOption, {\n onNone: () => spec,\n onSome: (nodeSpec) => (nodeImplExists ? Spec.merge(spec, nodeSpec) : spec),\n });\n\n return mergedSpec;\n});\n\nconst getSpecPath = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n return path.join(confectDirectory, \"spec.ts\");\n});\n\nconst getNodeSpecPath = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n return path.join(confectDirectory, \"nodeSpec.ts\");\n});\n\nconst loadNodeSpec = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const nodeSpecPath = yield* getNodeSpecPath;\n\n if (!(yield* fs.exists(nodeSpecPath))) {\n return Option.none();\n }\n\n const nodeSpecPathUrl = yield* path.toFileUrl(nodeSpecPath);\n const nodeSpecModule = yield* Effect.tryPromise({\n try: () => tsx.tsImport(nodeSpecPathUrl.href, import.meta.url),\n catch: (error) => new SpecImportFailedError({ error }),\n });\n const nodeSpec = nodeSpecModule.default;\n\n if (!Spec.isNodeSpec(nodeSpec)) {\n return yield* new NodeSpecFileDoesNotExportSpecError();\n }\n\n return Option.some(nodeSpec);\n});\n\nconst esbuildOptions = (entryPoint: string) => ({\n entryPoints: [entryPoint],\n bundle: true,\n write: false,\n metafile: true,\n platform: \"node\" as const,\n format: \"esm\" as const,\n logLevel: \"silent\" as const,\n external: [\"@confect/core\", \"@confect/server\", \"effect\", \"@effect/*\"],\n plugins: [\n {\n name: \"notify-rebuild\",\n setup(build: esbuild.PluginBuild) {\n build.onEnd((result) => {\n if (result.errors.length === 0) {\n (build as { _emit?: (v: void) => void })._emit?.();\n } else {\n Effect.runPromise(\n Effect.gen(function* () {\n const formattedMessages = yield* Effect.promise(() =>\n esbuild.formatMessages(result.errors, {\n kind: \"error\",\n color: true,\n terminalWidth: 80,\n }),\n );\n const output = formatBuildErrors(\n result.errors,\n formattedMessages,\n );\n yield* Console.error(\"\\n\" + output + \"\\n\");\n yield* logFailure(\"Build errors found\");\n }),\n );\n }\n });\n },\n },\n ],\n});\n\nconst createSpecWatcher = (entryPoint: string) =>\n Stream.asyncPush<void>(\n (emit) =>\n Effect.acquireRelease(\n Effect.promise(async () => {\n const opts = esbuildOptions(entryPoint);\n const plugin = opts.plugins[0];\n const originalSetup = plugin!.setup!;\n (plugin as { setup: (build: esbuild.PluginBuild) => void }).setup = (\n build,\n ) => {\n (build as { _emit?: (v: void) => void })._emit = () =>\n emit.single();\n return originalSetup(build);\n };\n\n const ctx = await esbuild.context({\n ...opts,\n plugins: [plugin],\n });\n\n await ctx.watch();\n return ctx;\n }),\n (ctx) =>\n Effect.promise(() => ctx.dispose()).pipe(\n Effect.tap(() => Effect.logDebug(\"esbuild watcher disposed\")),\n ),\n ),\n { bufferSize: 1, strategy: \"sliding\" },\n );\n\ntype SpecWatcherEvent = \"change\" | \"restart\";\n\nconst specFileWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n specWatcherRestartQueue: Queue.Queue<void>,\n) =>\n Effect.forever(\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const specPath = yield* getSpecPath;\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeSpecExists = yield* fs.exists(nodeSpecPath);\n\n const specWatcher = createSpecWatcher(specPath);\n const nodeSpecWatcher = nodeSpecExists\n ? createSpecWatcher(nodeSpecPath)\n : Stream.empty;\n\n const specChanges = pipe(\n Stream.merge(specWatcher, nodeSpecWatcher),\n Stream.map((): SpecWatcherEvent => \"change\"),\n );\n const restartStream = pipe(\n Stream.fromQueue(specWatcherRestartQueue),\n Stream.map((): SpecWatcherEvent => \"restart\"),\n );\n\n yield* pipe(\n Stream.merge(specChanges, restartStream),\n Stream.debounce(Duration.millis(200)),\n Stream.takeUntil((event): event is \"restart\" => event === \"restart\"),\n Stream.runForEach((event) =>\n event === \"change\"\n ? Ref.update(pendingRef, (pending) => ({\n ...pending,\n specDirty: true,\n })).pipe(Effect.andThen(Queue.offer(signal, undefined)))\n : Effect.void,\n ),\n );\n }),\n );\n\nconst formatBuildError = (\n error: esbuild.Message | undefined,\n formattedMessage: string,\n): string => {\n const lines = String.split(formattedMessage, \"\\n\");\n const redErrorText = pipe(\n AnsiDoc.text(error?.text ?? \"\"),\n AnsiDoc.annotate(Ansi.red),\n AnsiDoc.render({ style: \"pretty\" }),\n );\n const replaced = pipe(\n Array.findFirstIndex(lines, (l) => pipe(l, String.trim, String.isNonEmpty)),\n Option.match({\n onNone: () => lines,\n onSome: (index) => Array.modify(lines, index, () => redErrorText),\n }),\n );\n return pipe(replaced, Array.join(\"\\n\"));\n};\n\nconst formatBuildErrors = (\n errors: readonly esbuild.Message[],\n formattedMessages: readonly string[],\n): string =>\n pipe(\n formattedMessages,\n Array.map((message, i) => formatBuildError(errors[i], message)),\n Array.join(\"\"),\n String.trimEnd,\n );\n\nexport class SpecFileDoesNotExportSpecError extends Schema.TaggedError<SpecFileDoesNotExportSpecError>()(\n \"SpecFileDoesNotExportSpecError\",\n {},\n) {}\n\nexport class NodeSpecFileDoesNotExportSpecError extends Schema.TaggedError<NodeSpecFileDoesNotExportSpecError>()(\n \"NodeSpecFileDoesNotExportSpecError\",\n {},\n) {}\n\nexport class SpecImportFailedError extends Schema.TaggedError<SpecImportFailedError>()(\n \"SpecImportFailedError\",\n {\n error: Schema.Unknown,\n },\n) {}\n\nconst syncOptionalFile = (generate: typeof generateHttp, convexFile: string) =>\n pipe(\n generate,\n Effect.andThen(\n Option.match({\n onSome: ({ change, convexFilePath }) =>\n Match.value(change).pipe(\n Match.when(\"Unchanged\", () => Effect.void),\n Match.whenOr(\"Added\", \"Modified\", (addedOrModified) =>\n logFileChangeIndented(addedOrModified, convexFilePath),\n ),\n Match.exhaustive,\n ),\n onNone: () =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const convexFilePath = path.join(convexDirectory, convexFile);\n\n if (yield* fs.exists(convexFilePath)) {\n yield* fs.remove(convexFilePath);\n yield* logFileChangeIndented(\"Removed\", convexFilePath);\n }\n }),\n }),\n ),\n );\n\nconst optionalConfectFiles: ReadonlyRecord<string, keyof Pending> = {\n \"http.ts\": \"httpDirty\",\n \"app.ts\": \"appDirty\",\n \"crons.ts\": \"cronsDirty\",\n \"auth.ts\": \"authDirty\",\n \"nodeSpec.ts\": \"specDirty\",\n \"nodeImpl.ts\": \"nodeImplDirty\",\n};\n\nconst confectDirectoryWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n specWatcherRestartQueue: Queue.Queue<void>,\n) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n yield* pipe(\n fs.watch(confectDirectory),\n Stream.runForEach((event) => {\n const basename = path.basename(event.path);\n const pendingKey = optionalConfectFiles[basename];\n\n if (pendingKey !== undefined) {\n return pipe(\n pendingRef,\n Ref.update((pending) => {\n const next = { ...pending, [pendingKey]: true };\n if (basename === \"nodeImpl.ts\") {\n return { ...next, specDirty: true };\n }\n return next;\n }),\n Effect.andThen(Queue.offer(signal, undefined)),\n Effect.andThen(\n basename === \"nodeSpec.ts\"\n ? Queue.offer(specWatcherRestartQueue, undefined)\n : Effect.void,\n ),\n );\n }\n return Effect.void;\n }),\n );\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"dev.mjs","names":["GroupPath.toString","FunctionPaths.make","FunctionPaths.diff","GroupPath.modulePath"],"sources":["../../src/confect/dev.ts"],"sourcesContent":["import { Spec } from \"@confect/core\";\nimport { Command } from \"@effect/cli\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Ansi, AnsiDoc } from \"@effect/printer-ansi\";\nimport {\n Array,\n Console,\n Deferred,\n Duration,\n Effect,\n Equal,\n HashSet,\n Match,\n Option,\n pipe,\n Queue,\n Ref,\n Schema,\n Stream,\n String,\n} from \"effect\";\nimport type { ReadonlyRecord } from \"effect/Record\";\nimport * as esbuild from \"esbuild\";\nimport type * as FunctionPath from \"../FunctionPath\";\nimport * as FunctionPaths from \"../FunctionPaths\";\nimport * as GroupPath from \"../GroupPath\";\nimport { logFailure, logPending, logSuccess } from \"../log\";\nimport { ConfectDirectory } from \"../services/ConfectDirectory\";\nimport { ConvexDirectory } from \"../services/ConvexDirectory\";\nimport { ProjectRoot } from \"../services/ProjectRoot\";\nimport {\n bundleAndImport,\n EXTERNAL_PACKAGES,\n generateAuthConfig,\n generateConvexConfig,\n generateCrons,\n generateHttp,\n removeGroups,\n writeGroups,\n} from \"../utils\";\nimport {\n codegenHandler,\n generateNodeApi,\n generateNodeRegisteredFunctions,\n} from \"./codegen\";\n\ntype Pending = {\n readonly specDirty: boolean;\n readonly nodeImplDirty: boolean;\n readonly httpDirty: boolean;\n readonly appDirty: boolean;\n readonly cronsDirty: boolean;\n readonly authDirty: boolean;\n};\n\nconst pendingInit: Pending = {\n specDirty: false,\n nodeImplDirty: false,\n httpDirty: false,\n appDirty: false,\n cronsDirty: false,\n authDirty: false,\n};\n\nconst changeChar = (change: \"Added\" | \"Removed\" | \"Modified\") =>\n Match.value(change).pipe(\n Match.when(\"Added\", () => ({ char: \"+\", color: Ansi.green })),\n Match.when(\"Removed\", () => ({ char: \"-\", color: Ansi.red })),\n Match.when(\"Modified\", () => ({ char: \"~\", color: Ansi.yellow })),\n Match.exhaustive,\n );\n\nconst logFileChangeIndented = (\n change: \"Added\" | \"Removed\" | \"Modified\",\n fullPath: string,\n) =>\n Effect.gen(function* () {\n const projectRoot = yield* ProjectRoot.get;\n const path = yield* Path.Path;\n\n const prefix = projectRoot + path.sep;\n const suffix = pipe(fullPath, String.startsWith(prefix))\n ? pipe(fullPath, String.slice(prefix.length))\n : fullPath;\n\n const { char, color } = changeChar(change);\n\n yield* Console.log(\n pipe(\n AnsiDoc.char(char),\n AnsiDoc.annotate(color),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(AnsiDoc.text(prefix), AnsiDoc.annotate(Ansi.blackBright)),\n pipe(AnsiDoc.text(suffix), AnsiDoc.annotate(color)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n });\n\nconst logFunctionAddedIndented = (functionPath: FunctionPath.FunctionPath) =>\n Console.log(\n pipe(\n AnsiDoc.text(\" \"),\n AnsiDoc.cat(pipe(AnsiDoc.char(\"+\"), AnsiDoc.annotate(Ansi.green))),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(\n AnsiDoc.text(GroupPath.toString(functionPath.groupPath) + \".\"),\n AnsiDoc.annotate(Ansi.blackBright),\n ),\n pipe(AnsiDoc.text(functionPath.name), AnsiDoc.annotate(Ansi.green)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n\nconst logFunctionRemovedIndented = (functionPath: FunctionPath.FunctionPath) =>\n Console.log(\n pipe(\n AnsiDoc.text(\" \"),\n AnsiDoc.cat(pipe(AnsiDoc.char(\"-\"), AnsiDoc.annotate(Ansi.red))),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(\n AnsiDoc.text(GroupPath.toString(functionPath.groupPath) + \".\"),\n AnsiDoc.annotate(Ansi.blackBright),\n ),\n pipe(AnsiDoc.text(functionPath.name), AnsiDoc.annotate(Ansi.red)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n\nexport const dev = Command.make(\"dev\", {}, () =>\n Effect.gen(function* () {\n yield* logPending(\"Performing initial sync…\");\n const initialFunctionPaths = yield* codegenHandler;\n\n const pendingRef = yield* Ref.make<Pending>(pendingInit);\n const signal = yield* Queue.sliding<void>(1);\n const specWatcherRestartQueue = yield* Queue.sliding<void>(1);\n\n yield* Effect.all(\n [\n specFileWatcher(signal, pendingRef, specWatcherRestartQueue),\n confectDirectoryWatcher(signal, pendingRef, specWatcherRestartQueue),\n syncLoop(signal, pendingRef, initialFunctionPaths),\n ],\n { concurrency: \"unbounded\" },\n );\n }),\n).pipe(Command.withDescription(\"Start the Confect development server\"));\n\nconst syncLoop = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n initialFunctionPaths: FunctionPaths.FunctionPaths,\n) =>\n Effect.gen(function* () {\n const functionPathsRef = yield* Ref.make(initialFunctionPaths);\n const initialSyncDone = yield* Deferred.make<void>();\n\n return yield* Effect.forever(\n Effect.gen(function* () {\n yield* Effect.logDebug(\"Running sync loop...\");\n yield* Queue.take(signal);\n\n const isDone = yield* Deferred.isDone(initialSyncDone);\n yield* Effect.when(\n logPending(\"Dependencies changed, reloading…\"),\n () => isDone,\n );\n yield* Deferred.succeed(initialSyncDone, undefined);\n\n const pending = yield* Ref.getAndSet(pendingRef, pendingInit);\n\n if (pending.specDirty || pending.nodeImplDirty) {\n yield* generateNodeApi;\n yield* generateNodeRegisteredFunctions;\n }\n\n const specResult: Option.Option<void> = yield* Effect.if(\n pending.specDirty,\n {\n onTrue: () =>\n loadSpec.pipe(\n Effect.andThen(\n Effect.fn(function* (spec) {\n yield* Effect.logDebug(\"Spec loaded\");\n\n const previous = yield* Ref.get(functionPathsRef);\n\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const current = FunctionPaths.make(spec);\n const {\n functionsAdded,\n functionsRemoved,\n groupsRemoved,\n groupsAdded,\n groupsChanged,\n } = FunctionPaths.diff(previous, current);\n\n // Removed groups\n yield* removeGroups(groupsRemoved);\n yield* Effect.forEach(groupsRemoved, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath =\n yield* GroupPath.modulePath(gp);\n const filePath = path.join(\n convexDirectory,\n relativeModulePath,\n );\n yield* logFileChangeIndented(\"Removed\", filePath);\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsRemoved, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionRemovedIndented,\n );\n }),\n );\n\n // Added groups\n yield* writeGroups(spec, groupsAdded);\n yield* Effect.forEach(groupsAdded, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath =\n yield* GroupPath.modulePath(gp);\n const filePath = path.join(\n convexDirectory,\n relativeModulePath,\n );\n yield* logFileChangeIndented(\"Added\", filePath);\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsAdded, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionAddedIndented,\n );\n }),\n );\n\n // Changed groups\n yield* writeGroups(spec, groupsChanged);\n yield* Effect.forEach(groupsChanged, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath =\n yield* GroupPath.modulePath(gp);\n const filePath = path.join(\n convexDirectory,\n relativeModulePath,\n );\n yield* logFileChangeIndented(\"Modified\", filePath);\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsAdded, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionAddedIndented,\n );\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsRemoved, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionRemovedIndented,\n );\n }),\n );\n\n yield* Ref.set(functionPathsRef, current);\n\n return Option.some(undefined);\n }),\n ),\n Effect.catchTag(\"SpecImportFailedError\", () =>\n logFailure(\"Spec import failed\").pipe(\n Effect.as(Option.none()),\n ),\n ),\n Effect.catchTag(\"SpecFileDoesNotExportSpecError\", () =>\n logFailure(\n \"Spec file does not default export a Convex spec\",\n ).pipe(Effect.as(Option.none())),\n ),\n Effect.catchTag(\"NodeSpecFileDoesNotExportSpecError\", () =>\n logFailure(\n \"Node spec file does not default export a Node spec\",\n ).pipe(Effect.as(Option.none())),\n ),\n ),\n onFalse: () => Effect.succeed(Option.some(undefined)),\n },\n );\n\n const dirtyOptionalFiles = [\n ...(pending.httpDirty\n ? [syncOptionalFile(generateHttp, \"http.ts\")]\n : []),\n ...(pending.appDirty\n ? [syncOptionalFile(generateConvexConfig, \"convex.config.ts\")]\n : []),\n ...(pending.cronsDirty\n ? [syncOptionalFile(generateCrons, \"crons.ts\")]\n : []),\n ...(pending.authDirty\n ? [syncOptionalFile(generateAuthConfig, \"auth.config.ts\")]\n : []),\n ];\n\n yield* Array.isNonEmptyReadonlyArray(dirtyOptionalFiles)\n ? Effect.all(dirtyOptionalFiles, { concurrency: \"unbounded\" })\n : Effect.void;\n\n yield* Option.match(specResult, {\n onSome: () => logSuccess(\"Generated files are up-to-date\"),\n onNone: () => Effect.void,\n });\n }),\n );\n });\n\nconst loadSpec = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const specPath = yield* getSpecPath;\n const specModule = yield* bundleAndImport(specPath).pipe(\n Effect.mapError((error) => new SpecImportFailedError({ error })),\n );\n const spec = specModule.default;\n\n if (!Spec.isConvexSpec(spec)) {\n return yield* new SpecFileDoesNotExportSpecError();\n }\n\n const nodeImplPath = path.join(confectDirectory, \"nodeImpl.ts\");\n const nodeImplExists = yield* fs.exists(nodeImplPath);\n const nodeSpecOption = yield* loadNodeSpec;\n const mergedSpec = Option.match(nodeSpecOption, {\n onNone: () => spec,\n onSome: (nodeSpec) => (nodeImplExists ? Spec.merge(spec, nodeSpec) : spec),\n });\n\n return mergedSpec;\n});\n\nconst getSpecPath = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n return path.join(confectDirectory, \"spec.ts\");\n});\n\nconst getNodeSpecPath = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n return path.join(confectDirectory, \"nodeSpec.ts\");\n});\n\nconst loadNodeSpec = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const nodeSpecPath = yield* getNodeSpecPath;\n\n if (!(yield* fs.exists(nodeSpecPath))) {\n return Option.none();\n }\n\n const nodeSpecModule = yield* bundleAndImport(nodeSpecPath).pipe(\n Effect.mapError((error) => new SpecImportFailedError({ error })),\n );\n const nodeSpec = nodeSpecModule.default;\n\n if (!Spec.isNodeSpec(nodeSpec)) {\n return yield* new NodeSpecFileDoesNotExportSpecError();\n }\n\n return Option.some(nodeSpec);\n});\n\nconst esbuildOptions = (entryPoint: string) => ({\n entryPoints: [entryPoint],\n bundle: true,\n write: false,\n metafile: true,\n platform: \"node\" as const,\n format: \"esm\" as const,\n logLevel: \"silent\" as const,\n external: EXTERNAL_PACKAGES,\n plugins: [\n {\n name: \"notify-rebuild\",\n setup(build: esbuild.PluginBuild) {\n build.onEnd((result) => {\n if (result.errors.length === 0) {\n (build as { _emit?: (v: void) => void })._emit?.();\n } else {\n Effect.runPromise(\n Effect.gen(function* () {\n const formattedMessages = yield* Effect.promise(() =>\n esbuild.formatMessages(result.errors, {\n kind: \"error\",\n color: true,\n terminalWidth: 80,\n }),\n );\n const output = formatBuildErrors(\n result.errors,\n formattedMessages,\n );\n yield* Console.error(\"\\n\" + output + \"\\n\");\n yield* logFailure(\"Build errors found\");\n }),\n );\n }\n });\n },\n },\n ],\n});\n\nconst createSpecWatcher = (entryPoint: string) =>\n Stream.asyncPush<void>(\n (emit) =>\n Effect.acquireRelease(\n Effect.promise(async () => {\n const opts = esbuildOptions(entryPoint);\n const plugin = opts.plugins[0];\n const originalSetup = plugin!.setup!;\n (plugin as { setup: (build: esbuild.PluginBuild) => void }).setup = (\n build,\n ) => {\n (build as { _emit?: (v: void) => void })._emit = () =>\n emit.single();\n return originalSetup(build);\n };\n\n const ctx = await esbuild.context({\n ...opts,\n plugins: [plugin],\n });\n\n await ctx.watch();\n return ctx;\n }),\n (ctx) =>\n Effect.promise(() => ctx.dispose()).pipe(\n Effect.tap(() => Effect.logDebug(\"esbuild watcher disposed\")),\n ),\n ),\n { bufferSize: 1, strategy: \"sliding\" },\n );\n\ntype SpecWatcherEvent = \"change\" | \"restart\";\n\nconst specFileWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n specWatcherRestartQueue: Queue.Queue<void>,\n) =>\n Effect.forever(\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const specPath = yield* getSpecPath;\n const nodeSpecPath = yield* getNodeSpecPath;\n const nodeSpecExists = yield* fs.exists(nodeSpecPath);\n\n const specWatcher = createSpecWatcher(specPath);\n const nodeSpecWatcher = nodeSpecExists\n ? createSpecWatcher(nodeSpecPath)\n : Stream.empty;\n\n const specChanges = pipe(\n Stream.merge(specWatcher, nodeSpecWatcher),\n Stream.map((): SpecWatcherEvent => \"change\"),\n );\n const restartStream = pipe(\n Stream.fromQueue(specWatcherRestartQueue),\n Stream.map((): SpecWatcherEvent => \"restart\"),\n );\n\n yield* pipe(\n Stream.merge(specChanges, restartStream),\n Stream.debounce(Duration.millis(200)),\n Stream.takeUntil((event): event is \"restart\" => event === \"restart\"),\n Stream.runForEach((event) =>\n event === \"change\"\n ? Ref.update(pendingRef, (pending) => ({\n ...pending,\n specDirty: true,\n })).pipe(Effect.andThen(Queue.offer(signal, undefined)))\n : Effect.void,\n ),\n );\n }),\n );\n\nconst formatBuildError = (\n error: esbuild.Message | undefined,\n formattedMessage: string,\n): string => {\n const lines = String.split(formattedMessage, \"\\n\");\n const redErrorText = pipe(\n AnsiDoc.text(error?.text ?? \"\"),\n AnsiDoc.annotate(Ansi.red),\n AnsiDoc.render({ style: \"pretty\" }),\n );\n const replaced = pipe(\n Array.findFirstIndex(lines, (l) => pipe(l, String.trim, String.isNonEmpty)),\n Option.match({\n onNone: () => lines,\n onSome: (index) => Array.modify(lines, index, () => redErrorText),\n }),\n );\n return pipe(replaced, Array.join(\"\\n\"));\n};\n\nconst formatBuildErrors = (\n errors: readonly esbuild.Message[],\n formattedMessages: readonly string[],\n): string =>\n pipe(\n formattedMessages,\n Array.map((message, i) => formatBuildError(errors[i], message)),\n Array.join(\"\"),\n String.trimEnd,\n );\n\nexport class SpecFileDoesNotExportSpecError extends Schema.TaggedError<SpecFileDoesNotExportSpecError>()(\n \"SpecFileDoesNotExportSpecError\",\n {},\n) {}\n\nexport class NodeSpecFileDoesNotExportSpecError extends Schema.TaggedError<NodeSpecFileDoesNotExportSpecError>()(\n \"NodeSpecFileDoesNotExportSpecError\",\n {},\n) {}\n\nexport class SpecImportFailedError extends Schema.TaggedError<SpecImportFailedError>()(\n \"SpecImportFailedError\",\n {\n error: Schema.Unknown,\n },\n) {}\n\nconst syncOptionalFile = (generate: typeof generateHttp, convexFile: string) =>\n pipe(\n generate,\n Effect.andThen(\n Option.match({\n onSome: ({ change, convexFilePath }) =>\n Match.value(change).pipe(\n Match.when(\"Unchanged\", () => Effect.void),\n Match.whenOr(\"Added\", \"Modified\", (addedOrModified) =>\n logFileChangeIndented(addedOrModified, convexFilePath),\n ),\n Match.exhaustive,\n ),\n onNone: () =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const convexFilePath = path.join(convexDirectory, convexFile);\n\n if (yield* fs.exists(convexFilePath)) {\n yield* fs.remove(convexFilePath);\n yield* logFileChangeIndented(\"Removed\", convexFilePath);\n }\n }),\n }),\n ),\n );\n\nconst optionalConfectFiles: ReadonlyRecord<string, keyof Pending> = {\n \"http.ts\": \"httpDirty\",\n \"app.ts\": \"appDirty\",\n \"crons.ts\": \"cronsDirty\",\n \"auth.ts\": \"authDirty\",\n \"nodeSpec.ts\": \"specDirty\",\n \"nodeImpl.ts\": \"nodeImplDirty\",\n};\n\nconst confectDirectoryWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n specWatcherRestartQueue: Queue.Queue<void>,\n) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n yield* pipe(\n fs.watch(confectDirectory),\n Stream.runForEach((event) => {\n const basename = path.basename(event.path);\n const pendingKey = optionalConfectFiles[basename];\n\n if (pendingKey !== undefined) {\n return pipe(\n pendingRef,\n Ref.update((pending) => {\n const next = { ...pending, [pendingKey]: true };\n if (basename === \"nodeImpl.ts\") {\n return { ...next, specDirty: true };\n }\n return next;\n }),\n Effect.andThen(Queue.offer(signal, undefined)),\n Effect.andThen(\n basename === \"nodeSpec.ts\"\n ? Queue.offer(specWatcherRestartQueue, undefined)\n : Effect.void,\n ),\n );\n }\n return Effect.void;\n }),\n );\n });\n"],"mappings":"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"}
package/dist/package.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "1.0.0";
2
+ var version = "1.0.1";
3
3
 
4
4
  //#endregion
5
5
  export { version };
package/dist/utils.mjs CHANGED
@@ -7,12 +7,54 @@ import { GroupPaths } from "./GroupPaths.mjs";
7
7
  import { groupPaths, make } from "./FunctionPaths.mjs";
8
8
  import { Array, Effect, HashSet, Option, Order, Record, String, pipe } from "effect";
9
9
  import { FileSystem, Path } from "@effect/platform";
10
+ import * as esbuild from "esbuild";
10
11
 
11
12
  //#region src/utils.ts
12
13
  const removePathExtension = (pathStr) => Effect.gen(function* () {
13
14
  const path = yield* Path.Path;
14
15
  return String.slice(0, -path.extname(pathStr).length)(pathStr);
15
16
  });
17
+ const EXTERNAL_PACKAGES = [
18
+ "@confect/core",
19
+ "@confect/server",
20
+ "effect",
21
+ "@effect/*"
22
+ ];
23
+ const isExternalImport = (path) => EXTERNAL_PACKAGES.some((p) => {
24
+ if (p.endsWith("/*")) return path.startsWith(p.slice(0, -1));
25
+ return path === p || path.startsWith(p + "/");
26
+ });
27
+ const absoluteExternalsPlugin = {
28
+ name: "absolute-externals",
29
+ setup(build) {
30
+ build.onResolve({ filter: /.*/ }, async (args) => {
31
+ if (args.kind !== "import-statement" && args.kind !== "dynamic-import") return;
32
+ if (!isExternalImport(args.path)) return;
33
+ return {
34
+ path: import.meta.resolve(args.path, "file://" + args.resolveDir + "/"),
35
+ external: true
36
+ };
37
+ });
38
+ }
39
+ };
40
+ /**
41
+ * Bundle a TypeScript entry point with esbuild and import the result via a
42
+ * data URL. This handles extensionless `.ts` imports regardless of whether the
43
+ * user's project sets `"type": "module"` in package.json.
44
+ */
45
+ const bundleAndImport = (entryPoint) => Effect.gen(function* () {
46
+ const code = (yield* Effect.promise(() => esbuild.build({
47
+ entryPoints: [entryPoint],
48
+ bundle: true,
49
+ write: false,
50
+ platform: "node",
51
+ format: "esm",
52
+ logLevel: "silent",
53
+ plugins: [absoluteExternalsPlugin]
54
+ }))).outputFiles[0].text;
55
+ const dataUrl = "data:text/javascript;base64," + Buffer.from(code).toString("base64");
56
+ return yield* Effect.promise(() => import(dataUrl));
57
+ });
16
58
  const writeFileStringAndLog = (filePath, contents) => Effect.gen(function* () {
17
59
  const fs = yield* FileSystem.FileSystem;
18
60
  if (!(yield* fs.exists(filePath))) {
@@ -164,5 +206,5 @@ const generateCrons = generateOptionalFile("crons.ts", "crons.ts", (importPath)
164
206
  const generateAuthConfig = generateOptionalFile("auth.ts", "auth.config.ts", (importPath) => authConfig({ authImportPath: importPath }));
165
207
 
166
208
  //#endregion
167
- export { generateAuthConfig, generateConvexConfig, generateCrons, generateFunctions, generateHttp, removeGroups, removePathExtension, writeFileStringAndLog, writeGroups };
209
+ export { EXTERNAL_PACKAGES, bundleAndImport, generateAuthConfig, generateConvexConfig, generateCrons, generateFunctions, generateHttp, removeGroups, removePathExtension, writeFileStringAndLog, writeGroups };
168
210
  //# sourceMappingURL=utils.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.mjs","names":["GroupPath.modulePath","modulePath","templates.functions","FunctionPaths.make","FunctionPaths.groupPaths","GroupPath.getGroupSpec","GroupPath.fromGroupModulePath","templates.http","templates.convexConfig","templates.crons","templates.authConfig"],"sources":["../src/utils.ts"],"sourcesContent":["import type { FunctionSpec, Spec } from \"@confect/core\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport type { PlatformError } from \"@effect/platform/Error\";\nimport {\n Array,\n Effect,\n HashSet,\n Option,\n Order,\n pipe,\n Record,\n String,\n} from \"effect\";\nimport * as FunctionPaths from \"./FunctionPaths\";\nimport * as GroupPath from \"./GroupPath\";\nimport * as GroupPaths from \"./GroupPaths\";\nimport { logFileAdded, logFileModified, logFileRemoved } from \"./log\";\nimport { ConfectDirectory } from \"./services/ConfectDirectory\";\nimport { ConvexDirectory } from \"./services/ConvexDirectory\";\nimport * as templates from \"./templates\";\n\nexport const removePathExtension = (pathStr: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n\n return String.slice(0, -path.extname(pathStr).length)(pathStr);\n });\n\nexport const writeFileStringAndLog = (filePath: string, contents: string) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n if (!(yield* fs.exists(filePath))) {\n yield* fs.writeFileString(filePath, contents);\n yield* logFileAdded(filePath);\n return;\n }\n const existing = yield* fs.readFileString(filePath);\n if (existing !== contents) {\n yield* fs.writeFileString(filePath, contents);\n yield* logFileModified(filePath);\n }\n });\n\nexport const findProjectRoot = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n const startDir = path.resolve(\".\");\n const root = path.parse(startDir).root;\n\n const directories = Array.unfold(startDir, (dir) =>\n dir === root\n ? Option.none()\n : Option.some([dir, path.dirname(dir)] as const),\n );\n\n const projectRoot = yield* Effect.findFirst(directories, (dir) =>\n fs.exists(path.join(dir, \"package.json\")),\n );\n\n return Option.getOrElse(projectRoot, () => startDir);\n});\n\nexport type WriteChange = \"Added\" | \"Modified\" | \"Unchanged\";\n\nexport const writeFileString = (\n filePath: string,\n contents: string,\n): Effect.Effect<WriteChange, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n\n if (!(yield* fs.exists(filePath))) {\n yield* fs.writeFileString(filePath, contents);\n return \"Added\";\n }\n const existing = yield* fs.readFileString(filePath);\n if (existing !== contents) {\n yield* fs.writeFileString(filePath, contents);\n return \"Modified\";\n }\n return \"Unchanged\";\n });\n\nexport const generateGroupModule = ({\n groupPath,\n functionNames,\n}: {\n groupPath: GroupPath.GroupPath;\n functionNames: string[];\n}) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n\n const directoryPath = path.dirname(modulePath);\n if (!(yield* fs.exists(directoryPath))) {\n yield* fs.makeDirectory(directoryPath, { recursive: true });\n }\n\n const isNodeGroup = groupPath.pathSegments[0] === \"node\";\n const registeredFunctionsFileName = isNodeGroup\n ? \"nodeRegisteredFunctions.ts\"\n : \"registeredFunctions.ts\";\n const registeredFunctionsPath = path.join(\n confectDirectory,\n \"_generated\",\n registeredFunctionsFileName,\n );\n const registeredFunctionsImportPath = yield* removePathExtension(\n path.relative(path.dirname(modulePath), registeredFunctionsPath),\n );\n const registeredFunctionsVariableName = isNodeGroup\n ? \"nodeRegisteredFunctions\"\n : \"registeredFunctions\";\n\n const functionsContentsString = yield* templates.functions({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n registeredFunctionsVariableName,\n useNode: isNodeGroup,\n ...(isNodeGroup\n ? {\n registeredFunctionsLookupPath: groupPath.pathSegments.slice(1),\n }\n : {}),\n });\n\n if (!(yield* fs.exists(modulePath))) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n return \"Added\" as const;\n }\n const existing = yield* fs.readFileString(modulePath);\n if (existing !== functionsContentsString) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n return \"Modified\" as const;\n }\n return \"Unchanged\" as const;\n });\n\nconst logGroupPaths = <R>(\n groupPaths: GroupPaths.GroupPaths,\n logFn: (fullPath: string) => Effect.Effect<void, never, R>,\n) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n yield* Effect.forEach(groupPaths, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath = yield* GroupPath.modulePath(gp);\n yield* logFn(path.join(convexDirectory, relativeModulePath));\n }),\n );\n });\n\nexport const generateFunctions = (spec: Spec.AnyWithProps) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const groupPathsFromFs = yield* getGroupPathsFromFs;\n const functionPaths = FunctionPaths.make(spec);\n const groupPathsFromSpec = FunctionPaths.groupPaths(functionPaths);\n\n const overlappingGroupPaths = GroupPaths.GroupPaths.make(\n HashSet.intersection(groupPathsFromFs, groupPathsFromSpec),\n );\n yield* Effect.forEach(overlappingGroupPaths, (groupPath) =>\n Effect.gen(function* () {\n const group = yield* GroupPath.getGroupSpec(spec, groupPath);\n const functionNames = pipe(\n group.functions,\n Record.values,\n Array.sortBy(\n Order.mapInput(\n Order.string,\n (fn: FunctionSpec.AnyWithProps) => fn.name,\n ),\n ),\n Array.map((fn) => fn.name),\n );\n const result = yield* generateGroupModule({ groupPath, functionNames });\n if (result === \"Modified\") {\n const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n yield* logFileModified(\n path.join(convexDirectory, relativeModulePath),\n );\n }\n }),\n );\n\n const extinctGroupPaths = GroupPaths.GroupPaths.make(\n HashSet.difference(groupPathsFromFs, groupPathsFromSpec),\n );\n yield* removeGroups(extinctGroupPaths);\n yield* logGroupPaths(extinctGroupPaths, logFileRemoved);\n\n const newGroupPaths = GroupPaths.GroupPaths.make(\n HashSet.difference(groupPathsFromSpec, groupPathsFromFs),\n );\n yield* writeGroups(spec, newGroupPaths);\n yield* logGroupPaths(newGroupPaths, logFileAdded);\n\n return functionPaths;\n });\n\nconst getGroupPathsFromFs = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const RESERVED_CONVEX_TS_FILE_NAMES = new Set([\n \"schema.ts\",\n \"http.ts\",\n \"crons.ts\",\n \"auth.config.ts\",\n \"convex.config.ts\",\n ]);\n\n const allConvexPaths = yield* fs.readDirectory(convexDirectory, {\n recursive: true,\n });\n const groupPathArray = yield* pipe(\n allConvexPaths,\n Array.filter(\n (convexPath) =>\n path.extname(convexPath) === \".ts\" &&\n !RESERVED_CONVEX_TS_FILE_NAMES.has(path.basename(convexPath)) &&\n path.basename(path.dirname(convexPath)) !== \"_generated\",\n ),\n Effect.forEach((groupModulePath) =>\n GroupPath.fromGroupModulePath(groupModulePath),\n ),\n );\n return pipe(groupPathArray, HashSet.fromIterable, GroupPaths.GroupPaths.make);\n});\n\nexport const removeGroups = (groupPaths: GroupPaths.GroupPaths) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n yield* Effect.all(\n HashSet.map(groupPaths, (groupPath) =>\n Effect.gen(function* () {\n const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n\n yield* Effect.logDebug(`Removing group '${relativeModulePath}'...`);\n\n yield* fs.remove(modulePath);\n yield* Effect.logDebug(`Group '${relativeModulePath}' removed`);\n }),\n ),\n { concurrency: \"unbounded\" },\n );\n });\n\nexport const writeGroups = (\n spec: Spec.AnyWithProps,\n groupPaths: GroupPaths.GroupPaths,\n) =>\n Effect.forEach(groupPaths, (groupPath) =>\n Effect.gen(function* () {\n const group = yield* GroupPath.getGroupSpec(spec, groupPath);\n\n const functionNames = pipe(\n group.functions,\n Record.values,\n Array.sortBy(\n Order.mapInput(\n Order.string,\n (fn: FunctionSpec.AnyWithProps) => fn.name,\n ),\n ),\n Array.map((fn) => fn.name),\n );\n\n yield* Effect.logDebug(`Generating group ${groupPath}...`);\n yield* generateGroupModule({\n groupPath,\n functionNames,\n });\n yield* Effect.logDebug(`Group ${groupPath} generated`);\n }),\n );\n\nconst generateOptionalFile = (\n confectFile: string,\n convexFile: string,\n generateContents: (importPath: string) => Effect.Effect<string>,\n) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const confectFilePath = path.join(confectDirectory, confectFile);\n\n if (!(yield* fs.exists(confectFilePath))) {\n return Option.none();\n }\n\n const convexFilePath = path.join(convexDirectory, convexFile);\n const relativeImportPath = path.relative(\n path.dirname(convexFilePath),\n confectFilePath,\n );\n const importPathWithoutExt = yield* removePathExtension(relativeImportPath);\n const contents = yield* generateContents(importPathWithoutExt);\n const change = yield* writeFileString(convexFilePath, contents);\n return Option.some({ change, convexFilePath });\n });\n\nexport const generateHttp = generateOptionalFile(\n \"http.ts\",\n \"http.ts\",\n (importPath) => templates.http({ httpImportPath: importPath }),\n);\n\nexport const generateConvexConfig = generateOptionalFile(\n \"app.ts\",\n \"convex.config.ts\",\n (importPath) => templates.convexConfig({ appImportPath: importPath }),\n);\n\nexport const generateCrons = generateOptionalFile(\n \"crons.ts\",\n \"crons.ts\",\n (importPath) => templates.crons({ cronsImportPath: importPath }),\n);\n\nexport const generateAuthConfig = generateOptionalFile(\n \"auth.ts\",\n \"auth.config.ts\",\n (importPath) => templates.authConfig({ authImportPath: importPath }),\n);\n"],"mappings":"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"}
1
+ {"version":3,"file":"utils.mjs","names":["GroupPath.modulePath","modulePath","templates.functions","FunctionPaths.make","FunctionPaths.groupPaths","GroupPath.getGroupSpec","GroupPath.fromGroupModulePath","templates.http","templates.convexConfig","templates.crons","templates.authConfig"],"sources":["../src/utils.ts"],"sourcesContent":["import type { FunctionSpec, Spec } from \"@confect/core\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport type { PlatformError } from \"@effect/platform/Error\";\nimport {\n Array,\n Effect,\n HashSet,\n Option,\n Order,\n pipe,\n Record,\n String,\n} from \"effect\";\nimport * as esbuild from \"esbuild\";\nimport * as FunctionPaths from \"./FunctionPaths\";\nimport * as GroupPath from \"./GroupPath\";\nimport * as GroupPaths from \"./GroupPaths\";\nimport { logFileAdded, logFileModified, logFileRemoved } from \"./log\";\nimport { ConfectDirectory } from \"./services/ConfectDirectory\";\nimport { ConvexDirectory } from \"./services/ConvexDirectory\";\nimport * as templates from \"./templates\";\n\nexport const removePathExtension = (pathStr: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n\n return String.slice(0, -path.extname(pathStr).length)(pathStr);\n });\n\nexport const EXTERNAL_PACKAGES = [\n \"@confect/core\",\n \"@confect/server\",\n \"effect\",\n \"@effect/*\",\n];\n\nconst isExternalImport = (path: string) =>\n EXTERNAL_PACKAGES.some((p) => {\n if (p.endsWith(\"/*\")) {\n return path.startsWith(p.slice(0, -1));\n }\n return path === p || path.startsWith(p + \"/\");\n });\n\nconst absoluteExternalsPlugin: esbuild.Plugin = {\n name: \"absolute-externals\",\n setup(build) {\n build.onResolve({ filter: /.*/ }, async (args) => {\n if (args.kind !== \"import-statement\" && args.kind !== \"dynamic-import\")\n return;\n if (!isExternalImport(args.path)) return;\n const resolved = import.meta.resolve(\n args.path,\n \"file://\" + args.resolveDir + \"/\",\n );\n return { path: resolved, external: true };\n });\n },\n};\n\n/**\n * Bundle a TypeScript entry point with esbuild and import the result via a\n * data URL. This handles extensionless `.ts` imports regardless of whether the\n * user's project sets `\"type\": \"module\"` in package.json.\n */\nexport const bundleAndImport = (entryPoint: string) =>\n Effect.gen(function* () {\n const result = yield* Effect.promise(() =>\n esbuild.build({\n entryPoints: [entryPoint],\n bundle: true,\n write: false,\n platform: \"node\",\n format: \"esm\",\n logLevel: \"silent\",\n plugins: [absoluteExternalsPlugin],\n }),\n );\n const code = result.outputFiles[0]!.text;\n const dataUrl =\n \"data:text/javascript;base64,\" + Buffer.from(code).toString(\"base64\");\n return yield* Effect.promise(() => import(dataUrl));\n });\n\nexport const writeFileStringAndLog = (filePath: string, contents: string) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n if (!(yield* fs.exists(filePath))) {\n yield* fs.writeFileString(filePath, contents);\n yield* logFileAdded(filePath);\n return;\n }\n const existing = yield* fs.readFileString(filePath);\n if (existing !== contents) {\n yield* fs.writeFileString(filePath, contents);\n yield* logFileModified(filePath);\n }\n });\n\nexport const findProjectRoot = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n const startDir = path.resolve(\".\");\n const root = path.parse(startDir).root;\n\n const directories = Array.unfold(startDir, (dir) =>\n dir === root\n ? Option.none()\n : Option.some([dir, path.dirname(dir)] as const),\n );\n\n const projectRoot = yield* Effect.findFirst(directories, (dir) =>\n fs.exists(path.join(dir, \"package.json\")),\n );\n\n return Option.getOrElse(projectRoot, () => startDir);\n});\n\nexport type WriteChange = \"Added\" | \"Modified\" | \"Unchanged\";\n\nexport const writeFileString = (\n filePath: string,\n contents: string,\n): Effect.Effect<WriteChange, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n\n if (!(yield* fs.exists(filePath))) {\n yield* fs.writeFileString(filePath, contents);\n return \"Added\";\n }\n const existing = yield* fs.readFileString(filePath);\n if (existing !== contents) {\n yield* fs.writeFileString(filePath, contents);\n return \"Modified\";\n }\n return \"Unchanged\";\n });\n\nexport const generateGroupModule = ({\n groupPath,\n functionNames,\n}: {\n groupPath: GroupPath.GroupPath;\n functionNames: string[];\n}) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n\n const directoryPath = path.dirname(modulePath);\n if (!(yield* fs.exists(directoryPath))) {\n yield* fs.makeDirectory(directoryPath, { recursive: true });\n }\n\n const isNodeGroup = groupPath.pathSegments[0] === \"node\";\n const registeredFunctionsFileName = isNodeGroup\n ? \"nodeRegisteredFunctions.ts\"\n : \"registeredFunctions.ts\";\n const registeredFunctionsPath = path.join(\n confectDirectory,\n \"_generated\",\n registeredFunctionsFileName,\n );\n const registeredFunctionsImportPath = yield* removePathExtension(\n path.relative(path.dirname(modulePath), registeredFunctionsPath),\n );\n const registeredFunctionsVariableName = isNodeGroup\n ? \"nodeRegisteredFunctions\"\n : \"registeredFunctions\";\n\n const functionsContentsString = yield* templates.functions({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n registeredFunctionsVariableName,\n useNode: isNodeGroup,\n ...(isNodeGroup\n ? {\n registeredFunctionsLookupPath: groupPath.pathSegments.slice(1),\n }\n : {}),\n });\n\n if (!(yield* fs.exists(modulePath))) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n return \"Added\" as const;\n }\n const existing = yield* fs.readFileString(modulePath);\n if (existing !== functionsContentsString) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n return \"Modified\" as const;\n }\n return \"Unchanged\" as const;\n });\n\nconst logGroupPaths = <R>(\n groupPaths: GroupPaths.GroupPaths,\n logFn: (fullPath: string) => Effect.Effect<void, never, R>,\n) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n yield* Effect.forEach(groupPaths, (gp) =>\n Effect.gen(function* () {\n const relativeModulePath = yield* GroupPath.modulePath(gp);\n yield* logFn(path.join(convexDirectory, relativeModulePath));\n }),\n );\n });\n\nexport const generateFunctions = (spec: Spec.AnyWithProps) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const groupPathsFromFs = yield* getGroupPathsFromFs;\n const functionPaths = FunctionPaths.make(spec);\n const groupPathsFromSpec = FunctionPaths.groupPaths(functionPaths);\n\n const overlappingGroupPaths = GroupPaths.GroupPaths.make(\n HashSet.intersection(groupPathsFromFs, groupPathsFromSpec),\n );\n yield* Effect.forEach(overlappingGroupPaths, (groupPath) =>\n Effect.gen(function* () {\n const group = yield* GroupPath.getGroupSpec(spec, groupPath);\n const functionNames = pipe(\n group.functions,\n Record.values,\n Array.sortBy(\n Order.mapInput(\n Order.string,\n (fn: FunctionSpec.AnyWithProps) => fn.name,\n ),\n ),\n Array.map((fn) => fn.name),\n );\n const result = yield* generateGroupModule({ groupPath, functionNames });\n if (result === \"Modified\") {\n const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n yield* logFileModified(\n path.join(convexDirectory, relativeModulePath),\n );\n }\n }),\n );\n\n const extinctGroupPaths = GroupPaths.GroupPaths.make(\n HashSet.difference(groupPathsFromFs, groupPathsFromSpec),\n );\n yield* removeGroups(extinctGroupPaths);\n yield* logGroupPaths(extinctGroupPaths, logFileRemoved);\n\n const newGroupPaths = GroupPaths.GroupPaths.make(\n HashSet.difference(groupPathsFromSpec, groupPathsFromFs),\n );\n yield* writeGroups(spec, newGroupPaths);\n yield* logGroupPaths(newGroupPaths, logFileAdded);\n\n return functionPaths;\n });\n\nconst getGroupPathsFromFs = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const RESERVED_CONVEX_TS_FILE_NAMES = new Set([\n \"schema.ts\",\n \"http.ts\",\n \"crons.ts\",\n \"auth.config.ts\",\n \"convex.config.ts\",\n ]);\n\n const allConvexPaths = yield* fs.readDirectory(convexDirectory, {\n recursive: true,\n });\n const groupPathArray = yield* pipe(\n allConvexPaths,\n Array.filter(\n (convexPath) =>\n path.extname(convexPath) === \".ts\" &&\n !RESERVED_CONVEX_TS_FILE_NAMES.has(path.basename(convexPath)) &&\n path.basename(path.dirname(convexPath)) !== \"_generated\",\n ),\n Effect.forEach((groupModulePath) =>\n GroupPath.fromGroupModulePath(groupModulePath),\n ),\n );\n return pipe(groupPathArray, HashSet.fromIterable, GroupPaths.GroupPaths.make);\n});\n\nexport const removeGroups = (groupPaths: GroupPaths.GroupPaths) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n\n yield* Effect.all(\n HashSet.map(groupPaths, (groupPath) =>\n Effect.gen(function* () {\n const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n\n yield* Effect.logDebug(`Removing group '${relativeModulePath}'...`);\n\n yield* fs.remove(modulePath);\n yield* Effect.logDebug(`Group '${relativeModulePath}' removed`);\n }),\n ),\n { concurrency: \"unbounded\" },\n );\n });\n\nexport const writeGroups = (\n spec: Spec.AnyWithProps,\n groupPaths: GroupPaths.GroupPaths,\n) =>\n Effect.forEach(groupPaths, (groupPath) =>\n Effect.gen(function* () {\n const group = yield* GroupPath.getGroupSpec(spec, groupPath);\n\n const functionNames = pipe(\n group.functions,\n Record.values,\n Array.sortBy(\n Order.mapInput(\n Order.string,\n (fn: FunctionSpec.AnyWithProps) => fn.name,\n ),\n ),\n Array.map((fn) => fn.name),\n );\n\n yield* Effect.logDebug(`Generating group ${groupPath}...`);\n yield* generateGroupModule({\n groupPath,\n functionNames,\n });\n yield* Effect.logDebug(`Group ${groupPath} generated`);\n }),\n );\n\nconst generateOptionalFile = (\n confectFile: string,\n convexFile: string,\n generateContents: (importPath: string) => Effect.Effect<string>,\n) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const confectFilePath = path.join(confectDirectory, confectFile);\n\n if (!(yield* fs.exists(confectFilePath))) {\n return Option.none();\n }\n\n const convexFilePath = path.join(convexDirectory, convexFile);\n const relativeImportPath = path.relative(\n path.dirname(convexFilePath),\n confectFilePath,\n );\n const importPathWithoutExt = yield* removePathExtension(relativeImportPath);\n const contents = yield* generateContents(importPathWithoutExt);\n const change = yield* writeFileString(convexFilePath, contents);\n return Option.some({ change, convexFilePath });\n });\n\nexport const generateHttp = generateOptionalFile(\n \"http.ts\",\n \"http.ts\",\n (importPath) => templates.http({ httpImportPath: importPath }),\n);\n\nexport const generateConvexConfig = generateOptionalFile(\n \"app.ts\",\n \"convex.config.ts\",\n (importPath) => templates.convexConfig({ appImportPath: importPath }),\n);\n\nexport const generateCrons = generateOptionalFile(\n \"crons.ts\",\n \"crons.ts\",\n (importPath) => templates.crons({ cronsImportPath: importPath }),\n);\n\nexport const generateAuthConfig = generateOptionalFile(\n \"auth.ts\",\n \"auth.config.ts\",\n (importPath) => templates.authConfig({ authImportPath: importPath }),\n);\n"],"mappings":";;;;;;;;;;;;AAsBA,MAAa,uBAAuB,YAClC,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;AAEzB,QAAO,OAAO,MAAM,GAAG,CAAC,KAAK,QAAQ,QAAQ,CAAC,OAAO,CAAC,QAAQ;EAC9D;AAEJ,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACD;AAED,MAAM,oBAAoB,SACxB,kBAAkB,MAAM,MAAM;AAC5B,KAAI,EAAE,SAAS,KAAK,CAClB,QAAO,KAAK,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC;AAExC,QAAO,SAAS,KAAK,KAAK,WAAW,IAAI,IAAI;EAC7C;AAEJ,MAAM,0BAA0C;CAC9C,MAAM;CACN,MAAM,OAAO;AACX,QAAM,UAAU,EAAE,QAAQ,MAAM,EAAE,OAAO,SAAS;AAChD,OAAI,KAAK,SAAS,sBAAsB,KAAK,SAAS,iBACpD;AACF,OAAI,CAAC,iBAAiB,KAAK,KAAK,CAAE;AAKlC,UAAO;IAAE,MAJQ,OAAO,KAAK,QAC3B,KAAK,MACL,YAAY,KAAK,aAAa,IAC/B;IACwB,UAAU;IAAM;IACzC;;CAEL;;;;;;AAOD,MAAa,mBAAmB,eAC9B,OAAO,IAAI,aAAa;CAYtB,MAAM,QAXS,OAAO,OAAO,cAC3B,QAAQ,MAAM;EACZ,aAAa,CAAC,WAAW;EACzB,QAAQ;EACR,OAAO;EACP,UAAU;EACV,QAAQ;EACR,UAAU;EACV,SAAS,CAAC,wBAAwB;EACnC,CAAC,CACH,EACmB,YAAY,GAAI;CACpC,MAAM,UACJ,iCAAiC,OAAO,KAAK,KAAK,CAAC,SAAS,SAAS;AACvE,QAAO,OAAO,OAAO,cAAc,OAAO,SAAS;EACnD;AAEJ,MAAa,yBAAyB,UAAkB,aACtD,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;AAC7B,KAAI,EAAE,OAAO,GAAG,OAAO,SAAS,GAAG;AACjC,SAAO,GAAG,gBAAgB,UAAU,SAAS;AAC7C,SAAO,aAAa,SAAS;AAC7B;;AAGF,MADiB,OAAO,GAAG,eAAe,SAAS,MAClC,UAAU;AACzB,SAAO,GAAG,gBAAgB,UAAU,SAAS;AAC7C,SAAO,gBAAgB,SAAS;;EAElC;AAEJ,MAAa,kBAAkB,OAAO,IAAI,aAAa;CACrD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CAEzB,MAAM,WAAW,KAAK,QAAQ,IAAI;CAClC,MAAM,OAAO,KAAK,MAAM,SAAS,CAAC;CAElC,MAAM,cAAc,MAAM,OAAO,WAAW,QAC1C,QAAQ,OACJ,OAAO,MAAM,GACb,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,IAAI,CAAC,CAAU,CACnD;CAED,MAAM,cAAc,OAAO,OAAO,UAAU,cAAc,QACxD,GAAG,OAAO,KAAK,KAAK,KAAK,eAAe,CAAC,CAC1C;AAED,QAAO,OAAO,UAAU,mBAAmB,SAAS;EACpD;AAIF,MAAa,mBACX,UACA,aAEA,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;AAE7B,KAAI,EAAE,OAAO,GAAG,OAAO,SAAS,GAAG;AACjC,SAAO,GAAG,gBAAgB,UAAU,SAAS;AAC7C,SAAO;;AAGT,MADiB,OAAO,GAAG,eAAe,SAAS,MAClC,UAAU;AACzB,SAAO,GAAG,gBAAgB,UAAU,SAAS;AAC7C,SAAO;;AAET,QAAO;EACP;AAEJ,MAAa,uBAAuB,EAClC,WACA,oBAKA,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;CAC/C,MAAM,mBAAmB,OAAO,iBAAiB;CAEjD,MAAM,qBAAqB,OAAOA,WAAqB,UAAU;CACjE,MAAMC,eAAa,KAAK,KAAK,iBAAiB,mBAAmB;CAEjE,MAAM,gBAAgB,KAAK,QAAQA,aAAW;AAC9C,KAAI,EAAE,OAAO,GAAG,OAAO,cAAc,EACnC,QAAO,GAAG,cAAc,eAAe,EAAE,WAAW,MAAM,CAAC;CAG7D,MAAM,cAAc,UAAU,aAAa,OAAO;CAClD,MAAM,8BAA8B,cAChC,+BACA;CACJ,MAAM,0BAA0B,KAAK,KACnC,kBACA,cACA,4BACD;CACD,MAAM,gCAAgC,OAAO,oBAC3C,KAAK,SAAS,KAAK,QAAQA,aAAW,EAAE,wBAAwB,CACjE;CACD,MAAM,kCAAkC,cACpC,4BACA;CAEJ,MAAM,0BAA0B,OAAOC,UAAoB;EACzD;EACA;EACA;EACA;EACA,SAAS;EACT,GAAI,cACA,EACE,+BAA+B,UAAU,aAAa,MAAM,EAAE,EAC/D,GACD,EAAE;EACP,CAAC;AAEF,KAAI,EAAE,OAAO,GAAG,OAAOD,aAAW,GAAG;AACnC,SAAO,GAAG,gBAAgBA,cAAY,wBAAwB;AAC9D,SAAO;;AAGT,MADiB,OAAO,GAAG,eAAeA,aAAW,MACpC,yBAAyB;AACxC,SAAO,GAAG,gBAAgBA,cAAY,wBAAwB;AAC9D,SAAO;;AAET,QAAO;EACP;AAEJ,MAAM,iBACJ,YACA,UAEA,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;AAE/C,QAAO,OAAO,QAAQ,aAAa,OACjC,OAAO,IAAI,aAAa;EACtB,MAAM,qBAAqB,OAAOD,WAAqB,GAAG;AAC1D,SAAO,MAAM,KAAK,KAAK,iBAAiB,mBAAmB,CAAC;GAC5D,CACH;EACD;AAEJ,MAAa,qBAAqB,SAChC,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;CAE/C,MAAM,mBAAmB,OAAO;CAChC,MAAM,gBAAgBG,KAAmB,KAAK;CAC9C,MAAM,qBAAqBC,WAAyB,cAAc;CAElE,MAAM,mCAA8C,KAClD,QAAQ,aAAa,kBAAkB,mBAAmB,CAC3D;AACD,QAAO,OAAO,QAAQ,wBAAwB,cAC5C,OAAO,IAAI,aAAa;AActB,OADe,OAAO,oBAAoB;GAAE;GAAW,eAXjC,MADR,OAAOC,aAAuB,MAAM,UAAU,EAEpD,WACN,OAAO,QACP,MAAM,OACJ,MAAM,SACJ,MAAM,SACL,OAAkC,GAAG,KACvC,CACF,EACD,MAAM,KAAK,OAAO,GAAG,KAAK,CAC3B;GACqE,CAAC,MACxD,YAAY;GACzB,MAAM,qBAAqB,OAAOL,WAAqB,UAAU;AACjE,UAAO,gBACL,KAAK,KAAK,iBAAiB,mBAAmB,CAC/C;;GAEH,CACH;CAED,MAAM,+BAA0C,KAC9C,QAAQ,WAAW,kBAAkB,mBAAmB,CACzD;AACD,QAAO,aAAa,kBAAkB;AACtC,QAAO,cAAc,mBAAmB,eAAe;CAEvD,MAAM,2BAAsC,KAC1C,QAAQ,WAAW,oBAAoB,iBAAiB,CACzD;AACD,QAAO,YAAY,MAAM,cAAc;AACvC,QAAO,cAAc,eAAe,aAAa;AAEjD,QAAO;EACP;AAEJ,MAAM,sBAAsB,OAAO,IAAI,aAAa;CAClD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;CAE/C,MAAM,gCAAgC,IAAI,IAAI;EAC5C;EACA;EACA;EACA;EACA;EACD,CAAC;AAiBF,QAAO,KAZgB,OAAO,KAHP,OAAO,GAAG,cAAc,iBAAiB,EAC9D,WAAW,MACZ,CAAC,EAGA,MAAM,QACH,eACC,KAAK,QAAQ,WAAW,KAAK,SAC7B,CAAC,8BAA8B,IAAI,KAAK,SAAS,WAAW,CAAC,IAC7D,KAAK,SAAS,KAAK,QAAQ,WAAW,CAAC,KAAK,aAC/C,EACD,OAAO,SAAS,oBACdM,oBAA8B,gBAAgB,CAC/C,CACF,EAC2B,QAAQ,yBAAoC,KAAK;EAC7E;AAEF,MAAa,gBAAgB,eAC3B,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;AAE/C,QAAO,OAAO,IACZ,QAAQ,IAAI,aAAa,cACvB,OAAO,IAAI,aAAa;EACtB,MAAM,qBAAqB,OAAON,WAAqB,UAAU;EACjE,MAAMC,eAAa,KAAK,KAAK,iBAAiB,mBAAmB;AAEjE,SAAO,OAAO,SAAS,mBAAmB,mBAAmB,MAAM;AAEnE,SAAO,GAAG,OAAOA,aAAW;AAC5B,SAAO,OAAO,SAAS,UAAU,mBAAmB,WAAW;GAC/D,CACH,EACD,EAAE,aAAa,aAAa,CAC7B;EACD;AAEJ,MAAa,eACX,MACA,eAEA,OAAO,QAAQ,aAAa,cAC1B,OAAO,IAAI,aAAa;CAGtB,MAAM,gBAAgB,MAFR,OAAOI,aAAuB,MAAM,UAAU,EAGpD,WACN,OAAO,QACP,MAAM,OACJ,MAAM,SACJ,MAAM,SACL,OAAkC,GAAG,KACvC,CACF,EACD,MAAM,KAAK,OAAO,GAAG,KAAK,CAC3B;AAED,QAAO,OAAO,SAAS,oBAAoB,UAAU,KAAK;AAC1D,QAAO,oBAAoB;EACzB;EACA;EACD,CAAC;AACF,QAAO,OAAO,SAAS,SAAS,UAAU,YAAY;EACtD,CACH;AAEH,MAAM,wBACJ,aACA,YACA,qBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CACjD,MAAM,kBAAkB,OAAO,gBAAgB;CAE/C,MAAM,kBAAkB,KAAK,KAAK,kBAAkB,YAAY;AAEhE,KAAI,EAAE,OAAO,GAAG,OAAO,gBAAgB,EACrC,QAAO,OAAO,MAAM;CAGtB,MAAM,iBAAiB,KAAK,KAAK,iBAAiB,WAAW;CAO7D,MAAM,SAAS,OAAO,gBAAgB,gBADrB,OAAO,iBADK,OAAO,oBAJT,KAAK,SAC9B,KAAK,QAAQ,eAAe,EAC5B,gBACD,CAC0E,CACb,CACC;AAC/D,QAAO,OAAO,KAAK;EAAE;EAAQ;EAAgB,CAAC;EAC9C;AAEJ,MAAa,eAAe,qBAC1B,WACA,YACC,eAAeE,KAAe,EAAE,gBAAgB,YAAY,CAAC,CAC/D;AAED,MAAa,uBAAuB,qBAClC,UACA,qBACC,eAAeC,aAAuB,EAAE,eAAe,YAAY,CAAC,CACtE;AAED,MAAa,gBAAgB,qBAC3B,YACA,aACC,eAAeC,MAAgB,EAAE,iBAAiB,YAAY,CAAC,CACjE;AAED,MAAa,qBAAqB,qBAChC,WACA,mBACC,eAAeC,WAAqB,EAAE,gBAAgB,YAAY,CAAC,CACrE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@confect/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Developer tooling for codegen and sync",
5
5
  "repository": {
6
6
  "type": "git",
@@ -44,8 +44,7 @@
44
44
  "@effect/printer": "^0.47.0",
45
45
  "@effect/printer-ansi": "^0.47.0",
46
46
  "code-block-writer": "^13.0.3",
47
- "esbuild": "^0.27.3",
48
- "tsx": "^4.21.0"
47
+ "esbuild": "^0.27.3"
49
48
  },
50
49
  "devDependencies": {
51
50
  "@effect/language-service": "0.73.1",
@@ -67,8 +66,8 @@
67
66
  "peerDependencies": {
68
67
  "@effect/platform": "^0.94.4",
69
68
  "effect": "^3.19.16",
70
- "@confect/core": "1.0.0",
71
- "@confect/server": "1.0.0"
69
+ "@confect/core": "1.0.1",
70
+ "@confect/server": "1.0.1"
72
71
  },
73
72
  "engines": {
74
73
  "node": ">=22",
@@ -3,7 +3,6 @@ import { DatabaseSchema } from "@confect/server";
3
3
  import { Command } from "@effect/cli";
4
4
  import { FileSystem, Path } from "@effect/platform";
5
5
  import { Effect, Match, Option } from "effect";
6
- import * as tsx from "tsx/esm/api";
7
6
  import {
8
7
  logFileAdded,
9
8
  logFileModified,
@@ -15,6 +14,7 @@ import { ConfectDirectory } from "../services/ConfectDirectory";
15
14
  import { ConvexDirectory } from "../services/ConvexDirectory";
16
15
  import * as templates from "../templates";
17
16
  import {
17
+ bundleAndImport,
18
18
  generateAuthConfig,
19
19
  generateConvexConfig,
20
20
  generateCrons,
@@ -32,17 +32,13 @@ const getNodeSpecPath = Effect.gen(function* () {
32
32
 
33
33
  const loadNodeSpec = Effect.gen(function* () {
34
34
  const fs = yield* FileSystem.FileSystem;
35
- const path = yield* Path.Path;
36
35
  const nodeSpecPath = yield* getNodeSpecPath;
37
36
 
38
37
  if (!(yield* fs.exists(nodeSpecPath))) {
39
38
  return Option.none<Spec.AnyWithPropsWithRuntime<"Node">>();
40
39
  }
41
40
 
42
- const nodeSpecPathUrl = yield* path.toFileUrl(nodeSpecPath);
43
- const nodeSpecModule = yield* Effect.promise(() =>
44
- tsx.tsImport(nodeSpecPathUrl.href, import.meta.url),
45
- );
41
+ const nodeSpecModule = yield* bundleAndImport(nodeSpecPath);
46
42
  const nodeSpec = nodeSpecModule.default;
47
43
 
48
44
  if (!Spec.isNodeSpec(nodeSpec)) {
@@ -167,11 +163,8 @@ const generateFunctionModules = Effect.gen(function* () {
167
163
  const confectDirectory = yield* ConfectDirectory.get;
168
164
 
169
165
  const specPath = path.join(confectDirectory, "spec.ts");
170
- const specPathUrl = yield* path.toFileUrl(specPath);
171
166
 
172
- const specModule = yield* Effect.promise(() =>
173
- tsx.tsImport(specPathUrl.href, import.meta.url),
174
- );
167
+ const specModule = yield* bundleAndImport(specPath);
175
168
  const spec = specModule.default;
176
169
 
177
170
  if (!Spec.isConvexSpec(spec)) {
@@ -196,11 +189,8 @@ const generateSchema = Effect.gen(function* () {
196
189
  const convexDirectory = yield* ConvexDirectory.get;
197
190
 
198
191
  const confectSchemaPath = path.join(confectDirectory, "schema.ts");
199
- const confectSchemaUrl = yield* path.toFileUrl(confectSchemaPath);
200
192
 
201
- yield* Effect.promise(() =>
202
- tsx.tsImport(confectSchemaUrl.href, import.meta.url),
203
- ).pipe(
193
+ yield* bundleAndImport(confectSchemaPath).pipe(
204
194
  Effect.andThen((schemaModule) => {
205
195
  const defaultExport = schemaModule.default;
206
196
 
@@ -21,7 +21,6 @@ import {
21
21
  } from "effect";
22
22
  import type { ReadonlyRecord } from "effect/Record";
23
23
  import * as esbuild from "esbuild";
24
- import * as tsx from "tsx/esm/api";
25
24
  import type * as FunctionPath from "../FunctionPath";
26
25
  import * as FunctionPaths from "../FunctionPaths";
27
26
  import * as GroupPath from "../GroupPath";
@@ -30,6 +29,8 @@ import { ConfectDirectory } from "../services/ConfectDirectory";
30
29
  import { ConvexDirectory } from "../services/ConvexDirectory";
31
30
  import { ProjectRoot } from "../services/ProjectRoot";
32
31
  import {
32
+ bundleAndImport,
33
+ EXTERNAL_PACKAGES,
33
34
  generateAuthConfig,
34
35
  generateConvexConfig,
35
36
  generateCrons,
@@ -336,11 +337,10 @@ const loadSpec = Effect.gen(function* () {
336
337
  const fs = yield* FileSystem.FileSystem;
337
338
  const path = yield* Path.Path;
338
339
  const confectDirectory = yield* ConfectDirectory.get;
339
- const specPathUrl = yield* path.toFileUrl(yield* getSpecPath);
340
- const specModule = yield* Effect.tryPromise({
341
- try: () => tsx.tsImport(specPathUrl.href, import.meta.url),
342
- catch: (error) => new SpecImportFailedError({ error }),
343
- });
340
+ const specPath = yield* getSpecPath;
341
+ const specModule = yield* bundleAndImport(specPath).pipe(
342
+ Effect.mapError((error) => new SpecImportFailedError({ error })),
343
+ );
344
344
  const spec = specModule.default;
345
345
 
346
346
  if (!Spec.isConvexSpec(spec)) {
@@ -374,18 +374,15 @@ const getNodeSpecPath = Effect.gen(function* () {
374
374
 
375
375
  const loadNodeSpec = Effect.gen(function* () {
376
376
  const fs = yield* FileSystem.FileSystem;
377
- const path = yield* Path.Path;
378
377
  const nodeSpecPath = yield* getNodeSpecPath;
379
378
 
380
379
  if (!(yield* fs.exists(nodeSpecPath))) {
381
380
  return Option.none();
382
381
  }
383
382
 
384
- const nodeSpecPathUrl = yield* path.toFileUrl(nodeSpecPath);
385
- const nodeSpecModule = yield* Effect.tryPromise({
386
- try: () => tsx.tsImport(nodeSpecPathUrl.href, import.meta.url),
387
- catch: (error) => new SpecImportFailedError({ error }),
388
- });
383
+ const nodeSpecModule = yield* bundleAndImport(nodeSpecPath).pipe(
384
+ Effect.mapError((error) => new SpecImportFailedError({ error })),
385
+ );
389
386
  const nodeSpec = nodeSpecModule.default;
390
387
 
391
388
  if (!Spec.isNodeSpec(nodeSpec)) {
@@ -403,7 +400,7 @@ const esbuildOptions = (entryPoint: string) => ({
403
400
  platform: "node" as const,
404
401
  format: "esm" as const,
405
402
  logLevel: "silent" as const,
406
- external: ["@confect/core", "@confect/server", "effect", "@effect/*"],
403
+ external: EXTERNAL_PACKAGES,
407
404
  plugins: [
408
405
  {
409
406
  name: "notify-rebuild",
package/src/utils.ts CHANGED
@@ -11,6 +11,7 @@ import {
11
11
  Record,
12
12
  String,
13
13
  } from "effect";
14
+ import * as esbuild from "esbuild";
14
15
  import * as FunctionPaths from "./FunctionPaths";
15
16
  import * as GroupPath from "./GroupPath";
16
17
  import * as GroupPaths from "./GroupPaths";
@@ -26,6 +27,61 @@ export const removePathExtension = (pathStr: string) =>
26
27
  return String.slice(0, -path.extname(pathStr).length)(pathStr);
27
28
  });
28
29
 
30
+ export const EXTERNAL_PACKAGES = [
31
+ "@confect/core",
32
+ "@confect/server",
33
+ "effect",
34
+ "@effect/*",
35
+ ];
36
+
37
+ const isExternalImport = (path: string) =>
38
+ EXTERNAL_PACKAGES.some((p) => {
39
+ if (p.endsWith("/*")) {
40
+ return path.startsWith(p.slice(0, -1));
41
+ }
42
+ return path === p || path.startsWith(p + "/");
43
+ });
44
+
45
+ const absoluteExternalsPlugin: esbuild.Plugin = {
46
+ name: "absolute-externals",
47
+ setup(build) {
48
+ build.onResolve({ filter: /.*/ }, async (args) => {
49
+ if (args.kind !== "import-statement" && args.kind !== "dynamic-import")
50
+ return;
51
+ if (!isExternalImport(args.path)) return;
52
+ const resolved = import.meta.resolve(
53
+ args.path,
54
+ "file://" + args.resolveDir + "/",
55
+ );
56
+ return { path: resolved, external: true };
57
+ });
58
+ },
59
+ };
60
+
61
+ /**
62
+ * Bundle a TypeScript entry point with esbuild and import the result via a
63
+ * data URL. This handles extensionless `.ts` imports regardless of whether the
64
+ * user's project sets `"type": "module"` in package.json.
65
+ */
66
+ export const bundleAndImport = (entryPoint: string) =>
67
+ Effect.gen(function* () {
68
+ const result = yield* Effect.promise(() =>
69
+ esbuild.build({
70
+ entryPoints: [entryPoint],
71
+ bundle: true,
72
+ write: false,
73
+ platform: "node",
74
+ format: "esm",
75
+ logLevel: "silent",
76
+ plugins: [absoluteExternalsPlugin],
77
+ }),
78
+ );
79
+ const code = result.outputFiles[0]!.text;
80
+ const dataUrl =
81
+ "data:text/javascript;base64," + Buffer.from(code).toString("base64");
82
+ return yield* Effect.promise(() => import(dataUrl));
83
+ });
84
+
29
85
  export const writeFileStringAndLog = (filePath: string, contents: string) =>
30
86
  Effect.gen(function* () {
31
87
  const fs = yield* FileSystem.FileSystem;