@confect/cli 9.0.0-next.7 → 9.0.0-next.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/dist/BuildError.mjs +9 -2
  3. package/dist/BuildError.mjs.map +1 -1
  4. package/dist/Bundler.mjs +5 -2
  5. package/dist/Bundler.mjs.map +1 -1
  6. package/dist/CodeBlockWriter.mjs +1 -1
  7. package/dist/CodeBlockWriter.mjs.map +1 -1
  8. package/dist/CodegenError.mjs +10 -15
  9. package/dist/CodegenError.mjs.map +1 -1
  10. package/dist/ConfectDirectory.mjs +5 -2
  11. package/dist/ConfectDirectory.mjs.map +1 -1
  12. package/dist/ConvexDirectory.mjs +6 -2
  13. package/dist/ConvexDirectory.mjs.map +1 -1
  14. package/dist/FunctionPath.mjs +1 -1
  15. package/dist/FunctionPath.mjs.map +1 -1
  16. package/dist/FunctionPaths.mjs +5 -1
  17. package/dist/FunctionPaths.mjs.map +1 -1
  18. package/dist/GroupPath.mjs +9 -2
  19. package/dist/GroupPath.mjs.map +1 -1
  20. package/dist/GroupPaths.mjs +1 -1
  21. package/dist/GroupPaths.mjs.map +1 -1
  22. package/dist/LeafModule.mjs +17 -24
  23. package/dist/LeafModule.mjs.map +1 -1
  24. package/dist/ProjectRoot.mjs +8 -3
  25. package/dist/ProjectRoot.mjs.map +1 -1
  26. package/dist/SpecAssemblyNode.mjs +6 -9
  27. package/dist/SpecAssemblyNode.mjs.map +1 -1
  28. package/dist/TableModule.mjs +6 -2
  29. package/dist/TableModule.mjs.map +1 -1
  30. package/dist/cliApp.mjs +1 -1
  31. package/dist/cliApp.mjs.map +1 -1
  32. package/dist/confect/codegen.mjs +36 -72
  33. package/dist/confect/codegen.mjs.map +1 -1
  34. package/dist/confect/dev.mjs +24 -7
  35. package/dist/confect/dev.mjs.map +1 -1
  36. package/dist/confect.mjs +2 -2
  37. package/dist/confect.mjs.map +1 -1
  38. package/dist/index.mjs +3 -2
  39. package/dist/index.mjs.map +1 -1
  40. package/dist/log.mjs +7 -3
  41. package/dist/log.mjs.map +1 -1
  42. package/dist/package.mjs +1 -1
  43. package/dist/templates.mjs +8 -15
  44. package/dist/templates.mjs.map +1 -1
  45. package/dist/utils.mjs +42 -22
  46. package/dist/utils.mjs.map +1 -1
  47. package/package.json +3 -3
  48. package/src/BuildError.ts +9 -2
  49. package/src/Bundler.ts +5 -2
  50. package/src/CodeBlockWriter.ts +1 -1
  51. package/src/CodegenError.ts +7 -38
  52. package/src/ConfectDirectory.ts +5 -2
  53. package/src/ConvexDirectory.ts +6 -2
  54. package/src/FunctionPath.ts +1 -1
  55. package/src/FunctionPaths.ts +5 -1
  56. package/src/GroupPath.ts +9 -11
  57. package/src/GroupPaths.ts +1 -1
  58. package/src/LeafModule.ts +24 -36
  59. package/src/ProjectRoot.ts +8 -3
  60. package/src/SpecAssemblyNode.ts +5 -14
  61. package/src/TableModule.ts +6 -2
  62. package/src/cliApp.ts +1 -1
  63. package/src/confect/codegen.ts +54 -108
  64. package/src/confect/dev.ts +24 -29
  65. package/src/confect.ts +2 -2
  66. package/src/index.ts +3 -2
  67. package/src/log.ts +7 -3
  68. package/src/templates.ts +11 -28
  69. package/src/utils.ts +47 -41
@@ -1,11 +1,17 @@
1
1
  import { fromBundlerError } from "./BuildError.mjs";
2
2
  import { bundle, directlyImports } from "./Bundler.mjs";
3
- import { ImplMissingDefaultLayerError, ImplMissingFunctionsError, ImplMissingSpecImportError, ImplNotFinalizedError, SpecMissingDefaultGroupSpecError, SpecRuntimeMismatchError } from "./CodegenError.mjs";
3
+ import { ImplMissingDefaultLayerError, ImplMissingFunctionsError, ImplMissingSpecImportError, ImplNotFinalizedError, SpecMissingDefaultGroupSpecError } from "./CodegenError.mjs";
4
4
  import { ConfectDirectory } from "./ConfectDirectory.mjs";
5
5
  import { removePathExtension } from "./utils.mjs";
6
- import { Array, Effect, Layer, Option, Ref, String } from "effect";
6
+ import * as Effect from "effect/Effect";
7
+ import * as Layer from "effect/Layer";
7
8
  import { GroupSpec, Registry } from "@confect/core";
8
- import { FileSystem, Path } from "@effect/platform";
9
+ import * as FileSystem from "@effect/platform/FileSystem";
10
+ import * as Path from "@effect/platform/Path";
11
+ import * as Array from "effect/Array";
12
+ import * as Option from "effect/Option";
13
+ import * as Ref from "effect/Ref";
14
+ import * as String from "effect/String";
9
15
  import * as GroupImpl from "@confect/server/GroupImpl";
10
16
 
11
17
  //#region src/LeafModule.ts
@@ -41,14 +47,8 @@ const specImportPathFromGenerated = (specRelativePath) => Effect.gen(function* (
41
47
  });
42
48
  const specPathForImpl = (implRelativePath) => swapModuleSuffix(implRelativePath, IMPL_SUFFIX, SPEC_SUFFIX);
43
49
  const implPathForSpec = (specRelativePath) => swapModuleSuffix(specRelativePath, SPEC_SUFFIX, IMPL_SUFFIX);
44
- const isNodeLeafModule = (relativePath) => relativePath.startsWith("node/") || relativePath.startsWith("node\\");
45
- const toNodeRegistryLeaf = (leaf) => ({
46
- ...leaf,
47
- pathSegments: [leaf.exportName],
48
- groupPathDot: leaf.exportName
49
- });
50
50
  const registeredFunctionsRelativePath = (leaf) => Effect.gen(function* () {
51
- return (yield* Path.Path).join("registeredFunctions", ...leaf.pathSegments.slice(leaf.runtime === "Node" ? 1 : 0)) + ".ts";
51
+ return (yield* Path.Path).join("registeredFunctions", ...leaf.pathSegments) + ".ts";
52
52
  });
53
53
  const discoverLeafSpecFiles = Effect.gen(function* () {
54
54
  const fs = yield* FileSystem.FileSystem;
@@ -80,14 +80,12 @@ const toLeafModule = (specRelativePath) => Effect.gen(function* () {
80
80
  const exportName = yield* exportNameFromModulePath(specRelativePath);
81
81
  const { pathSegments, groupPathDot } = yield* groupPathFromRelativeModulePath(specRelativePath);
82
82
  const specImportPath = yield* specImportPathFromGenerated(specRelativePath);
83
- const runtime = isNodeLeafModule(specRelativePath) ? "Node" : "Convex";
84
83
  return {
85
84
  relativePath: specRelativePath,
86
85
  pathSegments,
87
86
  groupPathDot,
88
87
  exportName,
89
- runtime,
90
- registryGroupPathDot: runtime === "Node" ? exportName : groupPathDot,
88
+ runtime: Option.none(),
91
89
  specImportPath
92
90
  };
93
91
  });
@@ -96,22 +94,17 @@ const absoluteModulePath = (relativePath) => Effect.gen(function* () {
96
94
  return (yield* Path.Path).resolve(confectDirectory, relativePath);
97
95
  });
98
96
  /**
99
- * Validate that the leaf's spec file default-exports a `GroupSpec` whose
100
- * runtime matches the leaf's location (`Convex` for files outside
101
- * `confect/node/`, `Node` for files inside it). Returns the validated
102
- * `GroupSpec` so callers can avoid re-bundling for later inspection (e.g.
103
- * parent/child name-collision checks at codegen time).
97
+ * Validate that the leaf's spec file default-exports a `GroupSpec`. Returns the
98
+ * validated `GroupSpec` so callers can read its runtime and avoid re-bundling for
99
+ * later inspection (e.g. stamping `leaf.runtime` and parent/child name-collision
100
+ * checks at codegen time). The group's runtime (`Convex` vs `Node`) is whatever
101
+ * the spec declares it is not constrained by the file's location.
104
102
  */
105
103
  const validateSpec = (leaf) => Effect.gen(function* () {
106
104
  const absolutePath = yield* absoluteModulePath(leaf.relativePath);
107
105
  const { module } = yield* bundle(absolutePath).pipe(Effect.mapError((error) => fromBundlerError(leaf.relativePath, error)));
108
106
  const groupSpec = module.default;
109
107
  if (!GroupSpec.isGroupSpec(groupSpec)) return yield* new SpecMissingDefaultGroupSpecError({ specPath: leaf.relativePath });
110
- if (groupSpec.runtime !== leaf.runtime) return yield* new SpecRuntimeMismatchError({
111
- specPath: leaf.relativePath,
112
- expectedRuntime: leaf.runtime,
113
- actualRuntime: groupSpec.runtime
114
- });
115
108
  return groupSpec;
116
109
  });
117
110
  /**
@@ -166,5 +159,5 @@ const validateImpl = (leaf) => Effect.gen(function* () {
166
159
  });
167
160
 
168
161
  //#endregion
169
- export { discoverLeafImplFiles, discoverLeafSpecFiles, implPathForSpec, isLeafImplPath, isLeafSpecPath, registeredFunctionsRelativePath, specPathForImpl, toLeafModule, toNodeRegistryLeaf, validateImpl, validateSpec };
162
+ export { discoverLeafImplFiles, discoverLeafSpecFiles, implPathForSpec, isLeafImplPath, isLeafSpecPath, registeredFunctionsRelativePath, specPathForImpl, toLeafModule, validateImpl, validateSpec };
170
163
  //# sourceMappingURL=LeafModule.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"LeafModule.mjs","names":["Bundler.bundle","Bundler.directlyImports"],"sources":["../src/LeafModule.ts"],"sourcesContent":["import { GroupSpec, Registry } from \"@confect/core\";\nimport * as GroupImpl from \"@confect/server/GroupImpl\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport type { Context } from \"effect\";\nimport { Array, Effect, Layer, Option, Ref, String } from \"effect\";\nimport { fromBundlerError } from \"./BuildError\";\nimport * as Bundler from \"./Bundler\";\nimport {\n ImplMissingDefaultLayerError,\n ImplMissingFunctionsError,\n ImplMissingSpecImportError,\n ImplNotFinalizedError,\n SpecMissingDefaultGroupSpecError,\n SpecRuntimeMismatchError,\n} from \"./CodegenError\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\nimport { removePathExtension } from \"./utils\";\n\nexport interface LeafModule {\n readonly relativePath: string;\n readonly pathSegments: readonly [string, ...string[]];\n readonly groupPathDot: string;\n readonly registryGroupPathDot: string;\n readonly exportName: string;\n readonly runtime: \"Convex\" | \"Node\";\n readonly specImportPath: string;\n}\n\nexport const SPEC_SUFFIX = \".spec.ts\";\nexport const IMPL_SUFFIX = \".impl.ts\";\n\nconst swapModuleSuffix = (\n relativePath: string,\n fromSuffix: string,\n toSuffix: string,\n) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { dir, name, ext } = path.parse(relativePath);\n if (ext !== \".ts\" || !name.endsWith(fromSuffix.slice(0, -\".ts\".length))) {\n return relativePath;\n }\n\n const stem = name.slice(0, -fromSuffix.slice(0, -\".ts\".length).length);\n const nextName = `${stem}${toSuffix.slice(0, -\".ts\".length)}`;\n return dir.length > 0\n ? path.join(dir, `${nextName}${ext}`)\n : `${nextName}${ext}`;\n });\n\nexport const isLeafSpecPath = (relativePath: string) =>\n relativePath.endsWith(SPEC_SUFFIX);\n\nexport const isLeafImplPath = (relativePath: string) =>\n relativePath.endsWith(IMPL_SUFFIX);\n\nexport const exportNameFromModulePath = (relativePath: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { name, ext } = path.parse(relativePath);\n if (ext !== \".ts\") {\n return name;\n }\n return name.endsWith(\".spec\") ? name.slice(0, -\".spec\".length) : name;\n });\n\nexport const groupPathFromRelativeModulePath = (relativePath: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { dir, name, ext } = path.parse(relativePath);\n const stem =\n ext === \".ts\" && name.endsWith(\".spec\")\n ? name.slice(0, -\".spec\".length)\n : name;\n const dirSegments = Array.filter(\n String.split(dir, path.sep),\n String.isNonEmpty,\n );\n const pathSegments = Array.append(dirSegments, stem) as [\n string,\n ...string[],\n ];\n return {\n pathSegments,\n groupPathDot: Array.join(pathSegments, \".\"),\n };\n });\n\nexport const specImportPathFromGenerated = (specRelativePath: string) =>\n Effect.gen(function* () {\n const withoutExt = yield* removePathExtension(specRelativePath);\n return `../${withoutExt}`;\n });\n\nexport const specPathForImpl = (implRelativePath: string) =>\n swapModuleSuffix(implRelativePath, IMPL_SUFFIX, SPEC_SUFFIX);\n\nexport const implPathForSpec = (specRelativePath: string) =>\n swapModuleSuffix(specRelativePath, SPEC_SUFFIX, IMPL_SUFFIX);\n\nexport const isNodeLeafModule = (relativePath: string) =>\n relativePath.startsWith(\"node/\") || relativePath.startsWith(\"node\\\\\");\n\nexport const toNodeRegistryLeaf = (leaf: LeafModule): LeafModule => ({\n ...leaf,\n pathSegments: [leaf.exportName],\n groupPathDot: leaf.exportName,\n});\n\nexport const registeredFunctionsRelativePath = (leaf: LeafModule) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n return (\n path.join(\n \"registeredFunctions\",\n ...leaf.pathSegments.slice(leaf.runtime === \"Node\" ? 1 : 0),\n ) + \".ts\"\n );\n });\n\nexport const discoverLeafSpecFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const excludedDirs = new Set([\"_generated\", \"tables\"]);\n const excludedFiles = new Set([\"nodeSpec.ts\", \"spec.ts\"]);\n\n const allPaths = yield* fs.readDirectory(confectDirectory, {\n recursive: true,\n });\n\n return Array.filter(allPaths, (relativePath) => {\n if (!isLeafSpecPath(relativePath)) {\n return false;\n }\n\n if (excludedFiles.has(relativePath)) {\n return false;\n }\n\n const segments = String.split(relativePath, path.sep);\n return !Array.some(segments, (segment) => excludedDirs.has(segment));\n });\n});\n\nexport const discoverLeafImplFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const excludedDirs = new Set([\"_generated\", \"tables\"]);\n\n const allPaths = yield* fs.readDirectory(confectDirectory, {\n recursive: true,\n });\n\n return Array.filter(allPaths, (relativePath) => {\n if (!isLeafImplPath(relativePath)) {\n return false;\n }\n\n const segments = String.split(relativePath, path.sep);\n return !Array.some(segments, (segment) => excludedDirs.has(segment));\n });\n});\n\nexport const toLeafModule = (specRelativePath: string) =>\n Effect.gen(function* () {\n const exportName = yield* exportNameFromModulePath(specRelativePath);\n const { pathSegments, groupPathDot } =\n yield* groupPathFromRelativeModulePath(specRelativePath);\n const specImportPath = yield* specImportPathFromGenerated(specRelativePath);\n const runtime = isNodeLeafModule(specRelativePath) ? \"Node\" : \"Convex\";\n\n return {\n relativePath: specRelativePath,\n pathSegments,\n groupPathDot,\n exportName,\n runtime,\n registryGroupPathDot: runtime === \"Node\" ? exportName : groupPathDot,\n specImportPath,\n } satisfies LeafModule;\n });\n\nconst absoluteModulePath = (relativePath: string) =>\n Effect.gen(function* () {\n const confectDirectory = yield* ConfectDirectory.get;\n const path = yield* Path.Path;\n return path.resolve(confectDirectory, relativePath);\n });\n\n/**\n * Validate that the leaf's spec file default-exports a `GroupSpec` whose\n * runtime matches the leaf's location (`Convex` for files outside\n * `confect/node/`, `Node` for files inside it). Returns the validated\n * `GroupSpec` so callers can avoid re-bundling for later inspection (e.g.\n * parent/child name-collision checks at codegen time).\n */\nexport const validateSpec = (leaf: LeafModule) =>\n Effect.gen(function* () {\n const absolutePath = yield* absoluteModulePath(leaf.relativePath);\n const { module } = yield* Bundler.bundle(absolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(leaf.relativePath, error)),\n );\n\n const groupSpec = module.default;\n\n if (!GroupSpec.isGroupSpec(groupSpec)) {\n return yield* new SpecMissingDefaultGroupSpecError({\n specPath: leaf.relativePath,\n });\n }\n\n if (groupSpec.runtime !== leaf.runtime) {\n return yield* new SpecRuntimeMismatchError({\n specPath: leaf.relativePath,\n expectedRuntime: leaf.runtime,\n actualRuntime: groupSpec.runtime,\n });\n }\n\n return groupSpec;\n });\n\n/**\n * Walk the built `Context` for a `Finalized` `GroupImpl` service value. The\n * lookup is value-shaped (via `GroupImpl.isFinalizedGroupImpl`) so we don't\n * need to know the group's path up front to construct a typed tag for it.\n */\nconst findFinalizedGroupImpl = <S>(\n context: Context.Context<S>,\n): Option.Option<GroupImpl.AnyFinalized> =>\n Array.findFirst(context.unsafeMap.values(), GroupImpl.isFinalizedGroupImpl);\n\n/**\n * Build the impl layer with a fresh `Registry` so each validation is\n * isolated from prior validations' `FunctionImpl.make` writes. The CLI no\n * longer reads the registry directly — `GroupImpl.finalize` snapshots the\n * registered function names onto the produced `Finalized` `GroupImpl`\n * service value — but a fresh `Ref` is still required because the default\n * `Context.Reference` is cached globally and would otherwise accumulate\n * items across impls.\n */\nconst buildImplLayer = (implLayer: Layer.Layer<unknown>) =>\n Effect.gen(function* () {\n const registry = Ref.unsafeMake<Registry.RegistryItems>({});\n return yield* Layer.build(\n implLayer as Layer.Layer<unknown, never, never>,\n ).pipe(Effect.provideService(Registry.Registry, registry));\n }).pipe(Effect.scoped);\n\n/**\n * Validate that the leaf's sibling impl file imports the spec, default-exports\n * a finalized `GroupImpl` layer, and provides a `FunctionImpl` for every\n * function declared by the spec.\n */\nexport const validateImpl = (leaf: LeafModule) =>\n Effect.gen(function* () {\n const implRelativePath = yield* implPathForSpec(leaf.relativePath);\n const implAbsolutePath = yield* absoluteModulePath(implRelativePath);\n const specAbsolutePath = yield* absoluteModulePath(leaf.relativePath);\n\n const bundled = yield* Bundler.bundle(implAbsolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(implRelativePath, error)),\n );\n\n if (\n !(yield* Bundler.directlyImports(\n bundled,\n implAbsolutePath,\n specAbsolutePath,\n ))\n ) {\n return yield* new ImplMissingSpecImportError({\n implPath: implRelativePath,\n expectedSpecPath: leaf.relativePath,\n });\n }\n\n if (!Layer.isLayer(bundled.module.default)) {\n return yield* new ImplMissingDefaultLayerError({\n implPath: implRelativePath,\n });\n }\n\n const { module: specModule } = yield* Bundler.bundle(specAbsolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(leaf.relativePath, error)),\n );\n const groupSpec = specModule.default as GroupSpec.AnyWithProps;\n const expectedFunctionNames = Object.keys(groupSpec.functions);\n\n const context = yield* buildImplLayer(\n bundled.module.default as Layer.Layer<unknown>,\n );\n const finalizedGroupImpl = yield* Option.match(\n findFinalizedGroupImpl(context),\n {\n onNone: () => new ImplNotFinalizedError({ implPath: implRelativePath }),\n onSome: Effect.succeed,\n },\n );\n\n const registeredSet = new Set(finalizedGroupImpl.registeredFunctionNames);\n const missing = expectedFunctionNames.filter(\n (name) => !registeredSet.has(name),\n );\n\n if (missing.length > 0) {\n return yield* new ImplMissingFunctionsError({\n implPath: implRelativePath,\n groupPath: leaf.groupPathDot,\n missingFunctionNames: missing,\n });\n }\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"LeafModule.mjs","names":["Bundler.bundle","Bundler.directlyImports"],"sources":["../src/LeafModule.ts"],"sourcesContent":["import { GroupSpec, Registry } from \"@confect/core\";\nimport * as GroupImpl from \"@confect/server/GroupImpl\";\nimport * as FileSystem from \"@effect/platform/FileSystem\";\nimport * as Path from \"@effect/platform/Path\";\nimport type { Context } from \"effect\";\nimport * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Option from \"effect/Option\";\nimport * as Ref from \"effect/Ref\";\nimport * as String from \"effect/String\";\nimport { fromBundlerError } from \"./BuildError\";\nimport * as Bundler from \"./Bundler\";\nimport {\n ImplMissingDefaultLayerError,\n ImplMissingFunctionsError,\n ImplMissingSpecImportError,\n ImplNotFinalizedError,\n SpecMissingDefaultGroupSpecError,\n} from \"./CodegenError\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\nimport { removePathExtension } from \"./utils\";\n\nexport interface LeafModule {\n readonly relativePath: string;\n readonly pathSegments: readonly [string, ...string[]];\n readonly groupPathDot: string;\n readonly exportName: string;\n /**\n * The runtime declared by the group's spec — `\"Node\"` for\n * `GroupSpec.makeNode()`, `\"Convex\"` for `GroupSpec.make()`. `None` while the\n * runtime is unknown: discovery (`toLeafModule`) works from the file path alone,\n * which does not determine the runtime, so this is filled in once the spec has\n * been bundled and validated (see `validateSpec`).\n */\n readonly runtime: Option.Option<\"Convex\" | \"Node\">;\n readonly specImportPath: string;\n}\n\nexport const SPEC_SUFFIX = \".spec.ts\";\nexport const IMPL_SUFFIX = \".impl.ts\";\n\nconst swapModuleSuffix = (\n relativePath: string,\n fromSuffix: string,\n toSuffix: string,\n) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { dir, name, ext } = path.parse(relativePath);\n if (ext !== \".ts\" || !name.endsWith(fromSuffix.slice(0, -\".ts\".length))) {\n return relativePath;\n }\n\n const stem = name.slice(0, -fromSuffix.slice(0, -\".ts\".length).length);\n const nextName = `${stem}${toSuffix.slice(0, -\".ts\".length)}`;\n return dir.length > 0\n ? path.join(dir, `${nextName}${ext}`)\n : `${nextName}${ext}`;\n });\n\nexport const isLeafSpecPath = (relativePath: string) =>\n relativePath.endsWith(SPEC_SUFFIX);\n\nexport const isLeafImplPath = (relativePath: string) =>\n relativePath.endsWith(IMPL_SUFFIX);\n\nexport const exportNameFromModulePath = (relativePath: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { name, ext } = path.parse(relativePath);\n if (ext !== \".ts\") {\n return name;\n }\n return name.endsWith(\".spec\") ? name.slice(0, -\".spec\".length) : name;\n });\n\nexport const groupPathFromRelativeModulePath = (relativePath: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { dir, name, ext } = path.parse(relativePath);\n const stem =\n ext === \".ts\" && name.endsWith(\".spec\")\n ? name.slice(0, -\".spec\".length)\n : name;\n const dirSegments = Array.filter(\n String.split(dir, path.sep),\n String.isNonEmpty,\n );\n const pathSegments = Array.append(dirSegments, stem) as [\n string,\n ...string[],\n ];\n return {\n pathSegments,\n groupPathDot: Array.join(pathSegments, \".\"),\n };\n });\n\nexport const specImportPathFromGenerated = (specRelativePath: string) =>\n Effect.gen(function* () {\n const withoutExt = yield* removePathExtension(specRelativePath);\n return `../${withoutExt}`;\n });\n\nexport const specPathForImpl = (implRelativePath: string) =>\n swapModuleSuffix(implRelativePath, IMPL_SUFFIX, SPEC_SUFFIX);\n\nexport const implPathForSpec = (specRelativePath: string) =>\n swapModuleSuffix(specRelativePath, SPEC_SUFFIX, IMPL_SUFFIX);\n\nexport const registeredFunctionsRelativePath = (leaf: LeafModule) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n return path.join(\"registeredFunctions\", ...leaf.pathSegments) + \".ts\";\n });\n\nexport const discoverLeafSpecFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const excludedDirs = new Set([\"_generated\", \"tables\"]);\n const excludedFiles = new Set([\"nodeSpec.ts\", \"spec.ts\"]);\n\n const allPaths = yield* fs.readDirectory(confectDirectory, {\n recursive: true,\n });\n\n return Array.filter(allPaths, (relativePath) => {\n if (!isLeafSpecPath(relativePath)) {\n return false;\n }\n\n if (excludedFiles.has(relativePath)) {\n return false;\n }\n\n const segments = String.split(relativePath, path.sep);\n return !Array.some(segments, (segment) => excludedDirs.has(segment));\n });\n});\n\nexport const discoverLeafImplFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const excludedDirs = new Set([\"_generated\", \"tables\"]);\n\n const allPaths = yield* fs.readDirectory(confectDirectory, {\n recursive: true,\n });\n\n return Array.filter(allPaths, (relativePath) => {\n if (!isLeafImplPath(relativePath)) {\n return false;\n }\n\n const segments = String.split(relativePath, path.sep);\n return !Array.some(segments, (segment) => excludedDirs.has(segment));\n });\n});\n\nexport const toLeafModule = (specRelativePath: string) =>\n Effect.gen(function* () {\n const exportName = yield* exportNameFromModulePath(specRelativePath);\n const { pathSegments, groupPathDot } =\n yield* groupPathFromRelativeModulePath(specRelativePath);\n const specImportPath = yield* specImportPathFromGenerated(specRelativePath);\n\n return {\n relativePath: specRelativePath,\n pathSegments,\n groupPathDot,\n exportName,\n // Unknown until the spec is bundled; see `LeafModule.runtime`.\n runtime: Option.none(),\n specImportPath,\n } satisfies LeafModule;\n });\n\nconst absoluteModulePath = (relativePath: string) =>\n Effect.gen(function* () {\n const confectDirectory = yield* ConfectDirectory.get;\n const path = yield* Path.Path;\n return path.resolve(confectDirectory, relativePath);\n });\n\n/**\n * Validate that the leaf's spec file default-exports a `GroupSpec`. Returns the\n * validated `GroupSpec` so callers can read its runtime and avoid re-bundling for\n * later inspection (e.g. stamping `leaf.runtime` and parent/child name-collision\n * checks at codegen time). The group's runtime (`Convex` vs `Node`) is whatever\n * the spec declares — it is not constrained by the file's location.\n */\nexport const validateSpec = (leaf: LeafModule) =>\n Effect.gen(function* () {\n const absolutePath = yield* absoluteModulePath(leaf.relativePath);\n const { module } = yield* Bundler.bundle(absolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(leaf.relativePath, error)),\n );\n\n const groupSpec = module.default;\n\n if (!GroupSpec.isGroupSpec(groupSpec)) {\n return yield* new SpecMissingDefaultGroupSpecError({\n specPath: leaf.relativePath,\n });\n }\n\n return groupSpec;\n });\n\n/**\n * Walk the built `Context` for a `Finalized` `GroupImpl` service value. The\n * lookup is value-shaped (via `GroupImpl.isFinalizedGroupImpl`) so we don't\n * need to know the group's path up front to construct a typed tag for it.\n */\nconst findFinalizedGroupImpl = <S>(\n context: Context.Context<S>,\n): Option.Option<GroupImpl.AnyFinalized> =>\n Array.findFirst(context.unsafeMap.values(), GroupImpl.isFinalizedGroupImpl);\n\n/**\n * Build the impl layer with a fresh `Registry` so each validation is\n * isolated from prior validations' `FunctionImpl.make` writes. The CLI no\n * longer reads the registry directly — `GroupImpl.finalize` snapshots the\n * registered function names onto the produced `Finalized` `GroupImpl`\n * service value — but a fresh `Ref` is still required because the default\n * `Context.Reference` is cached globally and would otherwise accumulate\n * items across impls.\n */\nconst buildImplLayer = (implLayer: Layer.Layer<unknown>) =>\n Effect.gen(function* () {\n const registry = Ref.unsafeMake<Registry.RegistryItems>({});\n return yield* Layer.build(\n implLayer as Layer.Layer<unknown, never, never>,\n ).pipe(Effect.provideService(Registry.Registry, registry));\n }).pipe(Effect.scoped);\n\n/**\n * Validate that the leaf's sibling impl file imports the spec, default-exports\n * a finalized `GroupImpl` layer, and provides a `FunctionImpl` for every\n * function declared by the spec.\n */\nexport const validateImpl = (leaf: LeafModule) =>\n Effect.gen(function* () {\n const implRelativePath = yield* implPathForSpec(leaf.relativePath);\n const implAbsolutePath = yield* absoluteModulePath(implRelativePath);\n const specAbsolutePath = yield* absoluteModulePath(leaf.relativePath);\n\n const bundled = yield* Bundler.bundle(implAbsolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(implRelativePath, error)),\n );\n\n if (\n !(yield* Bundler.directlyImports(\n bundled,\n implAbsolutePath,\n specAbsolutePath,\n ))\n ) {\n return yield* new ImplMissingSpecImportError({\n implPath: implRelativePath,\n expectedSpecPath: leaf.relativePath,\n });\n }\n\n if (!Layer.isLayer(bundled.module.default)) {\n return yield* new ImplMissingDefaultLayerError({\n implPath: implRelativePath,\n });\n }\n\n const { module: specModule } = yield* Bundler.bundle(specAbsolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(leaf.relativePath, error)),\n );\n const groupSpec = specModule.default as GroupSpec.AnyWithProps;\n const expectedFunctionNames = Object.keys(groupSpec.functions);\n\n const context = yield* buildImplLayer(\n bundled.module.default as Layer.Layer<unknown>,\n );\n const finalizedGroupImpl = yield* Option.match(\n findFinalizedGroupImpl(context),\n {\n onNone: () => new ImplNotFinalizedError({ implPath: implRelativePath }),\n onSome: Effect.succeed,\n },\n );\n\n const registeredSet = new Set(finalizedGroupImpl.registeredFunctionNames);\n const missing = expectedFunctionNames.filter(\n (name) => !registeredSet.has(name),\n );\n\n if (missing.length > 0) {\n return yield* new ImplMissingFunctionsError({\n implPath: implRelativePath,\n groupPath: leaf.groupPathDot,\n missingFunctionNames: missing,\n });\n }\n });\n"],"mappings":"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"}
@@ -1,6 +1,11 @@
1
- import { NodeFileSystem } from "@effect/platform-node";
2
- import { Array, Effect, Option, Ref, Schema } from "effect";
3
- import { FileSystem, Path } from "@effect/platform";
1
+ import * as Effect from "effect/Effect";
2
+ import * as FileSystem from "@effect/platform/FileSystem";
3
+ import * as Path from "@effect/platform/Path";
4
+ import * as Array from "effect/Array";
5
+ import * as Option from "effect/Option";
6
+ import * as Ref from "effect/Ref";
7
+ import * as Schema from "effect/Schema";
8
+ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
4
9
 
5
10
  //#region src/ProjectRoot.ts
6
11
  var ProjectRoot = class extends Effect.Service()("@confect/cli/ProjectRoot", {
@@ -1 +1 @@
1
- {"version":3,"file":"ProjectRoot.mjs","names":[],"sources":["../src/ProjectRoot.ts"],"sourcesContent":["import { FileSystem, Path } from \"@effect/platform\";\nimport { NodeFileSystem } from \"@effect/platform-node\";\nimport { Array, Effect, Option, Ref, Schema } from \"effect\";\n\nexport class ProjectRoot extends Effect.Service<ProjectRoot>()(\n \"@confect/cli/ProjectRoot\",\n {\n effect: Effect.gen(function* () {\n const projectRoot = yield* findProjectRoot;\n\n const ref = yield* Ref.make<string>(projectRoot);\n\n return { get: Ref.get(ref) } as const;\n }),\n dependencies: [NodeFileSystem.layer],\n accessors: true,\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 yield* Option.match(projectRoot, {\n onNone: () => Effect.fail(new ProjectRootNotFoundError()),\n onSome: Effect.succeed,\n });\n});\n\nexport class ProjectRootNotFoundError extends Schema.TaggedError<ProjectRootNotFoundError>()(\n \"ProjectRootNotFoundError\",\n {},\n) {\n override get message(): string {\n return \"Could not find project root (no 'package.json' found)\";\n }\n}\n"],"mappings":";;;;;AAIA,IAAa,cAAb,cAAiC,OAAO,SAAsB,CAC5D,4BACA;CACE,QAAQ,OAAO,IAAI,aAAa;EAC9B,MAAM,cAAc,OAAO;EAE3B,MAAM,MAAM,OAAO,IAAI,KAAa,YAAY;AAEhD,SAAO,EAAE,KAAK,IAAI,IAAI,IAAI,EAAE;GAC5B;CACF,cAAc,CAAC,eAAe,MAAM;CACpC,WAAW;CACZ,CACF,CAAC;AAEF,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,OAAO,MAAM,aAAa;EACtC,cAAc,OAAO,KAAK,IAAI,0BAA0B,CAAC;EACzD,QAAQ,OAAO;EAChB,CAAC;EACF;AAEF,IAAa,2BAAb,cAA8C,OAAO,aAAuC,CAC1F,4BACA,EAAE,CACH,CAAC;CACA,IAAa,UAAkB;AAC7B,SAAO"}
1
+ {"version":3,"file":"ProjectRoot.mjs","names":[],"sources":["../src/ProjectRoot.ts"],"sourcesContent":["import * as FileSystem from \"@effect/platform/FileSystem\";\nimport * as Path from \"@effect/platform/Path\";\nimport * as NodeFileSystem from \"@effect/platform-node/NodeFileSystem\";\nimport * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport * as Ref from \"effect/Ref\";\nimport * as Schema from \"effect/Schema\";\n\nexport class ProjectRoot extends Effect.Service<ProjectRoot>()(\n \"@confect/cli/ProjectRoot\",\n {\n effect: Effect.gen(function* () {\n const projectRoot = yield* findProjectRoot;\n\n const ref = yield* Ref.make<string>(projectRoot);\n\n return { get: Ref.get(ref) } as const;\n }),\n dependencies: [NodeFileSystem.layer],\n accessors: true,\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 yield* Option.match(projectRoot, {\n onNone: () => Effect.fail(new ProjectRootNotFoundError()),\n onSome: Effect.succeed,\n });\n});\n\nexport class ProjectRootNotFoundError extends Schema.TaggedError<ProjectRootNotFoundError>()(\n \"ProjectRootNotFoundError\",\n {},\n) {\n override get message(): string {\n return \"Could not find project root (no 'package.json' found)\";\n }\n}\n"],"mappings":";;;;;;;;;;AASA,IAAa,cAAb,cAAiC,OAAO,SAAsB,CAC5D,4BACA;CACE,QAAQ,OAAO,IAAI,aAAa;EAC9B,MAAM,cAAc,OAAO;EAE3B,MAAM,MAAM,OAAO,IAAI,KAAa,YAAY;AAEhD,SAAO,EAAE,KAAK,IAAI,IAAI,IAAI,EAAE;GAC5B;CACF,cAAc,CAAC,eAAe,MAAM;CACpC,WAAW;CACZ,CACF,CAAC;AAEF,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,OAAO,MAAM,aAAa;EACtC,cAAc,OAAO,KAAK,IAAI,0BAA0B,CAAC;EACzD,QAAQ,OAAO;EAChB,CAAC;EACF;AAEF,IAAa,2BAAb,cAA8C,OAAO,aAAuC,CAC1F,4BACA,EAAE,CACH,CAAC;CACA,IAAa,UAAkB;AAC7B,SAAO"}
@@ -1,4 +1,8 @@
1
- import { Array, Option, Order, Record, pipe } from "effect";
1
+ import * as Array from "effect/Array";
2
+ import * as Option from "effect/Option";
3
+ import { pipe } from "effect/Function";
4
+ import * as Record from "effect/Record";
5
+ import * as Order from "effect/Order";
2
6
 
3
7
  //#region src/SpecAssemblyNode.ts
4
8
  const importBindingFromLeaf = (leaf) => ({
@@ -16,13 +20,6 @@ const assemblyNodesAtDepth = (leaves, depth) => pipe(Array.groupBy(leaves, (leaf
16
20
  };
17
21
  }));
18
22
  const assemblyNodesFromLeaves = (leaves) => assemblyNodesAtDepth(leaves, 0);
19
- const partitionByRuntime = (leaves) => {
20
- const [node, convex] = Array.partition(leaves, (leaf) => leaf.runtime === "Convex");
21
- return {
22
- convex,
23
- node
24
- };
25
- };
26
23
  const importBindingsForNode = (node) => pipe(node.children, Array.flatMap(importBindingsForNode), (childBindings) => Option.match(node.importBinding, {
27
24
  onNone: () => childBindings,
28
25
  onSome: (binding) => Array.prepend(childBindings, binding)
@@ -30,5 +27,5 @@ const importBindingsForNode = (node) => pipe(node.children, Array.flatMap(import
30
27
  const collectImportBindings = (nodes) => pipe(Array.flatMap(nodes, importBindingsForNode), (bindings) => Record.fromIterableBy(bindings, (binding) => binding.importPath), Record.toEntries, Array.map(([, binding]) => binding), Array.sortBy(Order.mapInput(Order.string, (binding) => binding.localName)));
31
28
 
32
29
  //#endregion
33
- export { assemblyNodesFromLeaves, collectImportBindings, partitionByRuntime };
30
+ export { assemblyNodesFromLeaves, collectImportBindings };
34
31
  //# sourceMappingURL=SpecAssemblyNode.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"SpecAssemblyNode.mjs","names":[],"sources":["../src/SpecAssemblyNode.ts"],"sourcesContent":["import type { LeafModule } from \"./LeafModule\";\nimport { Array, Option, Order, pipe, Record } from \"effect\";\n\nexport interface SpecImportBinding {\n readonly importPath: string;\n readonly exportName: string;\n readonly localName: string;\n}\n\nexport interface SpecAssemblyNode {\n readonly segment: string;\n readonly importBinding: Option.Option<SpecImportBinding>;\n readonly children: ReadonlyArray<SpecAssemblyNode>;\n}\n\nconst importBindingFromLeaf = (leaf: LeafModule): SpecImportBinding => ({\n importPath: leaf.specImportPath,\n exportName: leaf.exportName,\n localName: leaf.pathSegments.join(\"_\"),\n});\n\nconst assemblyNodesAtDepth = (\n leaves: ReadonlyArray<LeafModule>,\n depth: number,\n): ReadonlyArray<SpecAssemblyNode> =>\n pipe(\n Array.groupBy(leaves, (leaf) => leaf.pathSegments[depth]!),\n Record.toEntries,\n Array.sortBy(Order.mapInput(Order.string, ([segment]) => segment)),\n Array.map(([segment, groupLeaves]) => {\n const terminal = Array.findFirst(\n groupLeaves,\n (leaf) => leaf.pathSegments.length === depth + 1,\n );\n const descendants = Array.filter(\n groupLeaves,\n (leaf) => leaf.pathSegments.length > depth + 1,\n );\n return {\n segment,\n importBinding: Option.map(terminal, importBindingFromLeaf),\n children: assemblyNodesAtDepth(descendants, depth + 1),\n };\n }),\n );\n\nexport const assemblyNodesFromLeaves = (\n leaves: ReadonlyArray<LeafModule>,\n): ReadonlyArray<SpecAssemblyNode> => assemblyNodesAtDepth(leaves, 0);\n\nexport const partitionByRuntime = (\n leaves: ReadonlyArray<LeafModule>,\n): {\n readonly convex: ReadonlyArray<LeafModule>;\n readonly node: ReadonlyArray<LeafModule>;\n} => {\n const [node, convex] = Array.partition(\n leaves,\n (leaf) => leaf.runtime === \"Convex\",\n );\n return { convex, node };\n};\n\nconst importBindingsForNode = (\n node: SpecAssemblyNode,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(node.children, Array.flatMap(importBindingsForNode), (childBindings) =>\n Option.match(node.importBinding, {\n onNone: () => childBindings,\n onSome: (binding) => Array.prepend(childBindings, binding),\n }),\n );\n\nexport const collectImportBindings = (\n nodes: ReadonlyArray<SpecAssemblyNode>,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(\n Array.flatMap(nodes, importBindingsForNode),\n (bindings) =>\n Record.fromIterableBy(bindings, (binding) => binding.importPath),\n Record.toEntries,\n Array.map(([, binding]) => binding),\n Array.sortBy(Order.mapInput(Order.string, (binding) => binding.localName)),\n );\n"],"mappings":";;;AAeA,MAAM,yBAAyB,UAAyC;CACtE,YAAY,KAAK;CACjB,YAAY,KAAK;CACjB,WAAW,KAAK,aAAa,KAAK,IAAI;CACvC;AAED,MAAM,wBACJ,QACA,UAEA,KACE,MAAM,QAAQ,SAAS,SAAS,KAAK,aAAa,OAAQ,EAC1D,OAAO,WACP,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,CAAC,aAAa,QAAQ,CAAC,EAClE,MAAM,KAAK,CAAC,SAAS,iBAAiB;CACpC,MAAM,WAAW,MAAM,UACrB,cACC,SAAS,KAAK,aAAa,WAAW,QAAQ,EAChD;CACD,MAAM,cAAc,MAAM,OACxB,cACC,SAAS,KAAK,aAAa,SAAS,QAAQ,EAC9C;AACD,QAAO;EACL;EACA,eAAe,OAAO,IAAI,UAAU,sBAAsB;EAC1D,UAAU,qBAAqB,aAAa,QAAQ,EAAE;EACvD;EACD,CACH;AAEH,MAAa,2BACX,WACoC,qBAAqB,QAAQ,EAAE;AAErE,MAAa,sBACX,WAIG;CACH,MAAM,CAAC,MAAM,UAAU,MAAM,UAC3B,SACC,SAAS,KAAK,YAAY,SAC5B;AACD,QAAO;EAAE;EAAQ;EAAM;;AAGzB,MAAM,yBACJ,SAEA,KAAK,KAAK,UAAU,MAAM,QAAQ,sBAAsB,GAAG,kBACzD,OAAO,MAAM,KAAK,eAAe;CAC/B,cAAc;CACd,SAAS,YAAY,MAAM,QAAQ,eAAe,QAAQ;CAC3D,CAAC,CACH;AAEH,MAAa,yBACX,UAEA,KACE,MAAM,QAAQ,OAAO,sBAAsB,GAC1C,aACC,OAAO,eAAe,WAAW,YAAY,QAAQ,WAAW,EAClE,OAAO,WACP,MAAM,KAAK,GAAG,aAAa,QAAQ,EACnC,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,YAAY,QAAQ,UAAU,CAAC,CAC3E"}
1
+ {"version":3,"file":"SpecAssemblyNode.mjs","names":[],"sources":["../src/SpecAssemblyNode.ts"],"sourcesContent":["import type { LeafModule } from \"./LeafModule\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Option from \"effect/Option\";\nimport * as Order from \"effect/Order\";\nimport * as Record from \"effect/Record\";\n\nexport interface SpecImportBinding {\n readonly importPath: string;\n readonly exportName: string;\n readonly localName: string;\n}\n\nexport interface SpecAssemblyNode {\n readonly segment: string;\n readonly importBinding: Option.Option<SpecImportBinding>;\n readonly children: ReadonlyArray<SpecAssemblyNode>;\n}\n\nconst importBindingFromLeaf = (leaf: LeafModule): SpecImportBinding => ({\n importPath: leaf.specImportPath,\n exportName: leaf.exportName,\n localName: leaf.pathSegments.join(\"_\"),\n});\n\nconst assemblyNodesAtDepth = (\n leaves: ReadonlyArray<LeafModule>,\n depth: number,\n): ReadonlyArray<SpecAssemblyNode> =>\n pipe(\n Array.groupBy(leaves, (leaf) => leaf.pathSegments[depth]!),\n Record.toEntries,\n Array.sortBy(Order.mapInput(Order.string, ([segment]) => segment)),\n Array.map(([segment, groupLeaves]) => {\n const terminal = Array.findFirst(\n groupLeaves,\n (leaf) => leaf.pathSegments.length === depth + 1,\n );\n const descendants = Array.filter(\n groupLeaves,\n (leaf) => leaf.pathSegments.length > depth + 1,\n );\n return {\n segment,\n importBinding: Option.map(terminal, importBindingFromLeaf),\n children: assemblyNodesAtDepth(descendants, depth + 1),\n };\n }),\n );\n\nexport const assemblyNodesFromLeaves = (\n leaves: ReadonlyArray<LeafModule>,\n): ReadonlyArray<SpecAssemblyNode> => assemblyNodesAtDepth(leaves, 0);\n\nconst importBindingsForNode = (\n node: SpecAssemblyNode,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(node.children, Array.flatMap(importBindingsForNode), (childBindings) =>\n Option.match(node.importBinding, {\n onNone: () => childBindings,\n onSome: (binding) => Array.prepend(childBindings, binding),\n }),\n );\n\nexport const collectImportBindings = (\n nodes: ReadonlyArray<SpecAssemblyNode>,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(\n Array.flatMap(nodes, importBindingsForNode),\n (bindings) =>\n Record.fromIterableBy(bindings, (binding) => binding.importPath),\n Record.toEntries,\n Array.map(([, binding]) => binding),\n Array.sortBy(Order.mapInput(Order.string, (binding) => binding.localName)),\n );\n"],"mappings":";;;;;;;AAmBA,MAAM,yBAAyB,UAAyC;CACtE,YAAY,KAAK;CACjB,YAAY,KAAK;CACjB,WAAW,KAAK,aAAa,KAAK,IAAI;CACvC;AAED,MAAM,wBACJ,QACA,UAEA,KACE,MAAM,QAAQ,SAAS,SAAS,KAAK,aAAa,OAAQ,EAC1D,OAAO,WACP,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,CAAC,aAAa,QAAQ,CAAC,EAClE,MAAM,KAAK,CAAC,SAAS,iBAAiB;CACpC,MAAM,WAAW,MAAM,UACrB,cACC,SAAS,KAAK,aAAa,WAAW,QAAQ,EAChD;CACD,MAAM,cAAc,MAAM,OACxB,cACC,SAAS,KAAK,aAAa,SAAS,QAAQ,EAC9C;AACD,QAAO;EACL;EACA,eAAe,OAAO,IAAI,UAAU,sBAAsB;EAC1D,UAAU,qBAAqB,aAAa,QAAQ,EAAE;EACvD;EACD,CACH;AAEH,MAAa,2BACX,WACoC,qBAAqB,QAAQ,EAAE;AAErE,MAAM,yBACJ,SAEA,KAAK,KAAK,UAAU,MAAM,QAAQ,sBAAsB,GAAG,kBACzD,OAAO,MAAM,KAAK,eAAe;CAC/B,cAAc;CACd,SAAS,YAAY,MAAM,QAAQ,eAAe,QAAQ;CAC3D,CAAC,CACH;AAEH,MAAa,yBACX,UAEA,KACE,MAAM,QAAQ,OAAO,sBAAsB,GAC1C,aACC,OAAO,eAAe,WAAW,YAAY,QAAQ,WAAW,EAClE,OAAO,WACP,MAAM,KAAK,GAAG,aAAa,QAAQ,EACnC,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,YAAY,QAAQ,UAAU,CAAC,CAC3E"}
@@ -2,9 +2,13 @@ import { fromBundlerError } from "./BuildError.mjs";
2
2
  import { bundle } from "./Bundler.mjs";
3
3
  import { DuplicateTableNameError, InvalidTableDefaultExportError, InvalidTableFilenameError } from "./CodegenError.mjs";
4
4
  import { ConfectDirectory } from "./ConfectDirectory.mjs";
5
- import { Array, Effect, Order, pipe } from "effect";
5
+ import * as Effect from "effect/Effect";
6
6
  import { Identifier } from "@confect/core";
7
- import { FileSystem, Path } from "@effect/platform";
7
+ import * as FileSystem from "@effect/platform/FileSystem";
8
+ import * as Path from "@effect/platform/Path";
9
+ import * as Array from "effect/Array";
10
+ import { pipe } from "effect/Function";
11
+ import * as Order from "effect/Order";
8
12
  import * as Table from "@confect/server/Table";
9
13
 
10
14
  //#region src/TableModule.ts
@@ -1 +1 @@
1
- {"version":3,"file":"TableModule.mjs","names":["Bundler.bundle"],"sources":["../src/TableModule.ts"],"sourcesContent":["import { Identifier } from \"@confect/core\";\nimport * as Table from \"@confect/server/Table\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Array, Effect, Order, pipe } from \"effect\";\nimport { fromBundlerError } from \"./BuildError\";\nimport * as Bundler from \"./Bundler\";\nimport {\n DuplicateTableNameError,\n InvalidTableDefaultExportError,\n InvalidTableFilenameError,\n} from \"./CodegenError\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\n\nexport const TABLES_DIRNAME = \"tables\";\n\n/**\n * Discovered metadata for a single user-authored table module under\n * `confect/tables/`.\n *\n * - `relativePath` — path from `confect/` to the file (e.g. `tables/notes.ts`).\n * - `tableName` — the file basename (e.g. `notes`). This is also the import\n * binding used in generated files, and the table name surfaced to Convex.\n */\nexport interface TableModule {\n readonly relativePath: string;\n readonly tableName: string;\n}\n\nconst tableNameFromRelativePath = (relativePath: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { name } = path.parse(relativePath);\n return name;\n });\n\nconst listTableFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const tablesDirectory = path.join(confectDirectory, TABLES_DIRNAME);\n\n if (!(yield* fs.exists(tablesDirectory))) {\n return [] as ReadonlyArray<string>;\n }\n\n const allPaths = yield* fs.readDirectory(tablesDirectory, {\n recursive: true,\n });\n\n return pipe(\n allPaths,\n Array.filter((p) => p.endsWith(\".ts\") && !p.endsWith(\".test.ts\")),\n Array.map((p) => path.join(TABLES_DIRNAME, p)),\n );\n});\n\nconst byTableName = Order.mapInput(\n Order.string,\n (tableModule: TableModule) => tableModule.tableName,\n);\n\n/**\n * Discover every `confect/tables/**\\/*.ts` module by listing the directory.\n * Validates that each filename is a legal table identifier — the table name is\n * derived from the file basename, so the filename must be a valid JavaScript\n * identifier with no leading underscore (Convex reserves `_<name>` for system\n * tables).\n *\n * This step does *not* bundle the table modules. It runs early in the codegen\n * pipeline so the `_generated/id.ts` constructor can be emitted *before* any\n * user-authored table is bundled (those modules import from `_generated/id.ts`\n * for cross-table refs).\n *\n * A missing `confect/tables/` directory is allowed and produces an empty list.\n *\n * Fails with {@link InvalidTableFilenameError} if any filename is not a valid\n * table identifier, or {@link DuplicateTableNameError} if two files resolve to\n * the same table name (the directory is scanned recursively but names are\n * derived from the basename alone, so `tables/a/notes.ts` and\n * `tables/b/notes.ts` would collide).\n */\nexport const discover = Effect.gen(function* () {\n const relativePaths = yield* listTableFiles;\n\n const tableModules = yield* Effect.forEach(\n relativePaths,\n (relativePath) =>\n Effect.gen(function* () {\n const tableName = yield* tableNameFromRelativePath(relativePath);\n yield* Effect.try({\n try: () => Identifier.validateConfectTableIdentifier(tableName),\n catch: (e) =>\n new InvalidTableFilenameError({\n tablePath: relativePath,\n reason: e instanceof Error ? e.message : String(e),\n }),\n });\n return { relativePath, tableName } satisfies TableModule;\n }),\n { concurrency: \"unbounded\" },\n );\n\n const sorted = pipe(tableModules, Array.sortBy(byTableName));\n\n const collisions = Object.entries(\n Array.groupBy(sorted, (tableModule) => tableModule.tableName),\n )\n .filter(([, group]) => group.length > 1)\n .map(([tableName, group]) => ({\n tableName,\n tablePaths: Array.map(group, (tableModule) => tableModule.relativePath),\n }));\n\n if (collisions.length > 0) {\n return yield* new DuplicateTableNameError({ collisions });\n }\n\n return sorted;\n});\n\n/**\n * Bundle every discovered table module and verify that its default export is\n * an {@link Table.UnnamedTable} (the result of `Table.make(...)` before a\n * name has been bound). Fails with {@link InvalidTableDefaultExportError} if\n * any module's default export is missing or has the wrong shape.\n *\n * Must run *after* `_generated/id.ts` has been emitted, because user-authored\n * table modules typically `import { Id } from \"../_generated/id\"` for\n * cross-table references.\n */\nexport const validate = (tableModules: ReadonlyArray<TableModule>) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n yield* Effect.forEach(\n tableModules,\n ({ relativePath }) =>\n Effect.gen(function* () {\n const absolutePath = path.resolve(confectDirectory, relativePath);\n const { module } = yield* Bundler.bundle(absolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(relativePath, error)),\n );\n\n if (!Table.isUnnamedTable(module.default)) {\n return yield* new InvalidTableDefaultExportError({\n tablePath: relativePath,\n });\n }\n }),\n { concurrency: \"unbounded\" },\n );\n });\n"],"mappings":";;;;;;;;;;AAaA,MAAa,iBAAiB;AAe9B,MAAM,6BAA6B,iBACjC,OAAO,IAAI,aAAa;CAEtB,MAAM,EAAE,UADK,OAAO,KAAK,MACH,MAAM,aAAa;AACzC,QAAO;EACP;AAEJ,MAAM,iBAAiB,OAAO,IAAI,aAAa;CAC7C,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CACjD,MAAM,kBAAkB,KAAK,KAAK,kBAAkB,eAAe;AAEnE,KAAI,EAAE,OAAO,GAAG,OAAO,gBAAgB,EACrC,QAAO,EAAE;AAOX,QAAO,KAJU,OAAO,GAAG,cAAc,iBAAiB,EACxD,WAAW,MACZ,CAAC,EAIA,MAAM,QAAQ,MAAM,EAAE,SAAS,MAAM,IAAI,CAAC,EAAE,SAAS,WAAW,CAAC,EACjE,MAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB,EAAE,CAAC,CAC/C;EACD;AAEF,MAAM,cAAc,MAAM,SACxB,MAAM,SACL,gBAA6B,YAAY,UAC3C;;;;;;;;;;;;;;;;;;;;;AAsBD,MAAa,WAAW,OAAO,IAAI,aAAa;CAC9C,MAAM,gBAAgB,OAAO;CAoB7B,MAAM,SAAS,KAlBM,OAAO,OAAO,QACjC,gBACC,iBACC,OAAO,IAAI,aAAa;EACtB,MAAM,YAAY,OAAO,0BAA0B,aAAa;AAChE,SAAO,OAAO,IAAI;GAChB,WAAW,WAAW,+BAA+B,UAAU;GAC/D,QAAQ,MACN,IAAI,0BAA0B;IAC5B,WAAW;IACX,QAAQ,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IACnD,CAAC;GACL,CAAC;AACF,SAAO;GAAE;GAAc;GAAW;GAClC,EACJ,EAAE,aAAa,aAAa,CAC7B,EAEiC,MAAM,OAAO,YAAY,CAAC;CAE5D,MAAM,aAAa,OAAO,QACxB,MAAM,QAAQ,SAAS,gBAAgB,YAAY,UAAU,CAC9D,CACE,QAAQ,GAAG,WAAW,MAAM,SAAS,EAAE,CACvC,KAAK,CAAC,WAAW,YAAY;EAC5B;EACA,YAAY,MAAM,IAAI,QAAQ,gBAAgB,YAAY,aAAa;EACxE,EAAE;AAEL,KAAI,WAAW,SAAS,EACtB,QAAO,OAAO,IAAI,wBAAwB,EAAE,YAAY,CAAC;AAG3D,QAAO;EACP;;;;;;;;;;;AAYF,MAAa,YAAY,iBACvB,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;AAEjD,QAAO,OAAO,QACZ,eACC,EAAE,mBACD,OAAO,IAAI,aAAa;EACtB,MAAM,eAAe,KAAK,QAAQ,kBAAkB,aAAa;EACjE,MAAM,EAAE,WAAW,OAAOA,OAAe,aAAa,CAAC,KACrD,OAAO,UAAU,UAAU,iBAAiB,cAAc,MAAM,CAAC,CAClE;AAED,MAAI,CAAC,MAAM,eAAe,OAAO,QAAQ,CACvC,QAAO,OAAO,IAAI,+BAA+B,EAC/C,WAAW,cACZ,CAAC;GAEJ,EACJ,EAAE,aAAa,aAAa,CAC7B;EACD"}
1
+ {"version":3,"file":"TableModule.mjs","names":["Bundler.bundle"],"sources":["../src/TableModule.ts"],"sourcesContent":["import { Identifier } from \"@confect/core\";\nimport * as Table from \"@confect/server/Table\";\nimport * as FileSystem from \"@effect/platform/FileSystem\";\nimport * as Path from \"@effect/platform/Path\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Order from \"effect/Order\";\nimport { fromBundlerError } from \"./BuildError\";\nimport * as Bundler from \"./Bundler\";\nimport {\n DuplicateTableNameError,\n InvalidTableDefaultExportError,\n InvalidTableFilenameError,\n} from \"./CodegenError\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\n\nexport const TABLES_DIRNAME = \"tables\";\n\n/**\n * Discovered metadata for a single user-authored table module under\n * `confect/tables/`.\n *\n * - `relativePath` — path from `confect/` to the file (e.g. `tables/notes.ts`).\n * - `tableName` — the file basename (e.g. `notes`). This is also the import\n * binding used in generated files, and the table name surfaced to Convex.\n */\nexport interface TableModule {\n readonly relativePath: string;\n readonly tableName: string;\n}\n\nconst tableNameFromRelativePath = (relativePath: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const { name } = path.parse(relativePath);\n return name;\n });\n\nconst listTableFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const tablesDirectory = path.join(confectDirectory, TABLES_DIRNAME);\n\n if (!(yield* fs.exists(tablesDirectory))) {\n return [] as ReadonlyArray<string>;\n }\n\n const allPaths = yield* fs.readDirectory(tablesDirectory, {\n recursive: true,\n });\n\n return pipe(\n allPaths,\n Array.filter((p) => p.endsWith(\".ts\") && !p.endsWith(\".test.ts\")),\n Array.map((p) => path.join(TABLES_DIRNAME, p)),\n );\n});\n\nconst byTableName = Order.mapInput(\n Order.string,\n (tableModule: TableModule) => tableModule.tableName,\n);\n\n/**\n * Discover every `confect/tables/**\\/*.ts` module by listing the directory.\n * Validates that each filename is a legal table identifier — the table name is\n * derived from the file basename, so the filename must be a valid JavaScript\n * identifier with no leading underscore (Convex reserves `_<name>` for system\n * tables).\n *\n * This step does *not* bundle the table modules. It runs early in the codegen\n * pipeline so the `_generated/id.ts` constructor can be emitted *before* any\n * user-authored table is bundled (those modules import from `_generated/id.ts`\n * for cross-table refs).\n *\n * A missing `confect/tables/` directory is allowed and produces an empty list.\n *\n * Fails with {@link InvalidTableFilenameError} if any filename is not a valid\n * table identifier, or {@link DuplicateTableNameError} if two files resolve to\n * the same table name (the directory is scanned recursively but names are\n * derived from the basename alone, so `tables/a/notes.ts` and\n * `tables/b/notes.ts` would collide).\n */\nexport const discover = Effect.gen(function* () {\n const relativePaths = yield* listTableFiles;\n\n const tableModules = yield* Effect.forEach(\n relativePaths,\n (relativePath) =>\n Effect.gen(function* () {\n const tableName = yield* tableNameFromRelativePath(relativePath);\n yield* Effect.try({\n try: () => Identifier.validateConfectTableIdentifier(tableName),\n catch: (e) =>\n new InvalidTableFilenameError({\n tablePath: relativePath,\n reason: e instanceof Error ? e.message : String(e),\n }),\n });\n return { relativePath, tableName } satisfies TableModule;\n }),\n { concurrency: \"unbounded\" },\n );\n\n const sorted = pipe(tableModules, Array.sortBy(byTableName));\n\n const collisions = Object.entries(\n Array.groupBy(sorted, (tableModule) => tableModule.tableName),\n )\n .filter(([, group]) => group.length > 1)\n .map(([tableName, group]) => ({\n tableName,\n tablePaths: Array.map(group, (tableModule) => tableModule.relativePath),\n }));\n\n if (collisions.length > 0) {\n return yield* new DuplicateTableNameError({ collisions });\n }\n\n return sorted;\n});\n\n/**\n * Bundle every discovered table module and verify that its default export is\n * an {@link Table.UnnamedTable} (the result of `Table.make(...)` before a\n * name has been bound). Fails with {@link InvalidTableDefaultExportError} if\n * any module's default export is missing or has the wrong shape.\n *\n * Must run *after* `_generated/id.ts` has been emitted, because user-authored\n * table modules typically `import { Id } from \"../_generated/id\"` for\n * cross-table references.\n */\nexport const validate = (tableModules: ReadonlyArray<TableModule>) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n yield* Effect.forEach(\n tableModules,\n ({ relativePath }) =>\n Effect.gen(function* () {\n const absolutePath = path.resolve(confectDirectory, relativePath);\n const { module } = yield* Bundler.bundle(absolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(relativePath, error)),\n );\n\n if (!Table.isUnnamedTable(module.default)) {\n return yield* new InvalidTableDefaultExportError({\n tablePath: relativePath,\n });\n }\n }),\n { concurrency: \"unbounded\" },\n );\n });\n"],"mappings":";;;;;;;;;;;;;;AAiBA,MAAa,iBAAiB;AAe9B,MAAM,6BAA6B,iBACjC,OAAO,IAAI,aAAa;CAEtB,MAAM,EAAE,UADK,OAAO,KAAK,MACH,MAAM,aAAa;AACzC,QAAO;EACP;AAEJ,MAAM,iBAAiB,OAAO,IAAI,aAAa;CAC7C,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CACjD,MAAM,kBAAkB,KAAK,KAAK,kBAAkB,eAAe;AAEnE,KAAI,EAAE,OAAO,GAAG,OAAO,gBAAgB,EACrC,QAAO,EAAE;AAOX,QAAO,KAJU,OAAO,GAAG,cAAc,iBAAiB,EACxD,WAAW,MACZ,CAAC,EAIA,MAAM,QAAQ,MAAM,EAAE,SAAS,MAAM,IAAI,CAAC,EAAE,SAAS,WAAW,CAAC,EACjE,MAAM,KAAK,MAAM,KAAK,KAAK,gBAAgB,EAAE,CAAC,CAC/C;EACD;AAEF,MAAM,cAAc,MAAM,SACxB,MAAM,SACL,gBAA6B,YAAY,UAC3C;;;;;;;;;;;;;;;;;;;;;AAsBD,MAAa,WAAW,OAAO,IAAI,aAAa;CAC9C,MAAM,gBAAgB,OAAO;CAoB7B,MAAM,SAAS,KAlBM,OAAO,OAAO,QACjC,gBACC,iBACC,OAAO,IAAI,aAAa;EACtB,MAAM,YAAY,OAAO,0BAA0B,aAAa;AAChE,SAAO,OAAO,IAAI;GAChB,WAAW,WAAW,+BAA+B,UAAU;GAC/D,QAAQ,MACN,IAAI,0BAA0B;IAC5B,WAAW;IACX,QAAQ,aAAa,QAAQ,EAAE,UAAU,OAAO,EAAE;IACnD,CAAC;GACL,CAAC;AACF,SAAO;GAAE;GAAc;GAAW;GAClC,EACJ,EAAE,aAAa,aAAa,CAC7B,EAEiC,MAAM,OAAO,YAAY,CAAC;CAE5D,MAAM,aAAa,OAAO,QACxB,MAAM,QAAQ,SAAS,gBAAgB,YAAY,UAAU,CAC9D,CACE,QAAQ,GAAG,WAAW,MAAM,SAAS,EAAE,CACvC,KAAK,CAAC,WAAW,YAAY;EAC5B;EACA,YAAY,MAAM,IAAI,QAAQ,gBAAgB,YAAY,aAAa;EACxE,EAAE;AAEL,KAAI,WAAW,SAAS,EACtB,QAAO,OAAO,IAAI,wBAAwB,EAAE,YAAY,CAAC;AAG3D,QAAO;EACP;;;;;;;;;;;AAYF,MAAa,YAAY,iBACvB,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;AAEjD,QAAO,OAAO,QACZ,eACC,EAAE,mBACD,OAAO,IAAI,aAAa;EACtB,MAAM,eAAe,KAAK,QAAQ,kBAAkB,aAAa;EACjE,MAAM,EAAE,WAAW,OAAOA,OAAe,aAAa,CAAC,KACrD,OAAO,UAAU,UAAU,iBAAiB,cAAc,MAAM,CAAC,CAClE;AAED,MAAI,CAAC,MAAM,eAAe,OAAO,QAAQ,CACvC,QAAO,OAAO,IAAI,+BAA+B,EAC/C,WAAW,cACZ,CAAC;GAEJ,EACJ,EAAE,aAAa,aAAa,CAC7B;EACD"}
package/dist/cliApp.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import { version } from "./package.mjs";
2
2
  import { confect } from "./confect.mjs";
3
- import { Command } from "@effect/cli";
3
+ import * as Command from "@effect/cli/Command";
4
4
 
5
5
  //#region src/cliApp.ts
6
6
  const cliApp = Command.run(confect, {
@@ -1 +1 @@
1
- {"version":3,"file":"cliApp.mjs","names":["packageJson.version"],"sources":["../src/cliApp.ts"],"sourcesContent":["import { Command } from \"@effect/cli\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { confect } from \"./confect\";\n\nexport const cliApp = Command.run(confect, {\n name: \"Confect\",\n version: packageJson.version,\n});\n"],"mappings":";;;;;AAIA,MAAa,SAAS,QAAQ,IAAI,SAAS;CACzC,MAAM;CACGA;CACV,CAAC"}
1
+ {"version":3,"file":"cliApp.mjs","names":["packageJson.version"],"sources":["../src/cliApp.ts"],"sourcesContent":["import * as Command from \"@effect/cli/Command\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { confect } from \"./confect\";\n\nexport const cliApp = Command.run(confect, {\n name: \"Confect\",\n version: packageJson.version,\n});\n"],"mappings":";;;;;AAIA,MAAa,SAAS,QAAQ,IAAI,SAAS;CACzC,MAAM;CACGA;CACV,CAAC"}
@@ -4,20 +4,26 @@ import { LegacySchemaFileError, MissingImplFileError, MissingSpecFileError, Pare
4
4
  import { ConvexDirectory } from "../ConvexDirectory.mjs";
5
5
  import { ConfectDirectory } from "../ConfectDirectory.mjs";
6
6
  import { FunctionPaths, make } from "../FunctionPaths.mjs";
7
- import { assemblyNodesFromLeaves, partitionByRuntime } from "../SpecAssemblyNode.mjs";
7
+ import { assemblyNodesFromLeaves } from "../SpecAssemblyNode.mjs";
8
8
  import { assembledSpec, convexSchema, id, refs, registeredFunctionsForGroup, runtimeSchema, schema, services, tableWrapper } from "../templates.mjs";
9
9
  import { WriteTracker, generateAuthConfig, generateCrons, generateFunctions, generateHttp, removePathIfExists, toModuleImportPath, touchConvexSchema, writeFileStringAndLog } from "../utils.mjs";
10
- import { discoverLeafImplFiles, discoverLeafSpecFiles, implPathForSpec, registeredFunctionsRelativePath, specPathForImpl, toLeafModule, toNodeRegistryLeaf, validateImpl, validateSpec } from "../LeafModule.mjs";
10
+ import { discoverLeafImplFiles, discoverLeafSpecFiles, implPathForSpec, registeredFunctionsRelativePath, specPathForImpl, toLeafModule, validateImpl, validateSpec } from "../LeafModule.mjs";
11
11
  import { TABLES_DIRNAME, discover, validate } from "../TableModule.mjs";
12
- import { Array, Effect, Either, HashSet, Match, Option, Ref } from "effect";
13
- import { Command } from "@effect/cli";
12
+ import * as Effect from "effect/Effect";
13
+ import * as Command from "@effect/cli/Command";
14
14
  import { Spec } from "@confect/core";
15
- import { FileSystem, Path } from "@effect/platform";
15
+ import * as FileSystem from "@effect/platform/FileSystem";
16
+ import * as Path from "@effect/platform/Path";
17
+ import * as Array from "effect/Array";
18
+ import * as Either from "effect/Either";
19
+ import * as HashSet from "effect/HashSet";
20
+ import * as Match from "effect/Match";
21
+ import * as Option from "effect/Option";
22
+ import * as Ref from "effect/Ref";
16
23
 
17
24
  //#region src/confect/codegen.ts
18
25
  const GENERATED_DIRNAME = "_generated";
19
26
  const GENERATED_SPEC_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "spec.ts"));
20
- const GENERATED_NODE_SPEC_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "nodeSpec.ts"));
21
27
  const GENERATED_SCHEMA_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "schema.ts"));
22
28
  const GENERATED_CONVEX_SCHEMA_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "convexSchema.ts"));
23
29
  const GENERATED_ID_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "id.ts"));
@@ -32,7 +38,8 @@ const LEGACY_PATHS = Effect.gen(function* () {
32
38
  path.join(GENERATED_DIRNAME, "registeredFunctions.ts"),
33
39
  path.join(GENERATED_DIRNAME, "nodeRegisteredFunctions.ts"),
34
40
  path.join(GENERATED_DIRNAME, "impl.ts"),
35
- path.join(GENERATED_DIRNAME, "nodeImpl.ts")
41
+ path.join(GENERATED_DIRNAME, "nodeImpl.ts"),
42
+ path.join(GENERATED_DIRNAME, "nodeSpec.ts")
36
43
  ];
37
44
  });
38
45
  const codegen = Command.make("codegen", {}, () => Effect.gen(function* () {
@@ -95,8 +102,12 @@ const loadAndValidateLeafModules = Effect.gen(function* () {
95
102
  const confectDirectory = yield* ConfectDirectory.get;
96
103
  const specFiles = yield* discoverLeafSpecFiles;
97
104
  const results = yield* Effect.forEach(specFiles, (specRelativePath) => Effect.gen(function* () {
98
- const leaf = yield* toLeafModule(specRelativePath);
99
- const groupSpec = yield* validateSpec(leaf);
105
+ const discovered = yield* toLeafModule(specRelativePath);
106
+ const groupSpec = yield* validateSpec(discovered);
107
+ const leaf = {
108
+ ...discovered,
109
+ runtime: Option.some(groupSpec.runtime)
110
+ };
100
111
  const implRelativePath = yield* implPathForSpec(specRelativePath);
101
112
  const implAbsolutePath = path.join(confectDirectory, implRelativePath);
102
113
  if (!(yield* fs.exists(implAbsolutePath))) return yield* new MissingImplFileError({
@@ -123,11 +134,8 @@ const loadAndValidateLeafModules = Effect.gen(function* () {
123
134
  * `Refs.make` error rather than a codegen-time diagnostic.
124
135
  */
125
136
  const validateNoParentChildNameCollisions = (leaves, groupSpecsByRelativePath) => Effect.gen(function* () {
126
- const { convex, node } = partitionByRuntime(leaves);
127
- const convexNodes = assemblyNodesFromLeaves(convex);
128
- const nodeNodes = assemblyNodesFromLeaves(Array.map(node, toNodeRegistryLeaf));
129
- yield* Effect.forEach(convexNodes, (n) => checkAssemblyNodeForCollisions(n, groupSpecsByRelativePath));
130
- yield* Effect.forEach(nodeNodes, (n) => checkAssemblyNodeForCollisions(n, groupSpecsByRelativePath));
137
+ const nodes = assemblyNodesFromLeaves(leaves);
138
+ yield* Effect.forEach(nodes, (n) => checkAssemblyNodeForCollisions(n, groupSpecsByRelativePath));
131
139
  });
132
140
  const checkAssemblyNodeForCollisions = (node, groupSpecsByRelativePath) => Effect.gen(function* () {
133
141
  yield* Option.match(node.importBinding, {
@@ -210,24 +218,9 @@ const generateAssembledSpecs = (leaves) => Effect.gen(function* () {
210
218
  const path = yield* Path.Path;
211
219
  const confectDirectory = yield* ConfectDirectory.get;
212
220
  const generatedSpecPath = yield* GENERATED_SPEC_PATH;
213
- const generatedNodeSpecPath = yield* GENERATED_NODE_SPEC_PATH;
214
- const { convex, node } = partitionByRuntime(leaves);
215
- if (convex.length > 0) {
216
- const nodes = assemblyNodesFromLeaves(convex);
217
- const specContents = yield* assembledSpec({
218
- nodes,
219
- runtime: "Convex"
220
- });
221
- yield* writeFileStringAndLog(path.join(confectDirectory, generatedSpecPath), specContents);
222
- }
223
- if (node.length > 0) {
224
- const nodes = assemblyNodesFromLeaves(Array.map(node, toNodeRegistryLeaf));
225
- const nodeSpecContents = yield* assembledSpec({
226
- nodes,
227
- runtime: "Node"
228
- });
229
- yield* writeFileStringAndLog(path.join(confectDirectory, generatedNodeSpecPath), nodeSpecContents);
230
- }
221
+ const nodes = assemblyNodesFromLeaves(leaves);
222
+ const specContents = yield* assembledSpec({ nodes });
223
+ yield* writeFileStringAndLog(path.join(confectDirectory, generatedSpecPath), specContents);
231
224
  });
232
225
  const validateImplModules = (leaves) => Effect.forEach(leaves, validateImpl);
233
226
  const generateGroupRegisteredFunctions = (leaves) => Effect.gen(function* () {
@@ -243,12 +236,16 @@ const generateGroupRegisteredFunctions = (leaves) => Effect.gen(function* () {
243
236
  const schemaImportPath = yield* toModuleImportPath(path.relative(path.dirname(registryPath), path.join(confectDirectory, "_generated", "schema.ts")));
244
237
  const specImportPath = yield* toModuleImportPath(path.relative(path.dirname(registryPath), path.join(confectDirectory, leaf.relativePath)));
245
238
  const implImportPath = yield* toModuleImportPath(path.relative(path.dirname(registryPath), path.join(confectDirectory, implRelativePath)));
239
+ const runtime = yield* Option.match(leaf.runtime, {
240
+ onNone: () => Effect.dieMessage(`Runtime for '${leaf.relativePath}' was not resolved before registry generation.`),
241
+ onSome: Effect.succeed
242
+ });
246
243
  yield* writeFileStringAndLog(registryPath, yield* registeredFunctionsForGroup({
247
244
  schemaImportPath,
248
245
  specImportPath,
249
246
  implImportPath,
250
247
  layerExportName: leaf.exportName,
251
- useNode: leaf.runtime === "Node"
248
+ useNode: runtime === "Node"
252
249
  }));
253
250
  }));
254
251
  });
@@ -279,44 +276,22 @@ const getGeneratedSpecPath = Effect.gen(function* () {
279
276
  const generatedSpecPath = yield* GENERATED_SPEC_PATH;
280
277
  return path.join(confectDirectory, generatedSpecPath);
281
278
  });
282
- const getGeneratedNodeSpecPath = Effect.gen(function* () {
283
- const path = yield* Path.Path;
284
- const confectDirectory = yield* ConfectDirectory.get;
285
- const generatedNodeSpecPath = yield* GENERATED_NODE_SPEC_PATH;
286
- return path.join(confectDirectory, generatedNodeSpecPath);
287
- });
288
279
  const loadGeneratedSpec = Effect.gen(function* () {
289
280
  const specPath = yield* getGeneratedSpecPath;
290
281
  const { module: specModule } = yield* bundle(specPath);
291
282
  const spec = specModule.default;
292
- if (!Spec.isConvexSpec(spec)) return yield* Effect.dieMessage("_generated/spec.ts does not export a valid Convex Spec");
283
+ if (!Spec.isSpec(spec)) return yield* Effect.dieMessage("_generated/spec.ts does not export a valid Spec");
293
284
  return spec;
294
285
  });
295
- const loadGeneratedNodeSpec = Effect.gen(function* () {
296
- const fs = yield* FileSystem.FileSystem;
297
- const nodeSpecPath = yield* getGeneratedNodeSpecPath;
298
- if (!(yield* fs.exists(nodeSpecPath))) return Option.none();
299
- const { module: nodeSpecModule } = yield* bundle(nodeSpecPath);
300
- const nodeSpec = nodeSpecModule.default;
301
- if (!Spec.isNodeSpec(nodeSpec)) return yield* Effect.dieMessage("_generated/nodeSpec.ts does not export a valid Node Spec");
302
- return Option.some(nodeSpec);
303
- });
304
286
  const emptyFunctionPaths = FunctionPaths.make(HashSet.empty());
305
287
  const loadPreviousFunctionPaths = Effect.gen(function* () {
306
288
  const fs = yield* FileSystem.FileSystem;
307
289
  const specPath = yield* getGeneratedSpecPath;
308
290
  if (!(yield* fs.exists(specPath))) return emptyFunctionPaths;
309
291
  const specEither = yield* loadGeneratedSpec.pipe(Effect.either);
310
- return yield* Either.match(specEither, {
311
- onLeft: () => Effect.succeed(emptyFunctionPaths),
312
- onRight: (spec) => Effect.gen(function* () {
313
- const nodeSpecOption = yield* loadGeneratedNodeSpec;
314
- const mergedSpec = Option.match(nodeSpecOption, {
315
- onNone: () => spec,
316
- onSome: (nodeSpec) => Spec.merge(spec, nodeSpec)
317
- });
318
- return make(mergedSpec);
319
- })
292
+ return Either.match(specEither, {
293
+ onLeft: () => emptyFunctionPaths,
294
+ onRight: (spec) => make(spec)
320
295
  });
321
296
  });
322
297
  /**
@@ -336,12 +311,7 @@ const removeObsoleteGeneratedFile = (fileName) => Effect.gen(function* () {
336
311
  const removeGeneratedApi = removeObsoleteGeneratedFile("api.ts");
337
312
  const removeGeneratedNodeApi = removeObsoleteGeneratedFile("nodeApi.ts");
338
313
  const generateFunctionModules = Effect.gen(function* () {
339
- const spec = yield* loadGeneratedSpec;
340
- const nodeSpecOption = yield* loadGeneratedNodeSpec;
341
- return yield* generateFunctions(Option.match(nodeSpecOption, {
342
- onNone: () => spec,
343
- onSome: (nodeSpec) => Spec.merge(spec, nodeSpec)
344
- }));
314
+ return yield* generateFunctions(yield* loadGeneratedSpec);
345
315
  });
346
316
  /**
347
317
  * The user-authored `confect/schema.ts` is no longer supported: codegen now
@@ -467,7 +437,6 @@ const generateServices = Effect.gen(function* () {
467
437
  yield* writeFileStringAndLog(servicesPath, yield* services({ schemaImportPath }));
468
438
  });
469
439
  const generateRefs = Effect.gen(function* () {
470
- const fs = yield* FileSystem.FileSystem;
471
440
  const path = yield* Path.Path;
472
441
  const confectDirectory = yield* ConfectDirectory.get;
473
442
  const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
@@ -475,12 +444,7 @@ const generateRefs = Effect.gen(function* () {
475
444
  const refsDir = path.dirname(refsPath);
476
445
  const generatedSpecPath = yield* GENERATED_SPEC_PATH;
477
446
  const specImportPath = yield* toModuleImportPath(path.relative(refsDir, path.join(confectDirectory, generatedSpecPath)));
478
- const nodeSpecPath = yield* getGeneratedNodeSpecPath;
479
- const nodeSpecImportPath = (yield* fs.exists(nodeSpecPath)) ? Option.some(yield* toModuleImportPath(path.relative(refsDir, nodeSpecPath))) : Option.none();
480
- yield* writeFileStringAndLog(refsPath, yield* refs({
481
- specImportPath,
482
- nodeSpecImportPath
483
- }));
447
+ yield* writeFileStringAndLog(refsPath, yield* refs({ specImportPath }));
484
448
  });
485
449
  const logGenerated = (effect) => effect.pipe(Effect.tap(Option.match({
486
450
  onNone: () => Effect.void,