@confect/cli 9.0.0-next.0 → 9.0.0-next.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +351 -1
- package/dist/BuildError.mjs +9 -2
- package/dist/BuildError.mjs.map +1 -1
- package/dist/Bundler.mjs +67 -57
- package/dist/Bundler.mjs.map +1 -1
- package/dist/CodeBlockWriter.mjs +1 -1
- package/dist/CodeBlockWriter.mjs.map +1 -1
- package/dist/CodegenError.mjs +36 -21
- package/dist/CodegenError.mjs.map +1 -1
- package/dist/ConfectDirectory.mjs +5 -2
- package/dist/ConfectDirectory.mjs.map +1 -1
- package/dist/ConvexDirectory.mjs +6 -2
- package/dist/ConvexDirectory.mjs.map +1 -1
- package/dist/FunctionPath.mjs +1 -1
- package/dist/FunctionPath.mjs.map +1 -1
- package/dist/FunctionPaths.mjs +5 -1
- package/dist/FunctionPaths.mjs.map +1 -1
- package/dist/GroupPath.mjs +9 -2
- package/dist/GroupPath.mjs.map +1 -1
- package/dist/GroupPaths.mjs +1 -1
- package/dist/GroupPaths.mjs.map +1 -1
- package/dist/LeafModule.mjs +20 -24
- package/dist/LeafModule.mjs.map +1 -1
- package/dist/ProjectRoot.mjs +8 -3
- package/dist/ProjectRoot.mjs.map +1 -1
- package/dist/SpecAssemblyNode.mjs +9 -11
- package/dist/SpecAssemblyNode.mjs.map +1 -1
- package/dist/TableModule.mjs +94 -0
- package/dist/TableModule.mjs.map +1 -0
- package/dist/cliApp.mjs +1 -1
- package/dist/cliApp.mjs.map +1 -1
- package/dist/confect/codegen.mjs +272 -141
- package/dist/confect/codegen.mjs.map +1 -1
- package/dist/confect/dev.mjs +36 -17
- package/dist/confect/dev.mjs.map +1 -1
- package/dist/confect.mjs +2 -2
- package/dist/confect.mjs.map +1 -1
- package/dist/index.mjs +3 -2
- package/dist/index.mjs.map +1 -1
- package/dist/log.mjs +9 -4
- package/dist/log.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/templates.mjs +139 -48
- package/dist/templates.mjs.map +1 -1
- package/dist/utils.mjs +42 -22
- package/dist/utils.mjs.map +1 -1
- package/package.json +33 -50
- package/dist/index.d.mts +0 -1
- package/src/BuildError.ts +0 -210
- package/src/Bundler.ts +0 -144
- package/src/CodeBlockWriter.ts +0 -65
- package/src/CodegenError.ts +0 -344
- package/src/ConfectDirectory.ts +0 -42
- package/src/ConvexDirectory.ts +0 -68
- package/src/FunctionPath.ts +0 -27
- package/src/FunctionPaths.ts +0 -103
- package/src/GroupPath.ts +0 -118
- package/src/GroupPaths.ts +0 -7
- package/src/LeafModule.ts +0 -313
- package/src/ProjectRoot.ts +0 -50
- package/src/SpecAssemblyNode.ts +0 -82
- package/src/cliApp.ts +0 -8
- package/src/confect/codegen.ts +0 -589
- package/src/confect/dev.ts +0 -749
- package/src/confect.ts +0 -19
- package/src/index.ts +0 -22
- package/src/log.ts +0 -104
- package/src/templates.ts +0 -477
- package/src/utils.ts +0 -429
package/dist/confect/codegen.mjs
CHANGED
|
@@ -1,35 +1,47 @@
|
|
|
1
|
-
import { logFileAdded, logFileModified, logFileRemoved, logPending, logSuccess } from "../log.mjs";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
1
|
+
import { logFileAdded, logFileModified, logFileRemoved, logPending, logSuccess, logWarn } from "../log.mjs";
|
|
2
|
+
import { bundle } from "../Bundler.mjs";
|
|
3
|
+
import { LegacySchemaFileError, MissingImplFileError, MissingSpecFileError, ParentChildNameCollisionError, tapAndLog } from "../CodegenError.mjs";
|
|
4
4
|
import { ConvexDirectory } from "../ConvexDirectory.mjs";
|
|
5
5
|
import { ConfectDirectory } from "../ConfectDirectory.mjs";
|
|
6
6
|
import { FunctionPaths, make } from "../FunctionPaths.mjs";
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
13
|
-
import
|
|
7
|
+
import { assemblyNodesFromLeaves } from "../SpecAssemblyNode.mjs";
|
|
8
|
+
import { assembledSpec, convexSchema, id, refs, registeredFunctionsForGroup, runtimeSchema, schema, services, tableWrapper } from "../templates.mjs";
|
|
9
|
+
import { WriteTracker, generateAuthConfig, generateCrons, generateFunctions, generateHttp, removePathIfExists, toModuleImportPath, touchConvexSchema, writeFileStringAndLog } from "../utils.mjs";
|
|
10
|
+
import { discoverLeafImplFiles, discoverLeafSpecFiles, implPathForSpec, registeredFunctionsRelativePath, specPathForImpl, toLeafModule, validateImpl, validateSpec } from "../LeafModule.mjs";
|
|
11
|
+
import { TABLES_DIRNAME, discover, validate } from "../TableModule.mjs";
|
|
12
|
+
import * as Effect from "effect/Effect";
|
|
13
|
+
import * as Command from "@effect/cli/Command";
|
|
14
14
|
import { Spec } from "@confect/core";
|
|
15
|
-
import * as
|
|
16
|
-
import
|
|
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";
|
|
17
23
|
|
|
18
24
|
//#region src/confect/codegen.ts
|
|
19
|
-
const
|
|
20
|
-
const
|
|
21
|
-
const
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
25
|
+
const GENERATED_DIRNAME = "_generated";
|
|
26
|
+
const GENERATED_SPEC_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "spec.ts"));
|
|
27
|
+
const GENERATED_SCHEMA_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "schema.ts"));
|
|
28
|
+
const GENERATED_CONVEX_SCHEMA_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "convexSchema.ts"));
|
|
29
|
+
const GENERATED_ID_PATH = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "id.ts"));
|
|
30
|
+
const GENERATED_TABLES_DIRNAME = Effect.andThen(Path.Path, (path) => path.join(GENERATED_DIRNAME, "tables"));
|
|
31
|
+
const LEGACY_PATHS = Effect.gen(function* () {
|
|
32
|
+
const path = yield* Path.Path;
|
|
33
|
+
return [
|
|
34
|
+
"spec.ts",
|
|
35
|
+
"nodeSpec.ts",
|
|
36
|
+
"impl.ts",
|
|
37
|
+
"nodeImpl.ts",
|
|
38
|
+
path.join(GENERATED_DIRNAME, "registeredFunctions.ts"),
|
|
39
|
+
path.join(GENERATED_DIRNAME, "nodeRegisteredFunctions.ts"),
|
|
40
|
+
path.join(GENERATED_DIRNAME, "impl.ts"),
|
|
41
|
+
path.join(GENERATED_DIRNAME, "nodeImpl.ts"),
|
|
42
|
+
path.join(GENERATED_DIRNAME, "nodeSpec.ts")
|
|
43
|
+
];
|
|
44
|
+
});
|
|
33
45
|
const codegen = Command.make("codegen", {}, () => Effect.gen(function* () {
|
|
34
46
|
yield* logPending("Performing initial sync…");
|
|
35
47
|
yield* codegenHandler.pipe(Effect.asVoid, Effect.tap(() => logSuccess("Generated files are up-to-date")), tapAndLog);
|
|
@@ -43,22 +55,31 @@ const codegenHandler = Effect.gen(function* () {
|
|
|
43
55
|
});
|
|
44
56
|
const runCodegen = Effect.gen(function* () {
|
|
45
57
|
yield* generateConfectGeneratedDirectory;
|
|
46
|
-
yield*
|
|
47
|
-
const
|
|
58
|
+
yield* rejectLegacySchemaFile;
|
|
59
|
+
const tableModules = yield* discover;
|
|
60
|
+
yield* warnIfNoTables(tableModules);
|
|
61
|
+
yield* generateIdConstructor(tableModules);
|
|
62
|
+
yield* validate(tableModules);
|
|
63
|
+
yield* generateTableWrappers(tableModules);
|
|
64
|
+
yield* removeObsoleteTableWrappers(tableModules);
|
|
65
|
+
yield* generateRuntimeSchema(tableModules);
|
|
66
|
+
const { leaves, groupSpecsByRelativePath } = yield* loadAndValidateLeafModules;
|
|
48
67
|
yield* removeLegacyFiles;
|
|
68
|
+
yield* validateNoParentChildNameCollisions(leaves, groupSpecsByRelativePath);
|
|
49
69
|
yield* generateAssembledSpecs(leaves);
|
|
50
|
-
yield* validateImplModules(leaves);
|
|
51
|
-
yield* generateGroupRegisteredFunctions(leaves);
|
|
52
|
-
yield* removeObsoleteRegisteredFunctions(leaves);
|
|
53
70
|
yield* Effect.all([
|
|
54
|
-
|
|
71
|
+
removeGeneratedApi,
|
|
55
72
|
generateRefs,
|
|
56
|
-
|
|
57
|
-
generateServices
|
|
73
|
+
removeGeneratedNodeApi,
|
|
74
|
+
generateServices,
|
|
75
|
+
generateConvexSchema(tableModules)
|
|
58
76
|
], { concurrency: "unbounded" });
|
|
77
|
+
yield* validateImplModules(leaves);
|
|
78
|
+
yield* generateGroupRegisteredFunctions(leaves);
|
|
79
|
+
yield* removeObsoleteRegisteredFunctions(leaves);
|
|
59
80
|
const [functionPaths] = yield* Effect.all([
|
|
60
81
|
generateFunctionModules,
|
|
61
|
-
|
|
82
|
+
generateConvexSchemaReexport,
|
|
62
83
|
logGenerated(generateHttp),
|
|
63
84
|
logGenerated(generateCrons),
|
|
64
85
|
logGenerated(generateAuthConfig)
|
|
@@ -80,20 +101,90 @@ const loadAndValidateLeafModules = Effect.gen(function* () {
|
|
|
80
101
|
const path = yield* Path.Path;
|
|
81
102
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
82
103
|
const specFiles = yield* discoverLeafSpecFiles;
|
|
83
|
-
const
|
|
84
|
-
const
|
|
85
|
-
yield* validateSpec(
|
|
104
|
+
const results = yield* Effect.forEach(specFiles, (specRelativePath) => Effect.gen(function* () {
|
|
105
|
+
const discovered = yield* toLeafModule(specRelativePath);
|
|
106
|
+
const groupSpec = yield* validateSpec(discovered);
|
|
107
|
+
const leaf = {
|
|
108
|
+
...discovered,
|
|
109
|
+
runtime: Option.some(groupSpec.runtime)
|
|
110
|
+
};
|
|
86
111
|
const implRelativePath = yield* implPathForSpec(specRelativePath);
|
|
87
112
|
const implAbsolutePath = path.join(confectDirectory, implRelativePath);
|
|
88
113
|
if (!(yield* fs.exists(implAbsolutePath))) return yield* new MissingImplFileError({
|
|
89
114
|
specPath: specRelativePath,
|
|
90
115
|
expectedImplPath: implRelativePath
|
|
91
116
|
});
|
|
92
|
-
return
|
|
117
|
+
return {
|
|
118
|
+
leaf,
|
|
119
|
+
groupSpec
|
|
120
|
+
};
|
|
93
121
|
}));
|
|
94
122
|
yield* validateOrphanImpls(specFiles);
|
|
95
|
-
return
|
|
123
|
+
return {
|
|
124
|
+
leaves: Array.map(results, ({ leaf }) => leaf),
|
|
125
|
+
groupSpecsByRelativePath: new Map(Array.map(results, ({ leaf, groupSpec }) => [leaf.relativePath, groupSpec]))
|
|
126
|
+
};
|
|
127
|
+
});
|
|
128
|
+
/**
|
|
129
|
+
* Walk the assembly tree and fail with a {@link ParentChildNameCollisionError}
|
|
130
|
+
* when a parent leaf declares a function or subgroup whose name matches a
|
|
131
|
+
* sibling subdirectory spec's segment. Without this check the colliding
|
|
132
|
+
* descendant would overwrite the parent's entry in the assembled
|
|
133
|
+
* `GroupSpec.groups` map at runtime, surfacing as a confusing
|
|
134
|
+
* `Refs.make` error rather than a codegen-time diagnostic.
|
|
135
|
+
*/
|
|
136
|
+
const validateNoParentChildNameCollisions = (leaves, groupSpecsByRelativePath) => Effect.gen(function* () {
|
|
137
|
+
const nodes = assemblyNodesFromLeaves(leaves);
|
|
138
|
+
yield* Effect.forEach(nodes, (n) => checkAssemblyNodeForCollisions(n, groupSpecsByRelativePath));
|
|
139
|
+
});
|
|
140
|
+
const checkAssemblyNodeForCollisions = (node, groupSpecsByRelativePath) => Effect.gen(function* () {
|
|
141
|
+
yield* Option.match(node.importBinding, {
|
|
142
|
+
onNone: () => Effect.void,
|
|
143
|
+
onSome: (binding) => Effect.gen(function* () {
|
|
144
|
+
if (node.children.length === 0) return;
|
|
145
|
+
const parentRelativePath = bindingToRelativeSpecPath(binding.importPath);
|
|
146
|
+
const parentGroupSpec = groupSpecsByRelativePath.get(parentRelativePath);
|
|
147
|
+
if (parentGroupSpec === void 0) return;
|
|
148
|
+
yield* Effect.forEach(node.children, (child) => {
|
|
149
|
+
if (Object.prototype.hasOwnProperty.call(parentGroupSpec.functions, child.segment)) return Effect.fail(new ParentChildNameCollisionError({
|
|
150
|
+
parentSpecPath: parentRelativePath,
|
|
151
|
+
childSpecPath: childRepresentativeSpecPath(child),
|
|
152
|
+
collisionName: child.segment,
|
|
153
|
+
collisionKind: "function"
|
|
154
|
+
}));
|
|
155
|
+
if (Object.prototype.hasOwnProperty.call(parentGroupSpec.groups, child.segment)) return Effect.fail(new ParentChildNameCollisionError({
|
|
156
|
+
parentSpecPath: parentRelativePath,
|
|
157
|
+
childSpecPath: childRepresentativeSpecPath(child),
|
|
158
|
+
collisionName: child.segment,
|
|
159
|
+
collisionKind: "group"
|
|
160
|
+
}));
|
|
161
|
+
return Effect.void;
|
|
162
|
+
});
|
|
163
|
+
})
|
|
164
|
+
});
|
|
165
|
+
yield* Effect.forEach(node.children, (child) => checkAssemblyNodeForCollisions(child, groupSpecsByRelativePath));
|
|
96
166
|
});
|
|
167
|
+
/**
|
|
168
|
+
* `LeafModule.specImportPath` is the import path used from inside the
|
|
169
|
+
* generated `_generated/spec.ts` (e.g. `"../notes.spec"`). Strip the
|
|
170
|
+
* `../` prefix and re-add the `.ts` extension to recover the leaf's
|
|
171
|
+
* confect-relative spec path used as the key in
|
|
172
|
+
* `groupSpecsByRelativePath`.
|
|
173
|
+
*/
|
|
174
|
+
const bindingToRelativeSpecPath = (importPath) => {
|
|
175
|
+
return `${importPath.startsWith("../") ? importPath.slice(3) : importPath}.ts`;
|
|
176
|
+
};
|
|
177
|
+
/**
|
|
178
|
+
* A child assembly node may itself be a parent without a leaf (when the
|
|
179
|
+
* actual leaves live only in deeper subdirectories). In that case we
|
|
180
|
+
* surface the first descendant leaf as a representative path so the
|
|
181
|
+
* error message points at something the user actually wrote.
|
|
182
|
+
*/
|
|
183
|
+
const childRepresentativeSpecPath = (node) => {
|
|
184
|
+
if (Option.isSome(node.importBinding)) return bindingToRelativeSpecPath(node.importBinding.value.importPath);
|
|
185
|
+
for (const child of node.children) return childRepresentativeSpecPath(child);
|
|
186
|
+
return node.segment;
|
|
187
|
+
};
|
|
97
188
|
const validateOrphanImpls = (specFiles) => Effect.gen(function* () {
|
|
98
189
|
const fs = yield* FileSystem.FileSystem;
|
|
99
190
|
const path = yield* Path.Path;
|
|
@@ -114,7 +205,8 @@ const removeLegacyFiles = Effect.gen(function* () {
|
|
|
114
205
|
const fs = yield* FileSystem.FileSystem;
|
|
115
206
|
const path = yield* Path.Path;
|
|
116
207
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
117
|
-
|
|
208
|
+
const legacyPaths = yield* LEGACY_PATHS;
|
|
209
|
+
yield* Effect.forEach(legacyPaths, (relativePath) => Effect.gen(function* () {
|
|
118
210
|
const absolutePath = path.join(confectDirectory, relativePath);
|
|
119
211
|
if (yield* fs.exists(absolutePath)) {
|
|
120
212
|
yield* removePathIfExists(absolutePath);
|
|
@@ -125,23 +217,10 @@ const removeLegacyFiles = Effect.gen(function* () {
|
|
|
125
217
|
const generateAssembledSpecs = (leaves) => Effect.gen(function* () {
|
|
126
218
|
const path = yield* Path.Path;
|
|
127
219
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
nodes,
|
|
133
|
-
runtime: "Convex"
|
|
134
|
-
});
|
|
135
|
-
yield* writeFileStringAndLog(path.join(confectDirectory, GENERATED_SPEC_PATH), specContents);
|
|
136
|
-
}
|
|
137
|
-
if (node.length > 0) {
|
|
138
|
-
const nodes = assemblyNodesFromLeaves(Array.map(node, toNodeRegistryLeaf));
|
|
139
|
-
const nodeSpecContents = yield* assembledSpec({
|
|
140
|
-
nodes,
|
|
141
|
-
runtime: "Node"
|
|
142
|
-
});
|
|
143
|
-
yield* writeFileStringAndLog(path.join(confectDirectory, GENERATED_NODE_SPEC_PATH), nodeSpecContents);
|
|
144
|
-
}
|
|
220
|
+
const generatedSpecPath = yield* GENERATED_SPEC_PATH;
|
|
221
|
+
const nodes = assemblyNodesFromLeaves(leaves);
|
|
222
|
+
const specContents = yield* assembledSpec({ nodes });
|
|
223
|
+
yield* writeFileStringAndLog(path.join(confectDirectory, generatedSpecPath), specContents);
|
|
145
224
|
});
|
|
146
225
|
const validateImplModules = (leaves) => Effect.forEach(leaves, validateImpl);
|
|
147
226
|
const generateGroupRegisteredFunctions = (leaves) => Effect.gen(function* () {
|
|
@@ -154,15 +233,19 @@ const generateGroupRegisteredFunctions = (leaves) => Effect.gen(function* () {
|
|
|
154
233
|
const fs = yield* FileSystem.FileSystem;
|
|
155
234
|
if (!(yield* fs.exists(registryDir))) yield* fs.makeDirectory(registryDir, { recursive: true });
|
|
156
235
|
const implRelativePath = yield* implPathForSpec(leaf.relativePath);
|
|
157
|
-
const
|
|
158
|
-
const
|
|
236
|
+
const schemaImportPath = yield* toModuleImportPath(path.relative(path.dirname(registryPath), path.join(confectDirectory, "_generated", "schema.ts")));
|
|
237
|
+
const specImportPath = yield* toModuleImportPath(path.relative(path.dirname(registryPath), path.join(confectDirectory, leaf.relativePath)));
|
|
159
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
|
+
});
|
|
160
243
|
yield* writeFileStringAndLog(registryPath, yield* registeredFunctionsForGroup({
|
|
161
|
-
|
|
162
|
-
|
|
244
|
+
schemaImportPath,
|
|
245
|
+
specImportPath,
|
|
163
246
|
implImportPath,
|
|
164
247
|
layerExportName: leaf.exportName,
|
|
165
|
-
useNode:
|
|
248
|
+
useNode: runtime === "Node"
|
|
166
249
|
}));
|
|
167
250
|
}));
|
|
168
251
|
});
|
|
@@ -190,130 +273,178 @@ const removeObsoleteRegisteredFunctions = (leaves) => Effect.gen(function* () {
|
|
|
190
273
|
const getGeneratedSpecPath = Effect.gen(function* () {
|
|
191
274
|
const path = yield* Path.Path;
|
|
192
275
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
const getGeneratedNodeSpecPath = Effect.gen(function* () {
|
|
196
|
-
const path = yield* Path.Path;
|
|
197
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
198
|
-
return path.join(confectDirectory, GENERATED_NODE_SPEC_PATH);
|
|
276
|
+
const generatedSpecPath = yield* GENERATED_SPEC_PATH;
|
|
277
|
+
return path.join(confectDirectory, generatedSpecPath);
|
|
199
278
|
});
|
|
200
279
|
const loadGeneratedSpec = Effect.gen(function* () {
|
|
201
280
|
const specPath = yield* getGeneratedSpecPath;
|
|
202
281
|
const { module: specModule } = yield* bundle(specPath);
|
|
203
282
|
const spec = specModule.default;
|
|
204
|
-
if (!Spec.
|
|
283
|
+
if (!Spec.isSpec(spec)) return yield* Effect.dieMessage("_generated/spec.ts does not export a valid Spec");
|
|
205
284
|
return spec;
|
|
206
285
|
});
|
|
207
|
-
const loadGeneratedNodeSpec = Effect.gen(function* () {
|
|
208
|
-
const fs = yield* FileSystem.FileSystem;
|
|
209
|
-
const nodeSpecPath = yield* getGeneratedNodeSpecPath;
|
|
210
|
-
if (!(yield* fs.exists(nodeSpecPath))) return Option.none();
|
|
211
|
-
const { module: nodeSpecModule } = yield* bundle(nodeSpecPath);
|
|
212
|
-
const nodeSpec = nodeSpecModule.default;
|
|
213
|
-
if (!Spec.isNodeSpec(nodeSpec)) return yield* Effect.dieMessage("_generated/nodeSpec.ts does not export a valid Node Spec");
|
|
214
|
-
return Option.some(nodeSpec);
|
|
215
|
-
});
|
|
216
286
|
const emptyFunctionPaths = FunctionPaths.make(HashSet.empty());
|
|
217
287
|
const loadPreviousFunctionPaths = Effect.gen(function* () {
|
|
218
288
|
const fs = yield* FileSystem.FileSystem;
|
|
219
289
|
const specPath = yield* getGeneratedSpecPath;
|
|
220
290
|
if (!(yield* fs.exists(specPath))) return emptyFunctionPaths;
|
|
221
291
|
const specEither = yield* loadGeneratedSpec.pipe(Effect.either);
|
|
222
|
-
return
|
|
223
|
-
onLeft: () =>
|
|
224
|
-
onRight: (spec) =>
|
|
225
|
-
const nodeSpecOption = yield* loadGeneratedNodeSpec;
|
|
226
|
-
const mergedSpec = Option.match(nodeSpecOption, {
|
|
227
|
-
onNone: () => spec,
|
|
228
|
-
onSome: (nodeSpec) => Spec.merge(spec, nodeSpec)
|
|
229
|
-
});
|
|
230
|
-
return make(mergedSpec);
|
|
231
|
-
})
|
|
292
|
+
return Either.match(specEither, {
|
|
293
|
+
onLeft: () => emptyFunctionPaths,
|
|
294
|
+
onRight: (spec) => make(spec)
|
|
232
295
|
});
|
|
233
296
|
});
|
|
234
|
-
|
|
297
|
+
/**
|
|
298
|
+
* Remove a now-obsolete `_generated/<name>.ts` if present (and log it), for
|
|
299
|
+
* projects upgrading from a version that still emitted it.
|
|
300
|
+
*/
|
|
301
|
+
const removeObsoleteGeneratedFile = (fileName) => Effect.gen(function* () {
|
|
302
|
+
const fs = yield* FileSystem.FileSystem;
|
|
235
303
|
const path = yield* Path.Path;
|
|
236
304
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
237
|
-
const
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
schemaImportPath,
|
|
243
|
-
specImportPath
|
|
244
|
-
}));
|
|
305
|
+
const filePath = path.join(confectDirectory, "_generated", fileName);
|
|
306
|
+
if (yield* fs.exists(filePath)) {
|
|
307
|
+
yield* removePathIfExists(filePath);
|
|
308
|
+
yield* logFileRemoved(filePath);
|
|
309
|
+
}
|
|
245
310
|
});
|
|
246
|
-
const
|
|
311
|
+
const removeGeneratedApi = removeObsoleteGeneratedFile("api.ts");
|
|
312
|
+
const removeGeneratedNodeApi = removeObsoleteGeneratedFile("nodeApi.ts");
|
|
313
|
+
const generateFunctionModules = Effect.gen(function* () {
|
|
314
|
+
return yield* generateFunctions(yield* loadGeneratedSpec);
|
|
315
|
+
});
|
|
316
|
+
/**
|
|
317
|
+
* The user-authored `confect/schema.ts` is no longer supported: codegen now
|
|
318
|
+
* owns both `_generated/schema.ts` (runtime) and `_generated/convexSchema.ts`
|
|
319
|
+
* (deploy), derived from a single scan of `confect/tables/*.ts`. Detect a
|
|
320
|
+
* stray file and fail with a clear migration message — leaving it in place
|
|
321
|
+
* would silently shadow the codegen-owned `_generated/schema.ts` /
|
|
322
|
+
* `_generated/convexSchema.ts`.
|
|
323
|
+
*/
|
|
324
|
+
const rejectLegacySchemaFile = Effect.gen(function* () {
|
|
247
325
|
const fs = yield* FileSystem.FileSystem;
|
|
248
326
|
const path = yield* Path.Path;
|
|
249
327
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
250
|
-
const
|
|
251
|
-
|
|
252
|
-
if (!(yield* fs.exists(nodeSpecPath))) {
|
|
253
|
-
if (yield* fs.exists(nodeApiPath)) {
|
|
254
|
-
yield* removePathIfExists(nodeApiPath);
|
|
255
|
-
yield* logFileRemoved(nodeApiPath);
|
|
256
|
-
}
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
const nodeApiDir = path.dirname(nodeApiPath);
|
|
260
|
-
const schemaImportPath = yield* toModuleImportPath(path.relative(nodeApiDir, path.join(confectDirectory, "schema.ts")));
|
|
261
|
-
const nodeSpecImportPath = yield* toModuleImportPath(path.relative(nodeApiDir, nodeSpecPath));
|
|
262
|
-
yield* writeFileStringAndLog(nodeApiPath, yield* nodeApi({
|
|
263
|
-
schemaImportPath,
|
|
264
|
-
nodeSpecImportPath
|
|
265
|
-
}));
|
|
328
|
+
const legacyPath = path.join(confectDirectory, "schema.ts");
|
|
329
|
+
if (yield* fs.exists(legacyPath)) return yield* new LegacySchemaFileError({ schemaPath: "schema.ts" });
|
|
266
330
|
});
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
331
|
+
/**
|
|
332
|
+
* Surface a yellow `⚠` warning when codegen sees no tables — either the
|
|
333
|
+
* `confect/tables/` directory is missing or it contains no `.ts` files.
|
|
334
|
+
* Generation still succeeds (emitting an empty `DatabaseSchema` and
|
|
335
|
+
* `defineSchema({})`), since action-only / table-free Confect backends
|
|
336
|
+
* are legal — but the warning catches the much more common case of a
|
|
337
|
+
* typoed directory or files placed under the wrong root.
|
|
338
|
+
*/
|
|
339
|
+
const warnIfNoTables = (tableModules) => tableModules.length === 0 ? logWarn(`No tables discovered in \`confect/${TABLES_DIRNAME}/\`. Generating an empty schema; add a \`Table.make(...)\` module under that directory unless this backend is intentionally tables-free.`) : Effect.void;
|
|
340
|
+
const tableModuleBindings = (tableModules, generatedFilePath) => Effect.gen(function* () {
|
|
341
|
+
const path = yield* Path.Path;
|
|
342
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
343
|
+
const generatedDir = path.dirname(generatedFilePath);
|
|
344
|
+
const generatedTablesDirname = yield* GENERATED_TABLES_DIRNAME;
|
|
345
|
+
return yield* Effect.forEach(tableModules, (tm) => Effect.gen(function* () {
|
|
346
|
+
const wrapperAbsolutePath = path.join(confectDirectory, generatedTablesDirname, `${tm.tableName}.ts`);
|
|
347
|
+
return {
|
|
348
|
+
importPath: yield* toModuleImportPath(path.relative(generatedDir, wrapperAbsolutePath)),
|
|
349
|
+
tableName: tm.tableName
|
|
350
|
+
};
|
|
273
351
|
}));
|
|
274
352
|
});
|
|
275
|
-
const
|
|
353
|
+
const generateIdConstructor = (tableModules) => Effect.gen(function* () {
|
|
354
|
+
const path = yield* Path.Path;
|
|
355
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
356
|
+
const generatedIdPath = yield* GENERATED_ID_PATH;
|
|
357
|
+
const idPath = path.join(confectDirectory, generatedIdPath);
|
|
358
|
+
const tableNames = tableModules.map((tm) => tm.tableName);
|
|
359
|
+
yield* writeFileStringAndLog(idPath, yield* id({ tableNames }));
|
|
360
|
+
});
|
|
361
|
+
const generateTableWrappers = (tableModules) => Effect.gen(function* () {
|
|
276
362
|
const fs = yield* FileSystem.FileSystem;
|
|
277
363
|
const path = yield* Path.Path;
|
|
278
364
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
yield*
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
365
|
+
const generatedTablesDirname = yield* GENERATED_TABLES_DIRNAME;
|
|
366
|
+
const wrappersDir = path.join(confectDirectory, generatedTablesDirname);
|
|
367
|
+
if (!(yield* fs.exists(wrappersDir))) yield* fs.makeDirectory(wrappersDir, { recursive: true });
|
|
368
|
+
yield* Effect.forEach(tableModules, (tm) => Effect.gen(function* () {
|
|
369
|
+
const wrapperPath = path.join(confectDirectory, generatedTablesDirname, `${tm.tableName}.ts`);
|
|
370
|
+
const unnamedAbsolutePath = path.join(confectDirectory, tm.relativePath);
|
|
371
|
+
const unnamedImportPath = yield* toModuleImportPath(path.relative(path.dirname(wrapperPath), unnamedAbsolutePath));
|
|
372
|
+
yield* writeFileStringAndLog(wrapperPath, yield* tableWrapper({
|
|
373
|
+
tableName: tm.tableName,
|
|
374
|
+
unnamedImportPath
|
|
375
|
+
}));
|
|
376
|
+
}), { concurrency: "unbounded" });
|
|
377
|
+
});
|
|
378
|
+
/**
|
|
379
|
+
* Remove any stale `_generated/tables/*.ts` wrapper whose source table
|
|
380
|
+
* has been deleted or renamed. Mirrors `removeObsoleteRegisteredFunctions`
|
|
381
|
+
* for the wrapper directory.
|
|
382
|
+
*/
|
|
383
|
+
const removeObsoleteTableWrappers = (tableModules) => Effect.gen(function* () {
|
|
384
|
+
const fs = yield* FileSystem.FileSystem;
|
|
385
|
+
const path = yield* Path.Path;
|
|
386
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
387
|
+
const generatedTablesDirname = yield* GENERATED_TABLES_DIRNAME;
|
|
388
|
+
const wrappersDir = path.join(confectDirectory, generatedTablesDirname);
|
|
389
|
+
if (!(yield* fs.exists(wrappersDir))) return;
|
|
390
|
+
const expected = new Set(tableModules.map((tm) => `${tm.tableName}.ts`));
|
|
391
|
+
const existing = yield* fs.readDirectory(wrappersDir, { recursive: true });
|
|
392
|
+
yield* Effect.forEach(existing, (entry) => {
|
|
393
|
+
if (path.extname(entry) !== ".ts") return Effect.void;
|
|
394
|
+
if (!expected.has(entry)) return Effect.gen(function* () {
|
|
395
|
+
const absolutePath = path.join(wrappersDir, entry);
|
|
396
|
+
if (yield* fs.exists(absolutePath)) {
|
|
397
|
+
yield* removePathIfExists(absolutePath);
|
|
398
|
+
yield* logFileRemoved(absolutePath);
|
|
399
|
+
}
|
|
400
|
+
});
|
|
401
|
+
return Effect.void;
|
|
402
|
+
});
|
|
285
403
|
});
|
|
286
|
-
const
|
|
404
|
+
const generateRuntimeSchema = (tableModules) => Effect.gen(function* () {
|
|
405
|
+
const path = yield* Path.Path;
|
|
406
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
407
|
+
const generatedSchemaPath = yield* GENERATED_SCHEMA_PATH;
|
|
408
|
+
const schemaPath = path.join(confectDirectory, generatedSchemaPath);
|
|
409
|
+
const bindings = yield* tableModuleBindings(tableModules, schemaPath);
|
|
410
|
+
yield* writeFileStringAndLog(schemaPath, yield* runtimeSchema({ tableModules: bindings }));
|
|
411
|
+
});
|
|
412
|
+
const generateConvexSchema = (tableModules) => Effect.gen(function* () {
|
|
413
|
+
const path = yield* Path.Path;
|
|
414
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
415
|
+
const generatedConvexSchemaPath = yield* GENERATED_CONVEX_SCHEMA_PATH;
|
|
416
|
+
const convexSchemaPath = path.join(confectDirectory, generatedConvexSchemaPath);
|
|
417
|
+
const bindings = yield* tableModuleBindings(tableModules, convexSchemaPath);
|
|
418
|
+
yield* writeFileStringAndLog(convexSchemaPath, yield* convexSchema({ tableModules: bindings }));
|
|
419
|
+
});
|
|
420
|
+
const generateConvexSchemaReexport = Effect.gen(function* () {
|
|
287
421
|
const path = yield* Path.Path;
|
|
288
422
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
289
423
|
const convexDirectory = yield* ConvexDirectory.get;
|
|
290
|
-
const
|
|
424
|
+
const generatedConvexSchemaRelativePath = yield* GENERATED_CONVEX_SCHEMA_PATH;
|
|
291
425
|
const convexSchemaPath = path.join(convexDirectory, "schema.ts");
|
|
292
|
-
const
|
|
293
|
-
yield*
|
|
426
|
+
const generatedConvexSchemaPath = path.join(confectDirectory, generatedConvexSchemaRelativePath);
|
|
427
|
+
const convexSchemaImportPath = yield* toModuleImportPath(path.relative(path.dirname(convexSchemaPath), generatedConvexSchemaPath));
|
|
428
|
+
yield* writeFileStringAndLog(convexSchemaPath, yield* schema({ convexSchemaImportPath }));
|
|
294
429
|
});
|
|
295
430
|
const generateServices = Effect.gen(function* () {
|
|
296
431
|
const path = yield* Path.Path;
|
|
297
432
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
298
433
|
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
299
434
|
const servicesPath = path.join(confectGeneratedDirectory, "services.ts");
|
|
300
|
-
const
|
|
435
|
+
const generatedSchemaPath = yield* GENERATED_SCHEMA_PATH;
|
|
436
|
+
const schemaImportPath = yield* toModuleImportPath(path.relative(path.dirname(servicesPath), path.join(confectDirectory, generatedSchemaPath)));
|
|
301
437
|
yield* writeFileStringAndLog(servicesPath, yield* services({ schemaImportPath }));
|
|
302
438
|
});
|
|
303
439
|
const generateRefs = Effect.gen(function* () {
|
|
304
|
-
const fs = yield* FileSystem.FileSystem;
|
|
305
440
|
const path = yield* Path.Path;
|
|
306
441
|
const confectDirectory = yield* ConfectDirectory.get;
|
|
307
442
|
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
308
443
|
const refsPath = path.join(confectGeneratedDirectory, "refs.ts");
|
|
309
444
|
const refsDir = path.dirname(refsPath);
|
|
310
|
-
const
|
|
311
|
-
const
|
|
312
|
-
|
|
313
|
-
yield* writeFileStringAndLog(refsPath, yield* refs({
|
|
314
|
-
specImportPath,
|
|
315
|
-
nodeSpecImportPath
|
|
316
|
-
}));
|
|
445
|
+
const generatedSpecPath = yield* GENERATED_SPEC_PATH;
|
|
446
|
+
const specImportPath = yield* toModuleImportPath(path.relative(refsDir, path.join(confectDirectory, generatedSpecPath)));
|
|
447
|
+
yield* writeFileStringAndLog(refsPath, yield* refs({ specImportPath }));
|
|
317
448
|
});
|
|
318
449
|
const logGenerated = (effect) => effect.pipe(Effect.tap(Option.match({
|
|
319
450
|
onNone: () => Effect.void,
|