@confect/cli 9.0.0-next.8 → 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.
@@ -1 +1 @@
1
- {"version":3,"file":"templates.mjs","names":[],"sources":["../src/templates.ts"],"sourcesContent":["import * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport { CodeBlockWriter } from \"./CodeBlockWriter\";\nimport {\n collectImportBindings,\n type SpecAssemblyNode,\n} from \"./SpecAssemblyNode\";\n\nexport const functions = ({\n functionNames,\n registeredFunctionsImportPath,\n useNode = false,\n}: {\n functionNames: string[];\n registeredFunctionsImportPath: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(`\"use node\";`);\n yield* cbw.blankLine();\n }\n\n yield* cbw.writeLine(\n `import registeredFunctions from \"${registeredFunctionsImportPath}\";`,\n );\n yield* cbw.newLine();\n yield* Effect.forEach(functionNames, (functionName) =>\n cbw.writeLine(\n `export const ${functionName} = registeredFunctions.${functionName};`,\n ),\n );\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `convex/schema.ts` as a one-line re-export of the codegen-emitted\n * deploy schema in `confect/_generated/convexSchema.ts`. Deploy-time\n * consumers (the Convex CLI, `convex-test`) keep reading\n * `convex/schema.ts`; the runtime `DatabaseSchema` in\n * `confect/_generated/schema.ts` is untouched by this file.\n */\nexport const schema = ({\n convexSchemaImportPath,\n}: {\n convexSchemaImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(\n `export { default } from \"${convexSchemaImportPath}\";`,\n );\n\n return yield* cbw.toString();\n });\n\ninterface TableModuleBinding {\n readonly importPath: string;\n readonly tableName: string;\n}\n\n/**\n * Emit `confect/_generated/schema.ts` — the runtime `DatabaseSchema` used\n * by impls and the per-group registries (and downstream by per-function\n * bundles for codec lookup). Every table wrapper at\n * `confect/_generated/tables/<name>.ts` is imported statically and\n * registered as a value entry on the `DatabaseSchema.make({...})` call.\n * Per-table laziness lives inside each `Table`: its `Fields`, `Doc`, and\n * `tableDefinition` are lazy memoised getters that only evaluate the\n * user-supplied field-schema callback on first access, so unused tables in\n * a function bundle never pay schema-construction cost despite the\n * static import.\n *\n * The `DatabaseSchema` import is aliased to `$DatabaseSchema` because each\n * table is imported under its own (filename-derived) name; a table named\n * `DatabaseSchema` would otherwise collide with the library import and emit\n * a duplicate-binding file. The leading `$` makes the alias collision-proof:\n * `validateConfectTableIdentifier` requires names to match\n * `/^[a-zA-Z][a-zA-Z0-9_]*$/`, which forbids `$`, so no valid table import\n * can ever shadow it.\n */\nexport const runtimeSchema = ({\n tableModules,\n}: {\n tableModules: ReadonlyArray<TableModuleBinding>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(\n `import { DatabaseSchema as $DatabaseSchema } from \"@confect/server\";`,\n );\n\n if (tableModules.length > 0) {\n yield* cbw.blankLine();\n yield* Effect.forEach(tableModules, ({ tableName, importPath }) =>\n cbw.writeLine(`import ${tableName} from \"${importPath}\";`),\n );\n }\n\n yield* cbw.blankLine();\n\n if (tableModules.length === 0) {\n yield* cbw.writeLine(`export default $DatabaseSchema.make({});`);\n } else {\n yield* cbw.writeLine(`export default $DatabaseSchema.make({`);\n yield* cbw.indent(\n Effect.gen(function* () {\n for (const { tableName } of tableModules) {\n yield* cbw.writeLine(`${tableName},`);\n }\n }),\n );\n yield* cbw.writeLine(`});`);\n }\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `confect/_generated/convexSchema.ts` — the Convex deploy-time\n * `SchemaDefinition`. Imports every table from its generated wrapper at\n * `_generated/tables/<name>` and calls `defineSchema({...})` exactly once.\n * The file deliberately avoids any `@confect/server` import so that the\n * deploy artifact's import graph stays decoupled from the runtime\n * `DatabaseSchema` machinery.\n *\n * The `defineSchema` import is aliased to `$defineSchema` because each table\n * is imported under its own (filename-derived) name; a table named\n * `defineSchema` would otherwise collide with the library import and emit a\n * duplicate-binding file. The leading `$` makes the alias collision-proof:\n * `validateConfectTableIdentifier` requires names to match\n * `/^[a-zA-Z][a-zA-Z0-9_]*$/`, which forbids `$`, so no valid table import\n * can ever shadow it.\n */\nexport const convexSchema = ({\n tableModules,\n}: {\n tableModules: ReadonlyArray<TableModuleBinding>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(\n `import { defineSchema as $defineSchema } from \"convex/server\";`,\n );\n\n if (tableModules.length > 0) {\n yield* cbw.blankLine();\n yield* Effect.forEach(tableModules, ({ tableName, importPath }) =>\n cbw.writeLine(`import ${tableName} from \"${importPath}\";`),\n );\n }\n\n yield* cbw.blankLine();\n\n if (tableModules.length === 0) {\n yield* cbw.writeLine(`export default $defineSchema({});`);\n } else {\n yield* cbw.writeLine(`export default $defineSchema({`);\n yield* cbw.indent(\n Effect.gen(function* () {\n for (const { tableName } of tableModules) {\n yield* cbw.writeLine(`${tableName}: ${tableName}.tableDefinition,`);\n }\n }),\n );\n yield* cbw.writeLine(`});`);\n }\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `confect/_generated/id.ts` — a type-constrained `Id` constructor and\n * a `TableNames` union derived from the user's `confect/tables/*.ts`\n * filenames. User-authored table modules import `Id` from this file to\n * declare cross-table id references without typing the destination name as\n * a free string (and without ever importing each other transitively).\n *\n * When the table directory is empty the `TableNames` union resolves to\n * `never`, which still lets the file typecheck against an empty workspace.\n */\nexport const id = ({ tableNames }: { tableNames: ReadonlyArray<string> }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { GenericId } from \"@confect/core\";`);\n yield* cbw.blankLine();\n\n const union =\n tableNames.length === 0\n ? \"never\"\n : tableNames.map((n) => `\"${n}\"`).join(\" | \");\n yield* cbw.writeLine(`export type TableNames = ${union};`);\n yield* cbw.blankLine();\n\n yield* cbw.writeLine(\n `export const Id = <const TableName extends TableNames>(`,\n );\n yield* cbw.indent(cbw.writeLine(`tableName: TableName,`));\n yield* cbw.writeLine(`) => GenericId.GenericId(tableName);`);\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `confect/_generated/tables/<tableName>.ts` — a two-line wrapper that\n * imports the user-authored `UnnamedTable` and binds the file basename to\n * it, producing the fully-named `Table` value that downstream consumers\n * (schema, specs, impls) read.\n */\nexport const tableWrapper = ({\n tableName,\n unnamedImportPath,\n}: {\n tableName: string;\n unnamedImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import unnamed from \"${unnamedImportPath}\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`export default unnamed(\"${tableName}\");`);\n\n return yield* cbw.toString();\n });\n\nexport const http = ({ httpImportPath }: { httpImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import http from \"${httpImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default http;`);\n\n return yield* cbw.toString();\n });\n\nexport const crons = ({ cronsImportPath }: { cronsImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import crons from \"${cronsImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default crons.convexCronJobs;`);\n\n return yield* cbw.toString();\n });\n\nexport const authConfig = ({ authImportPath }: { authImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import auth from \"${authImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default auth;`);\n\n return yield* cbw.toString();\n });\n\nexport const refs = ({\n specImportPath,\n nodeSpecImportPath,\n}: {\n specImportPath: string;\n nodeSpecImportPath: Option.Option<string>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Refs } from \"@confect/core\";`);\n yield* cbw.writeLine(`import spec from \"${specImportPath}\";`);\n yield* Option.match(nodeSpecImportPath, {\n onNone: () => Effect.void,\n onSome: (nodeSpecImportPath_) =>\n cbw.writeLine(`import nodeSpec from \"${nodeSpecImportPath_}\";`),\n });\n yield* cbw.blankLine();\n yield* cbw.writeLine(\n Option.match(nodeSpecImportPath, {\n onSome: () => `export default Refs.make(spec, nodeSpec);`,\n onNone: () => `export default Refs.make(spec);`,\n }),\n );\n\n return yield* cbw.toString();\n });\n\nexport const registeredFunctionsForGroup = ({\n schemaImportPath,\n specImportPath,\n implImportPath,\n layerExportName,\n useNode = false,\n}: {\n schemaImportPath: string;\n specImportPath: string;\n implImportPath: string;\n layerExportName: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(\n `import { RegisteredFunctions } from \"@confect/server\";`,\n );\n yield* cbw.writeLine(\n `import { RegisteredNodeFunction } from \"@confect/server/node\";`,\n );\n } else {\n yield* cbw.writeLine(\n `import { RegisteredConvexFunction, RegisteredFunctions } from \"@confect/server\";`,\n );\n }\n\n yield* cbw.writeLine(`import databaseSchema from \"${schemaImportPath}\";`);\n yield* cbw.writeLine(`import ${layerExportName} from \"${implImportPath}\";`);\n yield* cbw.blankLine();\n // The group's own leaf spec is referenced type-only (`typeof import(...)`),\n // so the spec module is erased at transpile time and never enters the\n // per-function bundle; only `databaseSchema` and the impl are runtime\n // imports. Typing from the leaf spec (not the project-wide assembled spec)\n // keeps the registry's type dependent solely on its own group.\n const specType = `typeof import(\"${specImportPath}\")[\"default\"]`;\n const makeFn = useNode\n ? \"RegisteredNodeFunction.make\"\n : \"RegisteredConvexFunction.make\";\n yield* cbw.writeLine(\n `export default RegisteredFunctions.buildForGroup<${specType}>(databaseSchema, ${layerExportName}, ${makeFn});`,\n );\n\n return yield* cbw.toString();\n });\n\nexport const services = ({ schemaImportPath }: { schemaImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n // Imports\n yield* cbw.writeLine(\"import {\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx as ActionCtx_,\");\n yield* cbw.writeLine(\"ActionRunner as ActionRunner_,\");\n yield* cbw.writeLine(\"Auth as Auth_,\");\n yield* cbw.writeLine(\"type DataModel,\");\n yield* cbw.writeLine(\"DatabaseReader as DatabaseReader_,\");\n yield* cbw.writeLine(\"DatabaseWriter as DatabaseWriter_,\");\n yield* cbw.writeLine(\"MutationCtx as MutationCtx_,\");\n yield* cbw.writeLine(\"MutationRunner as MutationRunner_,\");\n yield* cbw.writeLine(\"QueryCtx as QueryCtx_,\");\n yield* cbw.writeLine(\"QueryRunner as QueryRunner_,\");\n yield* cbw.writeLine(\"Scheduler as Scheduler_,\");\n yield* cbw.writeLine(\"StorageActionWriter as StorageActionWriter_,\");\n yield* cbw.writeLine(\"StorageReader as StorageReader_,\");\n yield* cbw.writeLine(\"StorageWriter as StorageWriter_,\");\n yield* cbw.writeLine(\"VectorSearch as VectorSearch_,\");\n }),\n );\n yield* cbw.writeLine(`} from \"@confect/server\";`);\n yield* cbw.writeLine(\n `import type schemaDefinition from \"${schemaImportPath}\";`,\n );\n yield* cbw.blankLine();\n\n // Auth\n yield* cbw.writeLine(\"export const Auth = Auth_.Auth;\");\n yield* cbw.writeLine(\"export type Auth = typeof Auth.Identifier;\");\n yield* cbw.blankLine();\n\n // Scheduler\n yield* cbw.writeLine(\"export const Scheduler = Scheduler_.Scheduler;\");\n yield* cbw.writeLine(\n \"export type Scheduler = typeof Scheduler.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageReader\n yield* cbw.writeLine(\n \"export const StorageReader = StorageReader_.StorageReader;\",\n );\n yield* cbw.writeLine(\n \"export type StorageReader = typeof StorageReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageWriter\n yield* cbw.writeLine(\n \"export const StorageWriter = StorageWriter_.StorageWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageWriter = typeof StorageWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageActionWriter\n yield* cbw.writeLine(\n \"export const StorageActionWriter = StorageActionWriter_.StorageActionWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageActionWriter = typeof StorageActionWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // VectorSearch\n yield* cbw.writeLine(\"export const VectorSearch =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"VectorSearch_.VectorSearch<DataModel.FromSchema<typeof schemaDefinition>>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type VectorSearch = typeof VectorSearch.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseReader\n yield* cbw.writeLine(\"export const DatabaseReader =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseReader_.DatabaseReader<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseReader = typeof DatabaseReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseWriter\n yield* cbw.writeLine(\"export const DatabaseWriter =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseWriter_.DatabaseWriter<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseWriter = typeof DatabaseWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryRunner\n yield* cbw.writeLine(\n \"export const QueryRunner = QueryRunner_.QueryRunner;\",\n );\n yield* cbw.writeLine(\n \"export type QueryRunner = typeof QueryRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // MutationRunner\n yield* cbw.writeLine(\n \"export const MutationRunner = MutationRunner_.MutationRunner;\",\n );\n yield* cbw.writeLine(\n \"export type MutationRunner = typeof MutationRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionRunner\n yield* cbw.writeLine(\n \"export const ActionRunner = ActionRunner_.ActionRunner;\",\n );\n yield* cbw.writeLine(\n \"export type ActionRunner = typeof ActionRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryCtx\n yield* cbw.writeLine(\"export const QueryCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"QueryCtx_.QueryCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\"export type QueryCtx = typeof QueryCtx.Identifier;\");\n yield* cbw.blankLine();\n\n // MutationCtx\n yield* cbw.writeLine(\"export const MutationCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"MutationCtx_.MutationCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type MutationCtx = typeof MutationCtx.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionCtx\n yield* cbw.writeLine(\"export const ActionCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx_.ActionCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type ActionCtx = typeof ActionCtx.Identifier;\",\n );\n\n return yield* cbw.toString();\n });\n\nconst writeChildAddGroupAt = (\n cbw: CodeBlockWriter,\n child: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addGroupAt(\");\n yield* cbw.quote(child.segment);\n yield* cbw.write(\", \");\n yield* writeGroupAssembly(cbw, child, groupFactory);\n yield* cbw.write(\")\");\n });\n\nconst writeGroupFactoryCall = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(groupFactory);\n yield* cbw.write(\"(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\")\");\n\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n });\n\nconst writeGroupAssembly: (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n) => Effect.Effect<void> = (cbw, node, groupFactory) =>\n Option.match(node.importBinding, {\n onNone: () => writeGroupFactoryCall(cbw, node, groupFactory),\n onSome: (binding) =>\n Effect.gen(function* () {\n yield* cbw.write(binding.localName);\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n }),\n });\n\nconst writeRootAddAt = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addAt(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\", \");\n\n yield* writeGroupAssembly(cbw, node, groupFactory);\n\n yield* cbw.write(\")\");\n });\n\nexport const assembledSpec = ({\n nodes,\n runtime,\n}: {\n nodes: ReadonlyArray<SpecAssemblyNode>;\n runtime: \"Convex\" | \"Node\";\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n const nodeRequiresGroupFactory = (node: SpecAssemblyNode): boolean =>\n Option.isNone(node.importBinding) ||\n Array.some(node.children, nodeRequiresGroupFactory);\n\n const needsGroupSpec = Array.some(nodes, nodeRequiresGroupFactory);\n yield* cbw.writeLine(\n needsGroupSpec\n ? `import { GroupSpec, Spec } from \"@confect/core\";`\n : `import { Spec } from \"@confect/core\";`,\n );\n\n yield* Effect.forEach(collectImportBindings(nodes), (binding) =>\n cbw.writeLine(\n `import ${binding.localName} from \"${binding.importPath}\";`,\n ),\n );\n\n yield* cbw.blankLine();\n\n const specFactory =\n runtime === \"Convex\" ? \"Spec.make()\" : \"Spec.makeNode()\";\n const groupFactory =\n runtime === \"Convex\" ? \"GroupSpec.makeAt\" : \"GroupSpec.makeNodeAt\";\n\n yield* cbw.write(`export default ${specFactory}`);\n yield* Effect.forEach(nodes, (node) =>\n writeRootAddAt(cbw, node, groupFactory),\n );\n yield* cbw.write(\";\");\n yield* cbw.newLine();\n\n return yield* cbw.toString();\n });\n"],"mappings":"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"}
1
+ {"version":3,"file":"templates.mjs","names":[],"sources":["../src/templates.ts"],"sourcesContent":["import * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport { CodeBlockWriter } from \"./CodeBlockWriter\";\nimport {\n collectImportBindings,\n type SpecAssemblyNode,\n} from \"./SpecAssemblyNode\";\n\nexport const functions = ({\n functionNames,\n registeredFunctionsImportPath,\n useNode = false,\n}: {\n functionNames: string[];\n registeredFunctionsImportPath: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(`\"use node\";`);\n yield* cbw.blankLine();\n }\n\n yield* cbw.writeLine(\n `import registeredFunctions from \"${registeredFunctionsImportPath}\";`,\n );\n yield* cbw.newLine();\n yield* Effect.forEach(functionNames, (functionName) =>\n cbw.writeLine(\n `export const ${functionName} = registeredFunctions.${functionName};`,\n ),\n );\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `convex/schema.ts` as a one-line re-export of the codegen-emitted\n * deploy schema in `confect/_generated/convexSchema.ts`. Deploy-time\n * consumers (the Convex CLI, `convex-test`) keep reading\n * `convex/schema.ts`; the runtime `DatabaseSchema` in\n * `confect/_generated/schema.ts` is untouched by this file.\n */\nexport const schema = ({\n convexSchemaImportPath,\n}: {\n convexSchemaImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(\n `export { default } from \"${convexSchemaImportPath}\";`,\n );\n\n return yield* cbw.toString();\n });\n\ninterface TableModuleBinding {\n readonly importPath: string;\n readonly tableName: string;\n}\n\n/**\n * Emit `confect/_generated/schema.ts` — the runtime `DatabaseSchema` used\n * by impls and the per-group registries (and downstream by per-function\n * bundles for codec lookup). Every table wrapper at\n * `confect/_generated/tables/<name>.ts` is imported statically and\n * registered as a value entry on the `DatabaseSchema.make({...})` call.\n * Per-table laziness lives inside each `Table`: its `Fields`, `Doc`, and\n * `tableDefinition` are lazy memoised getters that only evaluate the\n * user-supplied field-schema callback on first access, so unused tables in\n * a function bundle never pay schema-construction cost despite the\n * static import.\n *\n * The `DatabaseSchema` import is aliased to `$DatabaseSchema` because each\n * table is imported under its own (filename-derived) name; a table named\n * `DatabaseSchema` would otherwise collide with the library import and emit\n * a duplicate-binding file. The leading `$` makes the alias collision-proof:\n * `validateConfectTableIdentifier` requires names to match\n * `/^[a-zA-Z][a-zA-Z0-9_]*$/`, which forbids `$`, so no valid table import\n * can ever shadow it.\n */\nexport const runtimeSchema = ({\n tableModules,\n}: {\n tableModules: ReadonlyArray<TableModuleBinding>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(\n `import { DatabaseSchema as $DatabaseSchema } from \"@confect/server\";`,\n );\n\n if (tableModules.length > 0) {\n yield* cbw.blankLine();\n yield* Effect.forEach(tableModules, ({ tableName, importPath }) =>\n cbw.writeLine(`import ${tableName} from \"${importPath}\";`),\n );\n }\n\n yield* cbw.blankLine();\n\n if (tableModules.length === 0) {\n yield* cbw.writeLine(`export default $DatabaseSchema.make({});`);\n } else {\n yield* cbw.writeLine(`export default $DatabaseSchema.make({`);\n yield* cbw.indent(\n Effect.gen(function* () {\n for (const { tableName } of tableModules) {\n yield* cbw.writeLine(`${tableName},`);\n }\n }),\n );\n yield* cbw.writeLine(`});`);\n }\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `confect/_generated/convexSchema.ts` — the Convex deploy-time\n * `SchemaDefinition`. Imports every table from its generated wrapper at\n * `_generated/tables/<name>` and calls `defineSchema({...})` exactly once.\n * The file deliberately avoids any `@confect/server` import so that the\n * deploy artifact's import graph stays decoupled from the runtime\n * `DatabaseSchema` machinery.\n *\n * The `defineSchema` import is aliased to `$defineSchema` because each table\n * is imported under its own (filename-derived) name; a table named\n * `defineSchema` would otherwise collide with the library import and emit a\n * duplicate-binding file. The leading `$` makes the alias collision-proof:\n * `validateConfectTableIdentifier` requires names to match\n * `/^[a-zA-Z][a-zA-Z0-9_]*$/`, which forbids `$`, so no valid table import\n * can ever shadow it.\n */\nexport const convexSchema = ({\n tableModules,\n}: {\n tableModules: ReadonlyArray<TableModuleBinding>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(\n `import { defineSchema as $defineSchema } from \"convex/server\";`,\n );\n\n if (tableModules.length > 0) {\n yield* cbw.blankLine();\n yield* Effect.forEach(tableModules, ({ tableName, importPath }) =>\n cbw.writeLine(`import ${tableName} from \"${importPath}\";`),\n );\n }\n\n yield* cbw.blankLine();\n\n if (tableModules.length === 0) {\n yield* cbw.writeLine(`export default $defineSchema({});`);\n } else {\n yield* cbw.writeLine(`export default $defineSchema({`);\n yield* cbw.indent(\n Effect.gen(function* () {\n for (const { tableName } of tableModules) {\n yield* cbw.writeLine(`${tableName}: ${tableName}.tableDefinition,`);\n }\n }),\n );\n yield* cbw.writeLine(`});`);\n }\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `confect/_generated/id.ts` — a type-constrained `Id` constructor and\n * a `TableNames` union derived from the user's `confect/tables/*.ts`\n * filenames. User-authored table modules import `Id` from this file to\n * declare cross-table id references without typing the destination name as\n * a free string (and without ever importing each other transitively).\n *\n * When the table directory is empty the `TableNames` union resolves to\n * `never`, which still lets the file typecheck against an empty workspace.\n */\nexport const id = ({ tableNames }: { tableNames: ReadonlyArray<string> }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { GenericId } from \"@confect/core\";`);\n yield* cbw.blankLine();\n\n const union =\n tableNames.length === 0\n ? \"never\"\n : tableNames.map((n) => `\"${n}\"`).join(\" | \");\n yield* cbw.writeLine(`export type TableNames = ${union};`);\n yield* cbw.blankLine();\n\n yield* cbw.writeLine(\n `export const Id = <const TableName extends TableNames>(`,\n );\n yield* cbw.indent(cbw.writeLine(`tableName: TableName,`));\n yield* cbw.writeLine(`) => GenericId.GenericId(tableName);`);\n\n return yield* cbw.toString();\n });\n\n/**\n * Emit `confect/_generated/tables/<tableName>.ts` — a two-line wrapper that\n * imports the user-authored `UnnamedTable` and binds the file basename to\n * it, producing the fully-named `Table` value that downstream consumers\n * (schema, specs, impls) read.\n */\nexport const tableWrapper = ({\n tableName,\n unnamedImportPath,\n}: {\n tableName: string;\n unnamedImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import unnamed from \"${unnamedImportPath}\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`export default unnamed(\"${tableName}\");`);\n\n return yield* cbw.toString();\n });\n\nexport const http = ({ httpImportPath }: { httpImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import http from \"${httpImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default http;`);\n\n return yield* cbw.toString();\n });\n\nexport const crons = ({ cronsImportPath }: { cronsImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import crons from \"${cronsImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default crons.convexCronJobs;`);\n\n return yield* cbw.toString();\n });\n\nexport const authConfig = ({ authImportPath }: { authImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import auth from \"${authImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default auth;`);\n\n return yield* cbw.toString();\n });\n\nexport const refs = ({ specImportPath }: { specImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Refs } from \"@confect/core\";`);\n yield* cbw.writeLine(`import spec from \"${specImportPath}\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`export default Refs.make(spec);`);\n\n return yield* cbw.toString();\n });\n\nexport const registeredFunctionsForGroup = ({\n schemaImportPath,\n specImportPath,\n implImportPath,\n layerExportName,\n useNode = false,\n}: {\n schemaImportPath: string;\n specImportPath: string;\n implImportPath: string;\n layerExportName: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(\n `import { RegisteredFunctions } from \"@confect/server\";`,\n );\n yield* cbw.writeLine(\n `import { RegisteredNodeFunction } from \"@confect/server/node\";`,\n );\n } else {\n yield* cbw.writeLine(\n `import { RegisteredConvexFunction, RegisteredFunctions } from \"@confect/server\";`,\n );\n }\n\n yield* cbw.writeLine(`import databaseSchema from \"${schemaImportPath}\";`);\n yield* cbw.writeLine(`import ${layerExportName} from \"${implImportPath}\";`);\n yield* cbw.blankLine();\n // The group's own leaf spec is referenced type-only (`typeof import(...)`),\n // so the spec module is erased at transpile time and never enters the\n // per-function bundle; only `databaseSchema` and the impl are runtime\n // imports. Typing from the leaf spec (not the project-wide assembled spec)\n // keeps the registry's type dependent solely on its own group.\n const specType = `typeof import(\"${specImportPath}\")[\"default\"]`;\n const makeFn = useNode\n ? \"RegisteredNodeFunction.make\"\n : \"RegisteredConvexFunction.make\";\n yield* cbw.writeLine(\n `export default RegisteredFunctions.buildForGroup<${specType}>(databaseSchema, ${layerExportName}, ${makeFn});`,\n );\n\n return yield* cbw.toString();\n });\n\nexport const services = ({ schemaImportPath }: { schemaImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n // Imports\n yield* cbw.writeLine(\"import {\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx as ActionCtx_,\");\n yield* cbw.writeLine(\"ActionRunner as ActionRunner_,\");\n yield* cbw.writeLine(\"Auth as Auth_,\");\n yield* cbw.writeLine(\"type DataModel,\");\n yield* cbw.writeLine(\"DatabaseReader as DatabaseReader_,\");\n yield* cbw.writeLine(\"DatabaseWriter as DatabaseWriter_,\");\n yield* cbw.writeLine(\"MutationCtx as MutationCtx_,\");\n yield* cbw.writeLine(\"MutationRunner as MutationRunner_,\");\n yield* cbw.writeLine(\"QueryCtx as QueryCtx_,\");\n yield* cbw.writeLine(\"QueryRunner as QueryRunner_,\");\n yield* cbw.writeLine(\"Scheduler as Scheduler_,\");\n yield* cbw.writeLine(\"StorageActionWriter as StorageActionWriter_,\");\n yield* cbw.writeLine(\"StorageReader as StorageReader_,\");\n yield* cbw.writeLine(\"StorageWriter as StorageWriter_,\");\n yield* cbw.writeLine(\"VectorSearch as VectorSearch_,\");\n }),\n );\n yield* cbw.writeLine(`} from \"@confect/server\";`);\n yield* cbw.writeLine(\n `import type schemaDefinition from \"${schemaImportPath}\";`,\n );\n yield* cbw.blankLine();\n\n // Auth\n yield* cbw.writeLine(\"export const Auth = Auth_.Auth;\");\n yield* cbw.writeLine(\"export type Auth = typeof Auth.Identifier;\");\n yield* cbw.blankLine();\n\n // Scheduler\n yield* cbw.writeLine(\"export const Scheduler = Scheduler_.Scheduler;\");\n yield* cbw.writeLine(\n \"export type Scheduler = typeof Scheduler.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageReader\n yield* cbw.writeLine(\n \"export const StorageReader = StorageReader_.StorageReader;\",\n );\n yield* cbw.writeLine(\n \"export type StorageReader = typeof StorageReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageWriter\n yield* cbw.writeLine(\n \"export const StorageWriter = StorageWriter_.StorageWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageWriter = typeof StorageWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageActionWriter\n yield* cbw.writeLine(\n \"export const StorageActionWriter = StorageActionWriter_.StorageActionWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageActionWriter = typeof StorageActionWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // VectorSearch\n yield* cbw.writeLine(\"export const VectorSearch =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"VectorSearch_.VectorSearch<DataModel.FromSchema<typeof schemaDefinition>>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type VectorSearch = typeof VectorSearch.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseReader\n yield* cbw.writeLine(\"export const DatabaseReader =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseReader_.DatabaseReader<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseReader = typeof DatabaseReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseWriter\n yield* cbw.writeLine(\"export const DatabaseWriter =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseWriter_.DatabaseWriter<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseWriter = typeof DatabaseWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryRunner\n yield* cbw.writeLine(\n \"export const QueryRunner = QueryRunner_.QueryRunner;\",\n );\n yield* cbw.writeLine(\n \"export type QueryRunner = typeof QueryRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // MutationRunner\n yield* cbw.writeLine(\n \"export const MutationRunner = MutationRunner_.MutationRunner;\",\n );\n yield* cbw.writeLine(\n \"export type MutationRunner = typeof MutationRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionRunner\n yield* cbw.writeLine(\n \"export const ActionRunner = ActionRunner_.ActionRunner;\",\n );\n yield* cbw.writeLine(\n \"export type ActionRunner = typeof ActionRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryCtx\n yield* cbw.writeLine(\"export const QueryCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"QueryCtx_.QueryCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\"export type QueryCtx = typeof QueryCtx.Identifier;\");\n yield* cbw.blankLine();\n\n // MutationCtx\n yield* cbw.writeLine(\"export const MutationCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"MutationCtx_.MutationCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type MutationCtx = typeof MutationCtx.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionCtx\n yield* cbw.writeLine(\"export const ActionCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx_.ActionCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type ActionCtx = typeof ActionCtx.Identifier;\",\n );\n\n return yield* cbw.toString();\n });\n\nconst writeChildAddGroupAt = (\n cbw: CodeBlockWriter,\n child: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addGroupAt(\");\n yield* cbw.quote(child.segment);\n yield* cbw.write(\", \");\n yield* writeGroupAssembly(cbw, child, groupFactory);\n yield* cbw.write(\")\");\n });\n\nconst writeGroupFactoryCall = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(groupFactory);\n yield* cbw.write(\"(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\")\");\n\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n });\n\nconst writeGroupAssembly: (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n) => Effect.Effect<void> = (cbw, node, groupFactory) =>\n Option.match(node.importBinding, {\n onNone: () => writeGroupFactoryCall(cbw, node, groupFactory),\n onSome: (binding) =>\n Effect.gen(function* () {\n yield* cbw.write(binding.localName);\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n }),\n });\n\nconst writeRootAddAt = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addAt(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\", \");\n\n yield* writeGroupAssembly(cbw, node, groupFactory);\n\n yield* cbw.write(\")\");\n });\n\nexport const assembledSpec = ({\n nodes,\n}: {\n nodes: ReadonlyArray<SpecAssemblyNode>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n const nodeRequiresGroupFactory = (node: SpecAssemblyNode): boolean =>\n Option.isNone(node.importBinding) ||\n Array.some(node.children, nodeRequiresGroupFactory);\n\n const needsGroupSpec = Array.some(nodes, nodeRequiresGroupFactory);\n yield* cbw.writeLine(\n needsGroupSpec\n ? `import { GroupSpec, Spec } from \"@confect/core\";`\n : `import { Spec } from \"@confect/core\";`,\n );\n\n yield* Effect.forEach(collectImportBindings(nodes), (binding) =>\n cbw.writeLine(\n `import ${binding.localName} from \"${binding.importPath}\";`,\n ),\n );\n\n yield* cbw.blankLine();\n\n // The assembled spec is runtime-agnostic: a Node group's `makeNode()` is\n // already baked into its imported leaf spec, so the root is always\n // `Spec.make()` and binding-less container groups always use\n // `GroupSpec.makeAt` (containers register no functions and carry no runtime).\n yield* cbw.write(`export default Spec.make()`);\n yield* Effect.forEach(nodes, (node) =>\n writeRootAddAt(cbw, node, \"GroupSpec.makeAt\"),\n );\n yield* cbw.write(\";\");\n yield* cbw.newLine();\n\n return yield* cbw.toString();\n });\n"],"mappings":"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"}
package/dist/utils.mjs CHANGED
@@ -104,26 +104,40 @@ const generateGroupModule = ({ groupPath, functionNames, registeredFunctionsImpo
104
104
  const path = yield* Path.Path;
105
105
  const convexDirectory = yield* ConvexDirectory.get;
106
106
  const relativeModulePath = yield* modulePath(groupPath);
107
- const modulePath$2 = path.join(convexDirectory, relativeModulePath);
108
- const directoryPath = path.dirname(modulePath$2);
107
+ const modulePath$1 = path.join(convexDirectory, relativeModulePath);
108
+ const directoryPath = path.dirname(modulePath$1);
109
109
  if (!(yield* fs.exists(directoryPath))) yield* fs.makeDirectory(directoryPath, { recursive: true });
110
110
  const functionsContentsString = yield* functions({
111
111
  functionNames,
112
112
  registeredFunctionsImportPath,
113
113
  useNode
114
114
  });
115
- if (!(yield* fs.exists(modulePath$2))) {
116
- yield* fs.writeFileString(modulePath$2, functionsContentsString);
115
+ if (!(yield* fs.exists(modulePath$1))) {
116
+ yield* fs.writeFileString(modulePath$1, functionsContentsString);
117
117
  yield* markWritten;
118
118
  return "Added";
119
119
  }
120
- if ((yield* fs.readFileString(modulePath$2)) !== functionsContentsString) {
121
- yield* fs.writeFileString(modulePath$2, functionsContentsString);
120
+ if ((yield* fs.readFileString(modulePath$1)) !== functionsContentsString) {
121
+ yield* fs.writeFileString(modulePath$1, functionsContentsString);
122
122
  yield* markWritten;
123
123
  return "Modified";
124
124
  }
125
125
  return "Unchanged";
126
126
  });
127
+ /**
128
+ * Compute the module import specifier (relative to `modulePath`) for a group's
129
+ * registry file under `confect/_generated/registeredFunctions/`. The registry
130
+ * path mirrors the group's path one-to-one (see `registeredFunctionsRelativePath`
131
+ * in `LeafModule.ts`) for both Convex and Node groups. Centralizing this here
132
+ * keeps the "overlapping" and "new" group branches of `generateFunctions` from
133
+ * drifting apart.
134
+ */
135
+ const registeredFunctionsImportPathForGroup = (groupPath, modulePath) => Effect.gen(function* () {
136
+ const path = yield* Path.Path;
137
+ const confectDirectory = yield* ConfectDirectory.get;
138
+ const registeredFunctionsPath = path.join(confectDirectory, "_generated", "registeredFunctions", ...groupPath.pathSegments) + ".ts";
139
+ return yield* toModuleImportPath(path.relative(path.dirname(modulePath), registeredFunctionsPath));
140
+ });
127
141
  const logGroupPaths = (groupPaths, logFn) => Effect.gen(function* () {
128
142
  const path = yield* Path.Path;
129
143
  const convexDirectory = yield* ConvexDirectory.get;
@@ -135,23 +149,21 @@ const logGroupPaths = (groupPaths, logFn) => Effect.gen(function* () {
135
149
  const generateFunctions = (spec) => Effect.gen(function* () {
136
150
  const path = yield* Path.Path;
137
151
  const convexDirectory = yield* ConvexDirectory.get;
138
- const confectDirectory = yield* ConfectDirectory.get;
139
152
  const groupPathsFromFs = yield* getGroupPathsFromFs;
140
153
  const functionPaths = make(spec);
141
154
  const groupPathsFromSpec = groupPaths(functionPaths);
142
155
  const overlappingGroupPaths = GroupPaths.make(HashSet.intersection(groupPathsFromFs, groupPathsFromSpec));
143
156
  yield* Effect.forEach(overlappingGroupPaths, (groupPath) => Effect.gen(function* () {
144
- const functionNames = pipe((yield* getGroupSpec(spec, groupPath)).functions, Record.values, Array.sortBy(Order.mapInput(Order.string, (fn) => fn.name)), Array.map((fn) => fn.name));
157
+ const group = yield* getGroupSpec(spec, groupPath);
158
+ const functionNames = pipe(group.functions, Record.values, Array.sortBy(Order.mapInput(Order.string, (fn) => fn.name)), Array.map((fn) => fn.name));
145
159
  const relativeModulePath = yield* modulePath(groupPath);
146
- const modulePath$1 = path.join(convexDirectory, relativeModulePath);
147
- const registrySegments = groupPath.pathSegments[0] === "node" ? groupPath.pathSegments.slice(1) : groupPath.pathSegments;
148
- const registeredFunctionsPath = path.join(confectDirectory, "_generated", "registeredFunctions", ...registrySegments) + ".ts";
160
+ const modulePath$3 = path.join(convexDirectory, relativeModulePath);
149
161
  if ((yield* generateGroupModule({
150
162
  groupPath,
151
163
  functionNames,
152
- registeredFunctionsImportPath: yield* toModuleImportPath(path.relative(path.dirname(modulePath$1), registeredFunctionsPath)),
153
- useNode: groupPath.pathSegments[0] === "node"
154
- })) === "Modified") yield* logFileModified(modulePath$1);
164
+ registeredFunctionsImportPath: yield* registeredFunctionsImportPathForGroup(groupPath, modulePath$3),
165
+ useNode: group.runtime === "Node"
166
+ })) === "Modified") yield* logFileModified(modulePath$3);
155
167
  }));
156
168
  const extinctGroupPaths = GroupPaths.make(HashSet.difference(groupPathsFromFs, groupPathsFromSpec));
157
169
  yield* removeGroups(extinctGroupPaths);
@@ -179,27 +191,25 @@ const removeGroups = (groupPaths) => Effect.gen(function* () {
179
191
  const convexDirectory = yield* ConvexDirectory.get;
180
192
  yield* Effect.all(HashSet.map(groupPaths, (groupPath) => Effect.gen(function* () {
181
193
  const relativeModulePath = yield* modulePath(groupPath);
182
- const modulePath$3 = path.join(convexDirectory, relativeModulePath);
194
+ const modulePath$2 = path.join(convexDirectory, relativeModulePath);
183
195
  yield* Effect.logDebug(`Removing group '${relativeModulePath}'...`);
184
- yield* removePathIfExists(modulePath$3);
196
+ yield* removePathIfExists(modulePath$2);
185
197
  yield* Effect.logDebug(`Group '${relativeModulePath}' removed`);
186
198
  })), { concurrency: "unbounded" });
187
199
  });
188
200
  const writeGroups = (spec, groupPaths) => Effect.forEach(groupPaths, (groupPath) => Effect.gen(function* () {
189
201
  const path = yield* Path.Path;
190
202
  const convexDirectory = yield* ConvexDirectory.get;
191
- const confectDirectory = yield* ConfectDirectory.get;
192
- const functionNames = pipe((yield* getGroupSpec(spec, groupPath)).functions, Record.values, Array.sortBy(Order.mapInput(Order.string, (fn) => fn.name)), Array.map((fn) => fn.name));
203
+ const group = yield* getGroupSpec(spec, groupPath);
204
+ const functionNames = pipe(group.functions, Record.values, Array.sortBy(Order.mapInput(Order.string, (fn) => fn.name)), Array.map((fn) => fn.name));
193
205
  const relativeModulePath = yield* modulePath(groupPath);
194
- const modulePath$4 = path.join(convexDirectory, relativeModulePath);
195
- const registeredFunctionsPath = path.join(confectDirectory, "_generated", "registeredFunctions", ...groupPath.pathSegments) + ".ts";
196
- const registeredFunctionsImportPath = yield* toModuleImportPath(path.relative(path.dirname(modulePath$4), registeredFunctionsPath));
206
+ const registeredFunctionsImportPath = yield* registeredFunctionsImportPathForGroup(groupPath, path.join(convexDirectory, relativeModulePath));
197
207
  yield* Effect.logDebug(`Generating group ${groupPath}...`);
198
208
  yield* generateGroupModule({
199
209
  groupPath,
200
210
  functionNames,
201
211
  registeredFunctionsImportPath,
202
- useNode: groupPath.pathSegments[0] === "node"
212
+ useNode: group.runtime === "Node"
203
213
  });
204
214
  yield* Effect.logDebug(`Group ${groupPath} generated`);
205
215
  }));
@@ -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.crons","templates.authConfig"],"sources":["../src/utils.ts"],"sourcesContent":["import type { FunctionSpec, Spec } from \"@confect/core\";\nimport * as FileSystem from \"@effect/platform/FileSystem\";\nimport * as Path from \"@effect/platform/Path\";\nimport type { PlatformError } from \"@effect/platform/Error\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as Option from \"effect/Option\";\nimport * as Order from \"effect/Order\";\nimport * as Record from \"effect/Record\";\nimport * as Ref from \"effect/Ref\";\nimport * as String from \"effect/String\";\nimport * as FunctionPaths from \"./FunctionPaths\";\nimport * as GroupPath from \"./GroupPath\";\nimport * as GroupPaths from \"./GroupPaths\";\nimport { logFileAdded, logFileModified, logFileRemoved } from \"./log\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\nimport { ConvexDirectory } from \"./ConvexDirectory\";\nimport * as templates from \"./templates\";\n\n/**\n * Tracks whether the current codegen run wrote anything to disk. Set to\n * `true` by every helper that actually overwrites a file (skipping the\n * content-unchanged case). `codegenHandler` provides a fresh `Ref` per\n * run and reads it back to report `anyWritesHappened`; callers outside a\n * codegen pass hit the cached default `Ref` and need not interact with\n * the tracker.\n */\nexport class WriteTracker extends Context.Reference<WriteTracker>()(\n \"@confect/cli/WriteTracker\",\n { defaultValue: () => Ref.unsafeMake(false) },\n) {}\n\nconst markWritten = Effect.gen(function* () {\n const tracker = yield* WriteTracker;\n yield* Ref.set(tracker, true);\n});\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\n/** Ensures a relative path is a valid ESM/TS module specifier (e.g. `spec` → `./spec`). */\nexport const toModuleImportPath = (relativePath: string) =>\n Effect.gen(function* () {\n const withoutExt = yield* removePathExtension(relativePath);\n return withoutExt.startsWith(\".\") ? withoutExt : `./${withoutExt}`;\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* markWritten;\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* markWritten;\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 yield* markWritten;\n return \"Added\";\n }\n const existing = yield* fs.readFileString(filePath);\n if (existing !== contents) {\n yield* fs.writeFileString(filePath, contents);\n yield* markWritten;\n return \"Modified\";\n }\n return \"Unchanged\";\n });\n\nexport const removePathIfExists = (\n filePath: string,\n): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n\n if (!(yield* fs.exists(filePath))) {\n return;\n }\n\n yield* fs\n .remove(filePath)\n .pipe(\n Effect.catchTag(\"SystemError\", (error) =>\n error.reason === \"NotFound\" ? Effect.void : Effect.fail(error),\n ),\n );\n });\n\n/**\n * Bump the mtime of `convex/schema.ts` so the Convex CLI's chokidar watcher\n * emits a `change` event after every successful Confect codegen run. Without\n * this, a codegen that doesn't change any file content (for example,\n * recovering from a transient broken state in `confect/`) leaves Convex\n * stuck on its previous error because nothing it observes has changed.\n */\nexport const touchConvexSchema = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const schemaPath = path.join(convexDirectory, \"schema.ts\");\n\n if (!(yield* fs.exists(schemaPath))) {\n return;\n }\n\n const now = new Date();\n yield* fs.utimes(schemaPath, now, now);\n});\n\nexport const generateGroupModule = ({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n useNode = false,\n}: {\n groupPath: GroupPath.GroupPath;\n functionNames: string[];\n registeredFunctionsImportPath: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.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 functionsContentsString = yield* templates.functions({\n functionNames,\n registeredFunctionsImportPath,\n useNode,\n });\n\n if (!(yield* fs.exists(modulePath))) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n yield* markWritten;\n return \"Added\" as const;\n }\n const existing = yield* fs.readFileString(modulePath);\n if (existing !== functionsContentsString) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n yield* markWritten;\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 const confectDirectory = yield* ConfectDirectory.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 relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n const registrySegments =\n groupPath.pathSegments[0] === \"node\"\n ? groupPath.pathSegments.slice(1)\n : groupPath.pathSegments;\n const registeredFunctionsPath =\n path.join(\n confectDirectory,\n \"_generated\",\n \"registeredFunctions\",\n ...registrySegments,\n ) + \".ts\";\n const registeredFunctionsImportPath = yield* toModuleImportPath(\n path.relative(path.dirname(modulePath), registeredFunctionsPath),\n );\n const result = yield* generateGroupModule({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n useNode: groupPath.pathSegments[0] === \"node\",\n });\n if (result === \"Modified\") {\n yield* logFileModified(modulePath);\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 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* removePathIfExists(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 path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const confectDirectory = yield* ConfectDirectory.get;\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 const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n const registeredFunctionsPath =\n path.join(\n confectDirectory,\n \"_generated\",\n \"registeredFunctions\",\n ...groupPath.pathSegments,\n ) + \".ts\";\n const registeredFunctionsImportPath = yield* toModuleImportPath(\n path.relative(path.dirname(modulePath), registeredFunctionsPath),\n );\n\n yield* Effect.logDebug(`Generating group ${groupPath}...`);\n yield* generateGroupModule({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n useNode: groupPath.pathSegments[0] === \"node\",\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 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.crons","templates.authConfig"],"sources":["../src/utils.ts"],"sourcesContent":["import type { FunctionSpec, Spec } from \"@confect/core\";\nimport * as FileSystem from \"@effect/platform/FileSystem\";\nimport * as Path from \"@effect/platform/Path\";\nimport type { PlatformError } from \"@effect/platform/Error\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as Option from \"effect/Option\";\nimport * as Order from \"effect/Order\";\nimport * as Record from \"effect/Record\";\nimport * as Ref from \"effect/Ref\";\nimport * as String from \"effect/String\";\nimport * as FunctionPaths from \"./FunctionPaths\";\nimport * as GroupPath from \"./GroupPath\";\nimport * as GroupPaths from \"./GroupPaths\";\nimport { logFileAdded, logFileModified, logFileRemoved } from \"./log\";\nimport { ConfectDirectory } from \"./ConfectDirectory\";\nimport { ConvexDirectory } from \"./ConvexDirectory\";\nimport * as templates from \"./templates\";\n\n/**\n * Tracks whether the current codegen run wrote anything to disk. Set to\n * `true` by every helper that actually overwrites a file (skipping the\n * content-unchanged case). `codegenHandler` provides a fresh `Ref` per\n * run and reads it back to report `anyWritesHappened`; callers outside a\n * codegen pass hit the cached default `Ref` and need not interact with\n * the tracker.\n */\nexport class WriteTracker extends Context.Reference<WriteTracker>()(\n \"@confect/cli/WriteTracker\",\n { defaultValue: () => Ref.unsafeMake(false) },\n) {}\n\nconst markWritten = Effect.gen(function* () {\n const tracker = yield* WriteTracker;\n yield* Ref.set(tracker, true);\n});\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\n/** Ensures a relative path is a valid ESM/TS module specifier (e.g. `spec` → `./spec`). */\nexport const toModuleImportPath = (relativePath: string) =>\n Effect.gen(function* () {\n const withoutExt = yield* removePathExtension(relativePath);\n return withoutExt.startsWith(\".\") ? withoutExt : `./${withoutExt}`;\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* markWritten;\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* markWritten;\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 yield* markWritten;\n return \"Added\";\n }\n const existing = yield* fs.readFileString(filePath);\n if (existing !== contents) {\n yield* fs.writeFileString(filePath, contents);\n yield* markWritten;\n return \"Modified\";\n }\n return \"Unchanged\";\n });\n\nexport const removePathIfExists = (\n filePath: string,\n): Effect.Effect<void, PlatformError, FileSystem.FileSystem> =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n\n if (!(yield* fs.exists(filePath))) {\n return;\n }\n\n yield* fs\n .remove(filePath)\n .pipe(\n Effect.catchTag(\"SystemError\", (error) =>\n error.reason === \"NotFound\" ? Effect.void : Effect.fail(error),\n ),\n );\n });\n\n/**\n * Bump the mtime of `convex/schema.ts` so the Convex CLI's chokidar watcher\n * emits a `change` event after every successful Confect codegen run. Without\n * this, a codegen that doesn't change any file content (for example,\n * recovering from a transient broken state in `confect/`) leaves Convex\n * stuck on its previous error because nothing it observes has changed.\n */\nexport const touchConvexSchema = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const schemaPath = path.join(convexDirectory, \"schema.ts\");\n\n if (!(yield* fs.exists(schemaPath))) {\n return;\n }\n\n const now = new Date();\n yield* fs.utimes(schemaPath, now, now);\n});\n\nexport const generateGroupModule = ({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n useNode = false,\n}: {\n groupPath: GroupPath.GroupPath;\n functionNames: string[];\n registeredFunctionsImportPath: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.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 functionsContentsString = yield* templates.functions({\n functionNames,\n registeredFunctionsImportPath,\n useNode,\n });\n\n if (!(yield* fs.exists(modulePath))) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n yield* markWritten;\n return \"Added\" as const;\n }\n const existing = yield* fs.readFileString(modulePath);\n if (existing !== functionsContentsString) {\n yield* fs.writeFileString(modulePath, functionsContentsString);\n yield* markWritten;\n return \"Modified\" as const;\n }\n return \"Unchanged\" as const;\n });\n\n/**\n * Compute the module import specifier (relative to `modulePath`) for a group's\n * registry file under `confect/_generated/registeredFunctions/`. The registry\n * path mirrors the group's path one-to-one (see `registeredFunctionsRelativePath`\n * in `LeafModule.ts`) for both Convex and Node groups. Centralizing this here\n * keeps the \"overlapping\" and \"new\" group branches of `generateFunctions` from\n * drifting apart.\n */\nconst registeredFunctionsImportPathForGroup = (\n groupPath: GroupPath.GroupPath,\n modulePath: string,\n) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const registeredFunctionsPath =\n path.join(\n confectDirectory,\n \"_generated\",\n \"registeredFunctions\",\n ...groupPath.pathSegments,\n ) + \".ts\";\n\n return yield* toModuleImportPath(\n path.relative(path.dirname(modulePath), registeredFunctionsPath),\n );\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 relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n const registeredFunctionsImportPath =\n yield* registeredFunctionsImportPathForGroup(groupPath, modulePath);\n const result = yield* generateGroupModule({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n useNode: group.runtime === \"Node\",\n });\n if (result === \"Modified\") {\n yield* logFileModified(modulePath);\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 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* removePathIfExists(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 path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\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 const relativeModulePath = yield* GroupPath.modulePath(groupPath);\n const modulePath = path.join(convexDirectory, relativeModulePath);\n const registeredFunctionsImportPath =\n yield* registeredFunctionsImportPathForGroup(groupPath, modulePath);\n\n yield* Effect.logDebug(`Generating group ${groupPath}...`);\n yield* generateGroupModule({\n groupPath,\n functionNames,\n registeredFunctionsImportPath,\n useNode: group.runtime === \"Node\",\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 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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8BA,IAAa,eAAb,cAAkC,QAAQ,WAAyB,CACjE,6BACA,EAAE,oBAAoB,IAAI,WAAW,MAAM,EAAE,CAC9C,CAAC;AAEF,MAAM,cAAc,OAAO,IAAI,aAAa;CAC1C,MAAM,UAAU,OAAO;AACvB,QAAO,IAAI,IAAI,SAAS,KAAK;EAC7B;AAEF,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;;AAGJ,MAAa,sBAAsB,iBACjC,OAAO,IAAI,aAAa;CACtB,MAAM,aAAa,OAAO,oBAAoB,aAAa;AAC3D,QAAO,WAAW,WAAW,IAAI,GAAG,aAAa,KAAK;EACtD;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;AACP,SAAO,aAAa,SAAS;AAC7B;;AAGF,MADiB,OAAO,GAAG,eAAe,SAAS,MAClC,UAAU;AACzB,SAAO,GAAG,gBAAgB,UAAU,SAAS;AAC7C,SAAO;AACP,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;AACP,SAAO;;AAGT,MADiB,OAAO,GAAG,eAAe,SAAS,MAClC,UAAU;AACzB,SAAO,GAAG,gBAAgB,UAAU,SAAS;AAC7C,SAAO;AACP,SAAO;;AAET,QAAO;EACP;AAEJ,MAAa,sBACX,aAEA,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;AAE7B,KAAI,EAAE,OAAO,GAAG,OAAO,SAAS,EAC9B;AAGF,QAAO,GACJ,OAAO,SAAS,CAChB,KACC,OAAO,SAAS,gBAAgB,UAC9B,MAAM,WAAW,aAAa,OAAO,OAAO,OAAO,KAAK,MAAM,CAC/D,CACF;EACH;;;;;;;;AASJ,MAAa,oBAAoB,OAAO,IAAI,aAAa;CACvD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;CAC/C,MAAM,aAAa,KAAK,KAAK,iBAAiB,YAAY;AAE1D,KAAI,EAAE,OAAO,GAAG,OAAO,WAAW,EAChC;CAGF,MAAM,sBAAM,IAAI,MAAM;AACtB,QAAO,GAAG,OAAO,YAAY,KAAK,IAAI;EACtC;AAEF,MAAa,uBAAuB,EAClC,WACA,eACA,+BACA,UAAU,YAOV,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;CAE/C,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,0BAA0B,OAAOC,UAAoB;EACzD;EACA;EACA;EACD,CAAC;AAEF,KAAI,EAAE,OAAO,GAAG,OAAOD,aAAW,GAAG;AACnC,SAAO,GAAG,gBAAgBA,cAAY,wBAAwB;AAC9D,SAAO;AACP,SAAO;;AAGT,MADiB,OAAO,GAAG,eAAeA,aAAW,MACpC,yBAAyB;AACxC,SAAO,GAAG,gBAAgBA,cAAY,wBAAwB;AAC9D,SAAO;AACP,SAAO;;AAET,QAAO;EACP;;;;;;;;;AAUJ,MAAM,yCACJ,WACA,eAEA,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;CAEjD,MAAM,0BACJ,KAAK,KACH,kBACA,cACA,uBACA,GAAG,UAAU,aACd,GAAG;AAEN,QAAO,OAAO,mBACZ,KAAK,SAAS,KAAK,QAAQ,WAAW,EAAE,wBAAwB,CACjE;EACD;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;EACtB,MAAM,QAAQ,OAAOC,aAAuB,MAAM,UAAU;EAC5D,MAAM,gBAAgB,KACpB,MAAM,WACN,OAAO,QACP,MAAM,OACJ,MAAM,SACJ,MAAM,SACL,OAAkC,GAAG,KACvC,CACF,EACD,MAAM,KAAK,OAAO,GAAG,KAAK,CAC3B;EACD,MAAM,qBAAqB,OAAOL,WAAqB,UAAU;EACjE,MAAMC,eAAa,KAAK,KAAK,iBAAiB,mBAAmB;AASjE,OANe,OAAO,oBAAoB;GACxC;GACA;GACA,+BAJA,OAAO,sCAAsC,WAAWA,aAAW;GAKnE,SAAS,MAAM,YAAY;GAC5B,CAAC,MACa,WACb,QAAO,gBAAgBA,aAAW;GAEpC,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,oBACdK,oBAA8B,gBAAgB,CAC/C,CACF,EAC2B,QAAQ,yBAAoC,KAAK;EAC7E;AAEF,MAAa,gBAAgB,eAC3B,OAAO,IAAI,aAAa;CACtB,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,mBAAmBA,aAAW;AACrC,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;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,kBAAkB,OAAO,gBAAgB;CAC/C,MAAM,QAAQ,OAAOI,aAAuB,MAAM,UAAU;CAE5D,MAAM,gBAAgB,KACpB,MAAM,WACN,OAAO,QACP,MAAM,OACJ,MAAM,SACJ,MAAM,SACL,OAAkC,GAAG,KACvC,CACF,EACD,MAAM,KAAK,OAAO,GAAG,KAAK,CAC3B;CAED,MAAM,qBAAqB,OAAOL,WAAqB,UAAU;CAEjE,MAAM,gCACJ,OAAO,sCAAsC,WAF5B,KAAK,KAAK,iBAAiB,mBAAmB,CAEI;AAErE,QAAO,OAAO,SAAS,oBAAoB,UAAU,KAAK;AAC1D,QAAO,oBAAoB;EACzB;EACA;EACA;EACA,SAAS,MAAM,YAAY;EAC5B,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,eAAeO,KAAe,EAAE,gBAAgB,YAAY,CAAC,CAC/D;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,7 +1,7 @@
1
1
  {
2
2
  "name": "@confect/cli",
3
3
  "description": "Developer tooling for codegen and sync",
4
- "version": "9.0.0-next.8",
4
+ "version": "9.0.0-next.9",
5
5
  "author": "RJ Dellecese",
6
6
  "bin": {
7
7
  "confect": "./dist/index.mjs"
@@ -65,8 +65,8 @@
65
65
  "module": "./dist/index.js",
66
66
  "peerDependencies": {
67
67
  "effect": "^3.21.2",
68
- "@confect/core": "^9.0.0-next.8",
69
- "@confect/server": "^9.0.0-next.8"
68
+ "@confect/core": "^9.0.0-next.9",
69
+ "@confect/server": "^9.0.0-next.9"
70
70
  },
71
71
  "repository": {
72
72
  "type": "git",
@@ -33,15 +33,6 @@ export class SpecMissingDefaultGroupSpecError extends Schema.TaggedError<SpecMis
33
33
  },
34
34
  ) {}
35
35
 
36
- export class SpecRuntimeMismatchError extends Schema.TaggedError<SpecRuntimeMismatchError>()(
37
- "SpecRuntimeMismatchError",
38
- {
39
- specPath: Schema.String,
40
- expectedRuntime: Schema.Literal("Convex", "Node"),
41
- actualRuntime: Schema.Literal("Convex", "Node"),
42
- },
43
- ) {}
44
-
45
36
  export class ImplMissingSpecImportError extends Schema.TaggedError<ImplMissingSpecImportError>()(
46
37
  "ImplMissingSpecImportError",
47
38
  {
@@ -126,7 +117,6 @@ export const CodegenError = Schema.Union(
126
117
  MissingImplFileError,
127
118
  MissingSpecFileError,
128
119
  SpecMissingDefaultGroupSpecError,
129
- SpecRuntimeMismatchError,
130
120
  ImplMissingSpecImportError,
131
121
  ImplMissingDefaultLayerError,
132
122
  ImplNotFinalizedError,
@@ -198,26 +188,6 @@ const renderSpecMissingDefaultGroupSpecError = (
198
188
  ),
199
189
  );
200
190
 
201
- const renderSpecRuntimeMismatchError = (
202
- error: SpecRuntimeMismatchError,
203
- ): AnsiDoc.AnsiDoc => {
204
- const constructor =
205
- error.expectedRuntime === "Node"
206
- ? "GroupSpec.makeNode()"
207
- : "GroupSpec.make()";
208
- const moveHint =
209
- error.expectedRuntime === "Node"
210
- ? " or move the file into confect/node/."
211
- : " or move the file out of confect/node/.";
212
- return singleLine(
213
- AnsiDoc.text("Spec "),
214
- formatPathDoc(error.specPath),
215
- AnsiDoc.text(
216
- ` declares a ${error.actualRuntime} GroupSpec but its location requires ${error.expectedRuntime}; use ${constructor}${moveHint}`,
217
- ),
218
- );
219
- };
220
-
221
191
  const renderImplMissingSpecImportError = (
222
192
  error: ImplMissingSpecImportError,
223
193
  ): AnsiDoc.AnsiDoc => {
@@ -351,12 +321,6 @@ export const renderCodegenError = (error: CodegenError): string => {
351
321
  AnsiDoc.render({ style: "pretty" }),
352
322
  ),
353
323
  ),
354
- Match.tag("SpecRuntimeMismatchError", (e) =>
355
- pipe(
356
- renderSpecRuntimeMismatchError(e),
357
- AnsiDoc.render({ style: "pretty" }),
358
- ),
359
- ),
360
324
  Match.tag("ImplMissingSpecImportError", (e) =>
361
325
  pipe(
362
326
  renderImplMissingSpecImportError(e),
package/src/LeafModule.ts CHANGED
@@ -17,7 +17,6 @@ import {
17
17
  ImplMissingSpecImportError,
18
18
  ImplNotFinalizedError,
19
19
  SpecMissingDefaultGroupSpecError,
20
- SpecRuntimeMismatchError,
21
20
  } from "./CodegenError";
22
21
  import { ConfectDirectory } from "./ConfectDirectory";
23
22
  import { removePathExtension } from "./utils";
@@ -26,9 +25,15 @@ export interface LeafModule {
26
25
  readonly relativePath: string;
27
26
  readonly pathSegments: readonly [string, ...string[]];
28
27
  readonly groupPathDot: string;
29
- readonly registryGroupPathDot: string;
30
28
  readonly exportName: string;
31
- readonly runtime: "Convex" | "Node";
29
+ /**
30
+ * The runtime declared by the group's spec — `"Node"` for
31
+ * `GroupSpec.makeNode()`, `"Convex"` for `GroupSpec.make()`. `None` while the
32
+ * runtime is unknown: discovery (`toLeafModule`) works from the file path alone,
33
+ * which does not determine the runtime, so this is filled in once the spec has
34
+ * been bundled and validated (see `validateSpec`).
35
+ */
36
+ readonly runtime: Option.Option<"Convex" | "Node">;
32
37
  readonly specImportPath: string;
33
38
  }
34
39
 
@@ -104,24 +109,10 @@ export const specPathForImpl = (implRelativePath: string) =>
104
109
  export const implPathForSpec = (specRelativePath: string) =>
105
110
  swapModuleSuffix(specRelativePath, SPEC_SUFFIX, IMPL_SUFFIX);
106
111
 
107
- export const isNodeLeafModule = (relativePath: string) =>
108
- relativePath.startsWith("node/") || relativePath.startsWith("node\\");
109
-
110
- export const toNodeRegistryLeaf = (leaf: LeafModule): LeafModule => ({
111
- ...leaf,
112
- pathSegments: [leaf.exportName],
113
- groupPathDot: leaf.exportName,
114
- });
115
-
116
112
  export const registeredFunctionsRelativePath = (leaf: LeafModule) =>
117
113
  Effect.gen(function* () {
118
114
  const path = yield* Path.Path;
119
- return (
120
- path.join(
121
- "registeredFunctions",
122
- ...leaf.pathSegments.slice(leaf.runtime === "Node" ? 1 : 0),
123
- ) + ".ts"
124
- );
115
+ return path.join("registeredFunctions", ...leaf.pathSegments) + ".ts";
125
116
  });
126
117
 
127
118
  export const discoverLeafSpecFiles = Effect.gen(function* () {
@@ -177,15 +168,14 @@ export const toLeafModule = (specRelativePath: string) =>
177
168
  const { pathSegments, groupPathDot } =
178
169
  yield* groupPathFromRelativeModulePath(specRelativePath);
179
170
  const specImportPath = yield* specImportPathFromGenerated(specRelativePath);
180
- const runtime = isNodeLeafModule(specRelativePath) ? "Node" : "Convex";
181
171
 
182
172
  return {
183
173
  relativePath: specRelativePath,
184
174
  pathSegments,
185
175
  groupPathDot,
186
176
  exportName,
187
- runtime,
188
- registryGroupPathDot: runtime === "Node" ? exportName : groupPathDot,
177
+ // Unknown until the spec is bundled; see `LeafModule.runtime`.
178
+ runtime: Option.none(),
189
179
  specImportPath,
190
180
  } satisfies LeafModule;
191
181
  });
@@ -198,11 +188,11 @@ const absoluteModulePath = (relativePath: string) =>
198
188
  });
199
189
 
200
190
  /**
201
- * Validate that the leaf's spec file default-exports a `GroupSpec` whose
202
- * runtime matches the leaf's location (`Convex` for files outside
203
- * `confect/node/`, `Node` for files inside it). Returns the validated
204
- * `GroupSpec` so callers can avoid re-bundling for later inspection (e.g.
205
- * parent/child name-collision checks at codegen time).
191
+ * Validate that the leaf's spec file default-exports a `GroupSpec`. Returns the
192
+ * validated `GroupSpec` so callers can read its runtime and avoid re-bundling for
193
+ * later inspection (e.g. stamping `leaf.runtime` and parent/child name-collision
194
+ * checks at codegen time). The group's runtime (`Convex` vs `Node`) is whatever
195
+ * the spec declares it is not constrained by the file's location.
206
196
  */
207
197
  export const validateSpec = (leaf: LeafModule) =>
208
198
  Effect.gen(function* () {
@@ -219,14 +209,6 @@ export const validateSpec = (leaf: LeafModule) =>
219
209
  });
220
210
  }
221
211
 
222
- if (groupSpec.runtime !== leaf.runtime) {
223
- return yield* new SpecRuntimeMismatchError({
224
- specPath: leaf.relativePath,
225
- expectedRuntime: leaf.runtime,
226
- actualRuntime: groupSpec.runtime,
227
- });
228
- }
229
-
230
212
  return groupSpec;
231
213
  });
232
214
 
@@ -52,19 +52,6 @@ export const assemblyNodesFromLeaves = (
52
52
  leaves: ReadonlyArray<LeafModule>,
53
53
  ): ReadonlyArray<SpecAssemblyNode> => assemblyNodesAtDepth(leaves, 0);
54
54
 
55
- export const partitionByRuntime = (
56
- leaves: ReadonlyArray<LeafModule>,
57
- ): {
58
- readonly convex: ReadonlyArray<LeafModule>;
59
- readonly node: ReadonlyArray<LeafModule>;
60
- } => {
61
- const [node, convex] = Array.partition(
62
- leaves,
63
- (leaf) => leaf.runtime === "Convex",
64
- );
65
- return { convex, node };
66
- };
67
-
68
55
  const importBindingsForNode = (
69
56
  node: SpecAssemblyNode,
70
57
  ): ReadonlyArray<SpecImportBinding> =>