@confect/cli 9.0.0-next.9 → 9.0.0
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 +158 -1
- package/dist/Bundler.mjs +1 -4
- package/dist/Bundler.mjs.map +1 -1
- package/dist/confect/codegen.mjs.map +1 -1
- package/dist/log.mjs +2 -2
- package/dist/log.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/package.json +4 -21
- package/dist/index.d.mts +0 -1
- package/src/BuildError.ts +0 -217
- package/src/Bundler.ts +0 -145
- package/src/CodeBlockWriter.ts +0 -65
- package/src/CodegenError.ts +0 -407
- package/src/ConfectDirectory.ts +0 -45
- package/src/ConvexDirectory.ts +0 -72
- package/src/FunctionPath.ts +0 -27
- package/src/FunctionPaths.ts +0 -107
- package/src/GroupPath.ts +0 -116
- package/src/GroupPaths.ts +0 -7
- package/src/LeafModule.ts +0 -305
- package/src/ProjectRoot.ts +0 -55
- package/src/SpecAssemblyNode.ts +0 -75
- package/src/TableModule.ts +0 -157
- package/src/cliApp.ts +0 -8
- package/src/confect/codegen.ts +0 -847
- package/src/confect/dev.ts +0 -785
- package/src/confect.ts +0 -19
- package/src/index.ts +0 -23
- package/src/log.ts +0 -110
- package/src/templates.ts +0 -614
- package/src/utils.ts +0 -435
package/src/confect/codegen.ts
DELETED
|
@@ -1,847 +0,0 @@
|
|
|
1
|
-
import { Spec, type GroupSpec } from "@confect/core";
|
|
2
|
-
import * as Command from "@effect/cli/Command";
|
|
3
|
-
import * as FileSystem from "@effect/platform/FileSystem";
|
|
4
|
-
import * as Path from "@effect/platform/Path";
|
|
5
|
-
import * as Array from "effect/Array";
|
|
6
|
-
import * as Effect from "effect/Effect";
|
|
7
|
-
import * as Either from "effect/Either";
|
|
8
|
-
import * as HashSet from "effect/HashSet";
|
|
9
|
-
import * as Match from "effect/Match";
|
|
10
|
-
import * as Option from "effect/Option";
|
|
11
|
-
import * as Ref from "effect/Ref";
|
|
12
|
-
import * as Bundler from "../Bundler";
|
|
13
|
-
import * as CodegenError from "../CodegenError";
|
|
14
|
-
import {
|
|
15
|
-
LegacySchemaFileError,
|
|
16
|
-
MissingImplFileError,
|
|
17
|
-
MissingSpecFileError,
|
|
18
|
-
ParentChildNameCollisionError,
|
|
19
|
-
} from "../CodegenError";
|
|
20
|
-
import { ConfectDirectory } from "../ConfectDirectory";
|
|
21
|
-
import { ConvexDirectory } from "../ConvexDirectory";
|
|
22
|
-
import * as FunctionPaths from "../FunctionPaths";
|
|
23
|
-
import {
|
|
24
|
-
discoverLeafImplFiles,
|
|
25
|
-
discoverLeafSpecFiles,
|
|
26
|
-
implPathForSpec,
|
|
27
|
-
registeredFunctionsRelativePath,
|
|
28
|
-
specPathForImpl,
|
|
29
|
-
toLeafModule,
|
|
30
|
-
validateImpl,
|
|
31
|
-
validateSpec,
|
|
32
|
-
type LeafModule,
|
|
33
|
-
} from "../LeafModule";
|
|
34
|
-
import {
|
|
35
|
-
logFileAdded,
|
|
36
|
-
logFileModified,
|
|
37
|
-
logFileRemoved,
|
|
38
|
-
logPending,
|
|
39
|
-
logSuccess,
|
|
40
|
-
logWarn,
|
|
41
|
-
} from "../log";
|
|
42
|
-
import {
|
|
43
|
-
assemblyNodesFromLeaves,
|
|
44
|
-
type SpecAssemblyNode,
|
|
45
|
-
} from "../SpecAssemblyNode";
|
|
46
|
-
import * as TableModule from "../TableModule";
|
|
47
|
-
import * as templates from "../templates";
|
|
48
|
-
import {
|
|
49
|
-
generateAuthConfig,
|
|
50
|
-
generateCrons,
|
|
51
|
-
generateFunctions,
|
|
52
|
-
generateHttp,
|
|
53
|
-
removePathIfExists,
|
|
54
|
-
toModuleImportPath,
|
|
55
|
-
touchConvexSchema,
|
|
56
|
-
writeFileStringAndLog,
|
|
57
|
-
WriteTracker,
|
|
58
|
-
} from "../utils";
|
|
59
|
-
|
|
60
|
-
const GENERATED_DIRNAME = "_generated";
|
|
61
|
-
|
|
62
|
-
const GENERATED_SPEC_PATH = Effect.andThen(Path.Path, (path) =>
|
|
63
|
-
path.join(GENERATED_DIRNAME, "spec.ts"),
|
|
64
|
-
);
|
|
65
|
-
const GENERATED_SCHEMA_PATH = Effect.andThen(Path.Path, (path) =>
|
|
66
|
-
path.join(GENERATED_DIRNAME, "schema.ts"),
|
|
67
|
-
);
|
|
68
|
-
const GENERATED_CONVEX_SCHEMA_PATH = Effect.andThen(Path.Path, (path) =>
|
|
69
|
-
path.join(GENERATED_DIRNAME, "convexSchema.ts"),
|
|
70
|
-
);
|
|
71
|
-
const GENERATED_ID_PATH = Effect.andThen(Path.Path, (path) =>
|
|
72
|
-
path.join(GENERATED_DIRNAME, "id.ts"),
|
|
73
|
-
);
|
|
74
|
-
const GENERATED_TABLES_DIRNAME = Effect.andThen(Path.Path, (path) =>
|
|
75
|
-
path.join(GENERATED_DIRNAME, "tables"),
|
|
76
|
-
);
|
|
77
|
-
|
|
78
|
-
const LEGACY_PATHS = Effect.gen(function* () {
|
|
79
|
-
const path = yield* Path.Path;
|
|
80
|
-
|
|
81
|
-
return [
|
|
82
|
-
"spec.ts",
|
|
83
|
-
"nodeSpec.ts",
|
|
84
|
-
"impl.ts",
|
|
85
|
-
"nodeImpl.ts",
|
|
86
|
-
path.join(GENERATED_DIRNAME, "registeredFunctions.ts"),
|
|
87
|
-
path.join(GENERATED_DIRNAME, "nodeRegisteredFunctions.ts"),
|
|
88
|
-
path.join(GENERATED_DIRNAME, "impl.ts"),
|
|
89
|
-
path.join(GENERATED_DIRNAME, "nodeImpl.ts"),
|
|
90
|
-
// `_generated/nodeSpec.ts` is not part of the generated output (all groups
|
|
91
|
-
// live in `_generated/spec.ts`); delete any copy left by an older version.
|
|
92
|
-
path.join(GENERATED_DIRNAME, "nodeSpec.ts"),
|
|
93
|
-
];
|
|
94
|
-
});
|
|
95
|
-
|
|
96
|
-
export const codegen = Command.make("codegen", {}, () =>
|
|
97
|
-
Effect.gen(function* () {
|
|
98
|
-
yield* logPending("Performing initial sync…");
|
|
99
|
-
yield* codegenHandler.pipe(
|
|
100
|
-
Effect.asVoid,
|
|
101
|
-
Effect.tap(() => logSuccess("Generated files are up-to-date")),
|
|
102
|
-
CodegenError.tapAndLog,
|
|
103
|
-
);
|
|
104
|
-
}),
|
|
105
|
-
).pipe(
|
|
106
|
-
Command.withDescription(
|
|
107
|
-
"Generate `confect/_generated` files and the contents of the `convex` directory (except `convex.config.ts` and `tsconfig.json`)",
|
|
108
|
-
),
|
|
109
|
-
);
|
|
110
|
-
|
|
111
|
-
export const codegenHandler = Effect.gen(function* () {
|
|
112
|
-
const tracker = yield* Ref.make(false);
|
|
113
|
-
|
|
114
|
-
const functionPaths = yield* runCodegen.pipe(
|
|
115
|
-
Effect.provideService(WriteTracker, tracker),
|
|
116
|
-
);
|
|
117
|
-
|
|
118
|
-
const anyWritesHappened = yield* Ref.get(tracker);
|
|
119
|
-
return { functionPaths, anyWritesHappened };
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
const runCodegen = Effect.gen(function* () {
|
|
123
|
-
yield* generateConfectGeneratedDirectory;
|
|
124
|
-
// Reject a legacy `confect/schema.ts` up front so the user-facing
|
|
125
|
-
// migration message surfaces before any bundler error from impl
|
|
126
|
-
// validation (each impl imports `_generated/schema.ts`).
|
|
127
|
-
yield* rejectLegacySchemaFile;
|
|
128
|
-
// List `confect/tables/*.ts` (filename-only — no bundling yet) so the
|
|
129
|
-
// `_generated/id.ts` constructor can be emitted *before* we bundle any
|
|
130
|
-
// user-authored table module. Tables import from `_generated/id.ts` for
|
|
131
|
-
// cross-table id refs, so it must exist on disk first.
|
|
132
|
-
const tableModules = yield* TableModule.discover;
|
|
133
|
-
yield* warnIfNoTables(tableModules);
|
|
134
|
-
yield* generateIdConstructor(tableModules);
|
|
135
|
-
// Now that `_generated/id.ts` is on disk, bundle each table module and
|
|
136
|
-
// check its default export is an `UnnamedTable`. Surface diagnostics
|
|
137
|
-
// here (rather than later) so they appear before impl-validation noise.
|
|
138
|
-
yield* TableModule.validate(tableModules);
|
|
139
|
-
yield* generateTableWrappers(tableModules);
|
|
140
|
-
yield* removeObsoleteTableWrappers(tableModules);
|
|
141
|
-
yield* generateRuntimeSchema(tableModules);
|
|
142
|
-
const { leaves, groupSpecsByRelativePath } =
|
|
143
|
-
yield* loadAndValidateLeafModules;
|
|
144
|
-
yield* removeLegacyFiles;
|
|
145
|
-
yield* validateNoParentChildNameCollisions(leaves, groupSpecsByRelativePath);
|
|
146
|
-
yield* generateAssembledSpecs(leaves);
|
|
147
|
-
// `_generated/api.ts` / `nodeApi.ts` are no longer imported by generated or
|
|
148
|
-
// impl code (impls take the database schema from `_generated/schema`
|
|
149
|
-
// directly), so remove any copies left over from earlier versions before
|
|
150
|
-
// impl validation runs.
|
|
151
|
-
yield* Effect.all(
|
|
152
|
-
[
|
|
153
|
-
removeGeneratedApi,
|
|
154
|
-
generateRefs,
|
|
155
|
-
removeGeneratedNodeApi,
|
|
156
|
-
generateServices,
|
|
157
|
-
generateConvexSchema(tableModules),
|
|
158
|
-
],
|
|
159
|
-
{ concurrency: "unbounded" },
|
|
160
|
-
);
|
|
161
|
-
yield* validateImplModules(leaves);
|
|
162
|
-
yield* generateGroupRegisteredFunctions(leaves);
|
|
163
|
-
yield* removeObsoleteRegisteredFunctions(leaves);
|
|
164
|
-
const [functionPaths] = yield* Effect.all(
|
|
165
|
-
[
|
|
166
|
-
generateFunctionModules,
|
|
167
|
-
generateConvexSchemaReexport,
|
|
168
|
-
logGenerated(generateHttp),
|
|
169
|
-
logGenerated(generateCrons),
|
|
170
|
-
logGenerated(generateAuthConfig),
|
|
171
|
-
],
|
|
172
|
-
{ concurrency: "unbounded" },
|
|
173
|
-
);
|
|
174
|
-
yield* touchConvexSchema;
|
|
175
|
-
return functionPaths;
|
|
176
|
-
});
|
|
177
|
-
|
|
178
|
-
const generateConfectGeneratedDirectory = Effect.gen(function* () {
|
|
179
|
-
const fs = yield* FileSystem.FileSystem;
|
|
180
|
-
const path = yield* Path.Path;
|
|
181
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
182
|
-
|
|
183
|
-
if (!(yield* fs.exists(path.join(confectDirectory, "_generated")))) {
|
|
184
|
-
yield* fs.makeDirectory(path.join(confectDirectory, "_generated"), {
|
|
185
|
-
recursive: true,
|
|
186
|
-
});
|
|
187
|
-
yield* logFileAdded(path.join(confectDirectory, "_generated") + "/");
|
|
188
|
-
}
|
|
189
|
-
});
|
|
190
|
-
|
|
191
|
-
const loadAndValidateLeafModules = Effect.gen(function* () {
|
|
192
|
-
const fs = yield* FileSystem.FileSystem;
|
|
193
|
-
const path = yield* Path.Path;
|
|
194
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
195
|
-
const specFiles = yield* discoverLeafSpecFiles;
|
|
196
|
-
|
|
197
|
-
const results = yield* Effect.forEach(specFiles, (specRelativePath) =>
|
|
198
|
-
Effect.gen(function* () {
|
|
199
|
-
const discovered = yield* toLeafModule(specRelativePath);
|
|
200
|
-
const groupSpec = yield* validateSpec(discovered);
|
|
201
|
-
// Fill in the runtime now that the spec is bundled; discovery left it `None`.
|
|
202
|
-
const leaf = {
|
|
203
|
-
...discovered,
|
|
204
|
-
runtime: Option.some(groupSpec.runtime),
|
|
205
|
-
};
|
|
206
|
-
|
|
207
|
-
const implRelativePath = yield* implPathForSpec(specRelativePath);
|
|
208
|
-
const implAbsolutePath = path.join(confectDirectory, implRelativePath);
|
|
209
|
-
if (!(yield* fs.exists(implAbsolutePath))) {
|
|
210
|
-
return yield* new MissingImplFileError({
|
|
211
|
-
specPath: specRelativePath,
|
|
212
|
-
expectedImplPath: implRelativePath,
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
return { leaf, groupSpec };
|
|
217
|
-
}),
|
|
218
|
-
);
|
|
219
|
-
|
|
220
|
-
yield* validateOrphanImpls(specFiles);
|
|
221
|
-
|
|
222
|
-
const leaves = Array.map(results, ({ leaf }) => leaf);
|
|
223
|
-
const groupSpecsByRelativePath = new Map(
|
|
224
|
-
Array.map(results, ({ leaf, groupSpec }) => [leaf.relativePath, groupSpec]),
|
|
225
|
-
);
|
|
226
|
-
|
|
227
|
-
return { leaves, groupSpecsByRelativePath };
|
|
228
|
-
});
|
|
229
|
-
|
|
230
|
-
/**
|
|
231
|
-
* Walk the assembly tree and fail with a {@link ParentChildNameCollisionError}
|
|
232
|
-
* when a parent leaf declares a function or subgroup whose name matches a
|
|
233
|
-
* sibling subdirectory spec's segment. Without this check the colliding
|
|
234
|
-
* descendant would overwrite the parent's entry in the assembled
|
|
235
|
-
* `GroupSpec.groups` map at runtime, surfacing as a confusing
|
|
236
|
-
* `Refs.make` error rather than a codegen-time diagnostic.
|
|
237
|
-
*/
|
|
238
|
-
export const validateNoParentChildNameCollisions = (
|
|
239
|
-
leaves: ReadonlyArray<LeafModule>,
|
|
240
|
-
groupSpecsByRelativePath: ReadonlyMap<string, GroupSpec.AnyWithProps>,
|
|
241
|
-
) =>
|
|
242
|
-
Effect.gen(function* () {
|
|
243
|
-
// Convex and Node groups share one namespace, so they assemble into a
|
|
244
|
-
// single tree. A Node group nested under a Convex parent (or vice versa) is
|
|
245
|
-
// caught here by the parent/child collision check.
|
|
246
|
-
const nodes = assemblyNodesFromLeaves(leaves);
|
|
247
|
-
yield* Effect.forEach(nodes, (n) =>
|
|
248
|
-
checkAssemblyNodeForCollisions(n, groupSpecsByRelativePath),
|
|
249
|
-
);
|
|
250
|
-
});
|
|
251
|
-
|
|
252
|
-
const checkAssemblyNodeForCollisions = (
|
|
253
|
-
node: SpecAssemblyNode,
|
|
254
|
-
groupSpecsByRelativePath: ReadonlyMap<string, GroupSpec.AnyWithProps>,
|
|
255
|
-
): Effect.Effect<void, ParentChildNameCollisionError> =>
|
|
256
|
-
Effect.gen(function* () {
|
|
257
|
-
yield* Option.match(node.importBinding, {
|
|
258
|
-
onNone: () => Effect.void,
|
|
259
|
-
onSome: (binding) =>
|
|
260
|
-
Effect.gen(function* () {
|
|
261
|
-
if (node.children.length === 0) return;
|
|
262
|
-
const parentRelativePath = bindingToRelativeSpecPath(
|
|
263
|
-
binding.importPath,
|
|
264
|
-
);
|
|
265
|
-
const parentGroupSpec =
|
|
266
|
-
groupSpecsByRelativePath.get(parentRelativePath);
|
|
267
|
-
if (parentGroupSpec === undefined) return;
|
|
268
|
-
yield* Effect.forEach(node.children, (child) => {
|
|
269
|
-
if (
|
|
270
|
-
Object.prototype.hasOwnProperty.call(
|
|
271
|
-
parentGroupSpec.functions,
|
|
272
|
-
child.segment,
|
|
273
|
-
)
|
|
274
|
-
) {
|
|
275
|
-
return Effect.fail(
|
|
276
|
-
new ParentChildNameCollisionError({
|
|
277
|
-
parentSpecPath: parentRelativePath,
|
|
278
|
-
childSpecPath: childRepresentativeSpecPath(child),
|
|
279
|
-
collisionName: child.segment,
|
|
280
|
-
collisionKind: "function",
|
|
281
|
-
}),
|
|
282
|
-
);
|
|
283
|
-
}
|
|
284
|
-
if (
|
|
285
|
-
Object.prototype.hasOwnProperty.call(
|
|
286
|
-
parentGroupSpec.groups,
|
|
287
|
-
child.segment,
|
|
288
|
-
)
|
|
289
|
-
) {
|
|
290
|
-
return Effect.fail(
|
|
291
|
-
new ParentChildNameCollisionError({
|
|
292
|
-
parentSpecPath: parentRelativePath,
|
|
293
|
-
childSpecPath: childRepresentativeSpecPath(child),
|
|
294
|
-
collisionName: child.segment,
|
|
295
|
-
collisionKind: "group",
|
|
296
|
-
}),
|
|
297
|
-
);
|
|
298
|
-
}
|
|
299
|
-
return Effect.void;
|
|
300
|
-
});
|
|
301
|
-
}),
|
|
302
|
-
});
|
|
303
|
-
yield* Effect.forEach(node.children, (child) =>
|
|
304
|
-
checkAssemblyNodeForCollisions(child, groupSpecsByRelativePath),
|
|
305
|
-
);
|
|
306
|
-
});
|
|
307
|
-
|
|
308
|
-
/**
|
|
309
|
-
* `LeafModule.specImportPath` is the import path used from inside the
|
|
310
|
-
* generated `_generated/spec.ts` (e.g. `"../notes.spec"`). Strip the
|
|
311
|
-
* `../` prefix and re-add the `.ts` extension to recover the leaf's
|
|
312
|
-
* confect-relative spec path used as the key in
|
|
313
|
-
* `groupSpecsByRelativePath`.
|
|
314
|
-
*/
|
|
315
|
-
const bindingToRelativeSpecPath = (importPath: string): string => {
|
|
316
|
-
const withoutDotDot = importPath.startsWith("../")
|
|
317
|
-
? importPath.slice(3)
|
|
318
|
-
: importPath;
|
|
319
|
-
return `${withoutDotDot}.ts`;
|
|
320
|
-
};
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
* A child assembly node may itself be a parent without a leaf (when the
|
|
324
|
-
* actual leaves live only in deeper subdirectories). In that case we
|
|
325
|
-
* surface the first descendant leaf as a representative path so the
|
|
326
|
-
* error message points at something the user actually wrote.
|
|
327
|
-
*/
|
|
328
|
-
const childRepresentativeSpecPath = (node: SpecAssemblyNode): string => {
|
|
329
|
-
if (Option.isSome(node.importBinding)) {
|
|
330
|
-
return bindingToRelativeSpecPath(node.importBinding.value.importPath);
|
|
331
|
-
}
|
|
332
|
-
for (const child of node.children) {
|
|
333
|
-
return childRepresentativeSpecPath(child);
|
|
334
|
-
}
|
|
335
|
-
return node.segment;
|
|
336
|
-
};
|
|
337
|
-
|
|
338
|
-
const validateOrphanImpls = (specFiles: ReadonlyArray<string>) =>
|
|
339
|
-
Effect.gen(function* () {
|
|
340
|
-
const fs = yield* FileSystem.FileSystem;
|
|
341
|
-
const path = yield* Path.Path;
|
|
342
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
343
|
-
const implFiles = yield* discoverLeafImplFiles;
|
|
344
|
-
const specPaths = new Set(specFiles);
|
|
345
|
-
|
|
346
|
-
yield* Effect.forEach(implFiles, (implRelativePath) =>
|
|
347
|
-
Effect.gen(function* () {
|
|
348
|
-
const specRelativePath = yield* specPathForImpl(implRelativePath);
|
|
349
|
-
if (specPaths.has(specRelativePath)) {
|
|
350
|
-
return;
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
const specAbsolutePath = path.join(confectDirectory, specRelativePath);
|
|
354
|
-
if (!(yield* fs.exists(specAbsolutePath))) {
|
|
355
|
-
return yield* new MissingSpecFileError({
|
|
356
|
-
implPath: implRelativePath,
|
|
357
|
-
expectedSpecPath: specRelativePath,
|
|
358
|
-
});
|
|
359
|
-
}
|
|
360
|
-
}),
|
|
361
|
-
);
|
|
362
|
-
});
|
|
363
|
-
|
|
364
|
-
const removeLegacyFiles = Effect.gen(function* () {
|
|
365
|
-
const fs = yield* FileSystem.FileSystem;
|
|
366
|
-
const path = yield* Path.Path;
|
|
367
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
368
|
-
const legacyPaths = yield* LEGACY_PATHS;
|
|
369
|
-
|
|
370
|
-
yield* Effect.forEach(legacyPaths, (relativePath) =>
|
|
371
|
-
Effect.gen(function* () {
|
|
372
|
-
const absolutePath = path.join(confectDirectory, relativePath);
|
|
373
|
-
if (yield* fs.exists(absolutePath)) {
|
|
374
|
-
yield* removePathIfExists(absolutePath);
|
|
375
|
-
yield* logFileRemoved(absolutePath);
|
|
376
|
-
}
|
|
377
|
-
}),
|
|
378
|
-
);
|
|
379
|
-
});
|
|
380
|
-
|
|
381
|
-
const generateAssembledSpecs = (leaves: ReadonlyArray<LeafModule>) =>
|
|
382
|
-
Effect.gen(function* () {
|
|
383
|
-
const path = yield* Path.Path;
|
|
384
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
385
|
-
const generatedSpecPath = yield* GENERATED_SPEC_PATH;
|
|
386
|
-
|
|
387
|
-
// A single assembled spec holds every group regardless of runtime — a Node
|
|
388
|
-
// group's `makeNode()` lives in its imported leaf spec, so the assembled
|
|
389
|
-
// file is runtime-agnostic. Always emit it (even empty) so downstream
|
|
390
|
-
// readers (`loadGeneratedSpec`, `generateRefs`) always find a spec module.
|
|
391
|
-
const nodes = assemblyNodesFromLeaves(leaves);
|
|
392
|
-
const specContents = yield* templates.assembledSpec({ nodes });
|
|
393
|
-
yield* writeFileStringAndLog(
|
|
394
|
-
path.join(confectDirectory, generatedSpecPath),
|
|
395
|
-
specContents,
|
|
396
|
-
);
|
|
397
|
-
});
|
|
398
|
-
|
|
399
|
-
const validateImplModules = (leaves: ReadonlyArray<LeafModule>) =>
|
|
400
|
-
Effect.forEach(leaves, validateImpl);
|
|
401
|
-
|
|
402
|
-
const generateGroupRegisteredFunctions = (leaves: ReadonlyArray<LeafModule>) =>
|
|
403
|
-
Effect.gen(function* () {
|
|
404
|
-
const path = yield* Path.Path;
|
|
405
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
406
|
-
|
|
407
|
-
yield* Effect.forEach(leaves, (leaf) =>
|
|
408
|
-
Effect.gen(function* () {
|
|
409
|
-
const registryRelativePath =
|
|
410
|
-
yield* registeredFunctionsRelativePath(leaf);
|
|
411
|
-
const registryPath = path.join(
|
|
412
|
-
confectDirectory,
|
|
413
|
-
"_generated",
|
|
414
|
-
registryRelativePath,
|
|
415
|
-
);
|
|
416
|
-
const registryDir = path.dirname(registryPath);
|
|
417
|
-
const fs = yield* FileSystem.FileSystem;
|
|
418
|
-
if (!(yield* fs.exists(registryDir))) {
|
|
419
|
-
yield* fs.makeDirectory(registryDir, { recursive: true });
|
|
420
|
-
}
|
|
421
|
-
|
|
422
|
-
const implRelativePath = yield* implPathForSpec(leaf.relativePath);
|
|
423
|
-
const schemaImportPath = yield* toModuleImportPath(
|
|
424
|
-
path.relative(
|
|
425
|
-
path.dirname(registryPath),
|
|
426
|
-
path.join(confectDirectory, "_generated", "schema.ts"),
|
|
427
|
-
),
|
|
428
|
-
);
|
|
429
|
-
// The group's own leaf spec (sibling of its impl), referenced
|
|
430
|
-
// type-only by the registry to shape its returned record.
|
|
431
|
-
const specImportPath = yield* toModuleImportPath(
|
|
432
|
-
path.relative(
|
|
433
|
-
path.dirname(registryPath),
|
|
434
|
-
path.join(confectDirectory, leaf.relativePath),
|
|
435
|
-
),
|
|
436
|
-
);
|
|
437
|
-
const implImportPath = yield* toModuleImportPath(
|
|
438
|
-
path.relative(
|
|
439
|
-
path.dirname(registryPath),
|
|
440
|
-
path.join(confectDirectory, implRelativePath),
|
|
441
|
-
),
|
|
442
|
-
);
|
|
443
|
-
|
|
444
|
-
// Every leaf reaching this point came through
|
|
445
|
-
// `loadAndValidateLeafModules`, which stamps the runtime from the
|
|
446
|
-
// validated spec — so `None` here means that invariant was broken.
|
|
447
|
-
const runtime = yield* Option.match(leaf.runtime, {
|
|
448
|
-
onNone: () =>
|
|
449
|
-
Effect.dieMessage(
|
|
450
|
-
`Runtime for '${leaf.relativePath}' was not resolved before registry generation.`,
|
|
451
|
-
),
|
|
452
|
-
onSome: Effect.succeed,
|
|
453
|
-
});
|
|
454
|
-
|
|
455
|
-
const contents = yield* templates.registeredFunctionsForGroup({
|
|
456
|
-
schemaImportPath,
|
|
457
|
-
specImportPath,
|
|
458
|
-
implImportPath,
|
|
459
|
-
layerExportName: leaf.exportName,
|
|
460
|
-
useNode: runtime === "Node",
|
|
461
|
-
});
|
|
462
|
-
|
|
463
|
-
yield* writeFileStringAndLog(registryPath, contents);
|
|
464
|
-
}),
|
|
465
|
-
);
|
|
466
|
-
});
|
|
467
|
-
|
|
468
|
-
const removeObsoleteRegisteredFunctions = (leaves: ReadonlyArray<LeafModule>) =>
|
|
469
|
-
Effect.gen(function* () {
|
|
470
|
-
const fs = yield* FileSystem.FileSystem;
|
|
471
|
-
const path = yield* Path.Path;
|
|
472
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
473
|
-
const registryRoot = path.join(
|
|
474
|
-
confectDirectory,
|
|
475
|
-
"_generated",
|
|
476
|
-
"registeredFunctions",
|
|
477
|
-
);
|
|
478
|
-
|
|
479
|
-
if (!(yield* fs.exists(registryRoot))) {
|
|
480
|
-
return;
|
|
481
|
-
}
|
|
482
|
-
|
|
483
|
-
const expected = new Set(
|
|
484
|
-
yield* Effect.forEach(leaves, (leaf) =>
|
|
485
|
-
registeredFunctionsRelativePath(leaf),
|
|
486
|
-
),
|
|
487
|
-
);
|
|
488
|
-
|
|
489
|
-
const existing = yield* fs.readDirectory(registryRoot, { recursive: true });
|
|
490
|
-
yield* Effect.forEach(existing, (relativePath) => {
|
|
491
|
-
if (path.extname(relativePath) !== ".ts") {
|
|
492
|
-
return Effect.void;
|
|
493
|
-
}
|
|
494
|
-
const normalized = path.join("registeredFunctions", relativePath);
|
|
495
|
-
if (!expected.has(normalized)) {
|
|
496
|
-
return Effect.gen(function* () {
|
|
497
|
-
const absolutePath = path.join(registryRoot, relativePath);
|
|
498
|
-
if (yield* fs.exists(absolutePath)) {
|
|
499
|
-
yield* removePathIfExists(absolutePath);
|
|
500
|
-
yield* logFileRemoved(absolutePath);
|
|
501
|
-
}
|
|
502
|
-
});
|
|
503
|
-
}
|
|
504
|
-
return Effect.void;
|
|
505
|
-
});
|
|
506
|
-
});
|
|
507
|
-
|
|
508
|
-
const getGeneratedSpecPath = Effect.gen(function* () {
|
|
509
|
-
const path = yield* Path.Path;
|
|
510
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
511
|
-
const generatedSpecPath = yield* GENERATED_SPEC_PATH;
|
|
512
|
-
return path.join(confectDirectory, generatedSpecPath);
|
|
513
|
-
});
|
|
514
|
-
|
|
515
|
-
const loadGeneratedSpec = Effect.gen(function* () {
|
|
516
|
-
const specPath = yield* getGeneratedSpecPath;
|
|
517
|
-
const { module: specModule } = yield* Bundler.bundle(specPath);
|
|
518
|
-
const spec = specModule.default;
|
|
519
|
-
|
|
520
|
-
if (!Spec.isSpec(spec)) {
|
|
521
|
-
return yield* Effect.dieMessage(
|
|
522
|
-
"_generated/spec.ts does not export a valid Spec",
|
|
523
|
-
);
|
|
524
|
-
}
|
|
525
|
-
|
|
526
|
-
return spec;
|
|
527
|
-
});
|
|
528
|
-
|
|
529
|
-
const emptyFunctionPaths = FunctionPaths.FunctionPaths.make(HashSet.empty());
|
|
530
|
-
|
|
531
|
-
export const loadPreviousFunctionPaths = Effect.gen(function* () {
|
|
532
|
-
const fs = yield* FileSystem.FileSystem;
|
|
533
|
-
const specPath = yield* getGeneratedSpecPath;
|
|
534
|
-
|
|
535
|
-
if (!(yield* fs.exists(specPath))) {
|
|
536
|
-
return emptyFunctionPaths;
|
|
537
|
-
}
|
|
538
|
-
|
|
539
|
-
const specEither = yield* loadGeneratedSpec.pipe(Effect.either);
|
|
540
|
-
|
|
541
|
-
return Either.match(specEither, {
|
|
542
|
-
onLeft: () => emptyFunctionPaths,
|
|
543
|
-
onRight: (spec) => FunctionPaths.make(spec),
|
|
544
|
-
});
|
|
545
|
-
});
|
|
546
|
-
|
|
547
|
-
/**
|
|
548
|
-
* Remove a now-obsolete `_generated/<name>.ts` if present (and log it), for
|
|
549
|
-
* projects upgrading from a version that still emitted it.
|
|
550
|
-
*/
|
|
551
|
-
const removeObsoleteGeneratedFile = (fileName: string) =>
|
|
552
|
-
Effect.gen(function* () {
|
|
553
|
-
const fs = yield* FileSystem.FileSystem;
|
|
554
|
-
const path = yield* Path.Path;
|
|
555
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
556
|
-
const filePath = path.join(confectDirectory, "_generated", fileName);
|
|
557
|
-
|
|
558
|
-
if (yield* fs.exists(filePath)) {
|
|
559
|
-
yield* removePathIfExists(filePath);
|
|
560
|
-
yield* logFileRemoved(filePath);
|
|
561
|
-
}
|
|
562
|
-
});
|
|
563
|
-
|
|
564
|
-
// `_generated/api.ts` is no longer imported by generated or impl code: impls
|
|
565
|
-
// take the database schema (`_generated/schema`) directly, and per-group
|
|
566
|
-
// registries reference the spec type-only. Remove any stale copy.
|
|
567
|
-
const removeGeneratedApi = removeObsoleteGeneratedFile("api.ts");
|
|
568
|
-
|
|
569
|
-
// `_generated/nodeApi.ts` is obsolete for the same reason.
|
|
570
|
-
const removeGeneratedNodeApi = removeObsoleteGeneratedFile("nodeApi.ts");
|
|
571
|
-
|
|
572
|
-
const generateFunctionModules = Effect.gen(function* () {
|
|
573
|
-
const spec = yield* loadGeneratedSpec;
|
|
574
|
-
return yield* generateFunctions(spec);
|
|
575
|
-
});
|
|
576
|
-
|
|
577
|
-
/**
|
|
578
|
-
* The user-authored `confect/schema.ts` is no longer supported: codegen now
|
|
579
|
-
* owns both `_generated/schema.ts` (runtime) and `_generated/convexSchema.ts`
|
|
580
|
-
* (deploy), derived from a single scan of `confect/tables/*.ts`. Detect a
|
|
581
|
-
* stray file and fail with a clear migration message — leaving it in place
|
|
582
|
-
* would silently shadow the codegen-owned `_generated/schema.ts` /
|
|
583
|
-
* `_generated/convexSchema.ts`.
|
|
584
|
-
*/
|
|
585
|
-
const rejectLegacySchemaFile = Effect.gen(function* () {
|
|
586
|
-
const fs = yield* FileSystem.FileSystem;
|
|
587
|
-
const path = yield* Path.Path;
|
|
588
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
589
|
-
const legacyPath = path.join(confectDirectory, "schema.ts");
|
|
590
|
-
|
|
591
|
-
if (yield* fs.exists(legacyPath)) {
|
|
592
|
-
return yield* new LegacySchemaFileError({ schemaPath: "schema.ts" });
|
|
593
|
-
}
|
|
594
|
-
});
|
|
595
|
-
|
|
596
|
-
/**
|
|
597
|
-
* Surface a yellow `⚠` warning when codegen sees no tables — either the
|
|
598
|
-
* `confect/tables/` directory is missing or it contains no `.ts` files.
|
|
599
|
-
* Generation still succeeds (emitting an empty `DatabaseSchema` and
|
|
600
|
-
* `defineSchema({})`), since action-only / table-free Confect backends
|
|
601
|
-
* are legal — but the warning catches the much more common case of a
|
|
602
|
-
* typoed directory or files placed under the wrong root.
|
|
603
|
-
*/
|
|
604
|
-
const warnIfNoTables = (
|
|
605
|
-
tableModules: ReadonlyArray<TableModule.TableModule>,
|
|
606
|
-
) =>
|
|
607
|
-
tableModules.length === 0
|
|
608
|
-
? logWarn(
|
|
609
|
-
`No tables discovered in \`confect/${TableModule.TABLES_DIRNAME}/\`. ` +
|
|
610
|
-
`Generating an empty schema; add a \`Table.make(...)\` module under that ` +
|
|
611
|
-
`directory unless this backend is intentionally tables-free.`,
|
|
612
|
-
)
|
|
613
|
-
: Effect.void;
|
|
614
|
-
|
|
615
|
-
const tableModuleBindings = (
|
|
616
|
-
tableModules: ReadonlyArray<TableModule.TableModule>,
|
|
617
|
-
generatedFilePath: string,
|
|
618
|
-
) =>
|
|
619
|
-
Effect.gen(function* () {
|
|
620
|
-
const path = yield* Path.Path;
|
|
621
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
622
|
-
const generatedDir = path.dirname(generatedFilePath);
|
|
623
|
-
|
|
624
|
-
const generatedTablesDirname = yield* GENERATED_TABLES_DIRNAME;
|
|
625
|
-
|
|
626
|
-
return yield* Effect.forEach(tableModules, (tm) =>
|
|
627
|
-
Effect.gen(function* () {
|
|
628
|
-
const wrapperAbsolutePath = path.join(
|
|
629
|
-
confectDirectory,
|
|
630
|
-
generatedTablesDirname,
|
|
631
|
-
`${tm.tableName}.ts`,
|
|
632
|
-
);
|
|
633
|
-
const importPath = yield* toModuleImportPath(
|
|
634
|
-
path.relative(generatedDir, wrapperAbsolutePath),
|
|
635
|
-
);
|
|
636
|
-
return {
|
|
637
|
-
importPath,
|
|
638
|
-
tableName: tm.tableName,
|
|
639
|
-
};
|
|
640
|
-
}),
|
|
641
|
-
);
|
|
642
|
-
});
|
|
643
|
-
|
|
644
|
-
const generateIdConstructor = (
|
|
645
|
-
tableModules: ReadonlyArray<TableModule.TableModule>,
|
|
646
|
-
) =>
|
|
647
|
-
Effect.gen(function* () {
|
|
648
|
-
const path = yield* Path.Path;
|
|
649
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
650
|
-
const generatedIdPath = yield* GENERATED_ID_PATH;
|
|
651
|
-
const idPath = path.join(confectDirectory, generatedIdPath);
|
|
652
|
-
|
|
653
|
-
const tableNames = tableModules.map((tm) => tm.tableName);
|
|
654
|
-
const contents = yield* templates.id({ tableNames });
|
|
655
|
-
|
|
656
|
-
yield* writeFileStringAndLog(idPath, contents);
|
|
657
|
-
});
|
|
658
|
-
|
|
659
|
-
const generateTableWrappers = (
|
|
660
|
-
tableModules: ReadonlyArray<TableModule.TableModule>,
|
|
661
|
-
) =>
|
|
662
|
-
Effect.gen(function* () {
|
|
663
|
-
const fs = yield* FileSystem.FileSystem;
|
|
664
|
-
const path = yield* Path.Path;
|
|
665
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
666
|
-
const generatedTablesDirname = yield* GENERATED_TABLES_DIRNAME;
|
|
667
|
-
const wrappersDir = path.join(confectDirectory, generatedTablesDirname);
|
|
668
|
-
|
|
669
|
-
if (!(yield* fs.exists(wrappersDir))) {
|
|
670
|
-
yield* fs.makeDirectory(wrappersDir, { recursive: true });
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
yield* Effect.forEach(
|
|
674
|
-
tableModules,
|
|
675
|
-
(tm) =>
|
|
676
|
-
Effect.gen(function* () {
|
|
677
|
-
const wrapperPath = path.join(
|
|
678
|
-
confectDirectory,
|
|
679
|
-
generatedTablesDirname,
|
|
680
|
-
`${tm.tableName}.ts`,
|
|
681
|
-
);
|
|
682
|
-
const unnamedAbsolutePath = path.join(
|
|
683
|
-
confectDirectory,
|
|
684
|
-
tm.relativePath,
|
|
685
|
-
);
|
|
686
|
-
const unnamedImportPath = yield* toModuleImportPath(
|
|
687
|
-
path.relative(path.dirname(wrapperPath), unnamedAbsolutePath),
|
|
688
|
-
);
|
|
689
|
-
const contents = yield* templates.tableWrapper({
|
|
690
|
-
tableName: tm.tableName,
|
|
691
|
-
unnamedImportPath,
|
|
692
|
-
});
|
|
693
|
-
yield* writeFileStringAndLog(wrapperPath, contents);
|
|
694
|
-
}),
|
|
695
|
-
{ concurrency: "unbounded" },
|
|
696
|
-
);
|
|
697
|
-
});
|
|
698
|
-
|
|
699
|
-
/**
|
|
700
|
-
* Remove any stale `_generated/tables/*.ts` wrapper whose source table
|
|
701
|
-
* has been deleted or renamed. Mirrors `removeObsoleteRegisteredFunctions`
|
|
702
|
-
* for the wrapper directory.
|
|
703
|
-
*/
|
|
704
|
-
const removeObsoleteTableWrappers = (
|
|
705
|
-
tableModules: ReadonlyArray<TableModule.TableModule>,
|
|
706
|
-
) =>
|
|
707
|
-
Effect.gen(function* () {
|
|
708
|
-
const fs = yield* FileSystem.FileSystem;
|
|
709
|
-
const path = yield* Path.Path;
|
|
710
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
711
|
-
const generatedTablesDirname = yield* GENERATED_TABLES_DIRNAME;
|
|
712
|
-
const wrappersDir = path.join(confectDirectory, generatedTablesDirname);
|
|
713
|
-
|
|
714
|
-
if (!(yield* fs.exists(wrappersDir))) {
|
|
715
|
-
return;
|
|
716
|
-
}
|
|
717
|
-
|
|
718
|
-
const expected = new Set(tableModules.map((tm) => `${tm.tableName}.ts`));
|
|
719
|
-
const existing = yield* fs.readDirectory(wrappersDir, { recursive: true });
|
|
720
|
-
yield* Effect.forEach(existing, (entry) => {
|
|
721
|
-
if (path.extname(entry) !== ".ts") {
|
|
722
|
-
return Effect.void;
|
|
723
|
-
}
|
|
724
|
-
if (!expected.has(entry)) {
|
|
725
|
-
return Effect.gen(function* () {
|
|
726
|
-
const absolutePath = path.join(wrappersDir, entry);
|
|
727
|
-
if (yield* fs.exists(absolutePath)) {
|
|
728
|
-
yield* removePathIfExists(absolutePath);
|
|
729
|
-
yield* logFileRemoved(absolutePath);
|
|
730
|
-
}
|
|
731
|
-
});
|
|
732
|
-
}
|
|
733
|
-
return Effect.void;
|
|
734
|
-
});
|
|
735
|
-
});
|
|
736
|
-
|
|
737
|
-
const generateRuntimeSchema = (
|
|
738
|
-
tableModules: ReadonlyArray<TableModule.TableModule>,
|
|
739
|
-
) =>
|
|
740
|
-
Effect.gen(function* () {
|
|
741
|
-
const path = yield* Path.Path;
|
|
742
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
743
|
-
const generatedSchemaPath = yield* GENERATED_SCHEMA_PATH;
|
|
744
|
-
const schemaPath = path.join(confectDirectory, generatedSchemaPath);
|
|
745
|
-
|
|
746
|
-
const bindings = yield* tableModuleBindings(tableModules, schemaPath);
|
|
747
|
-
const contents = yield* templates.runtimeSchema({ tableModules: bindings });
|
|
748
|
-
|
|
749
|
-
yield* writeFileStringAndLog(schemaPath, contents);
|
|
750
|
-
});
|
|
751
|
-
|
|
752
|
-
const generateConvexSchema = (
|
|
753
|
-
tableModules: ReadonlyArray<TableModule.TableModule>,
|
|
754
|
-
) =>
|
|
755
|
-
Effect.gen(function* () {
|
|
756
|
-
const path = yield* Path.Path;
|
|
757
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
758
|
-
const generatedConvexSchemaPath = yield* GENERATED_CONVEX_SCHEMA_PATH;
|
|
759
|
-
const convexSchemaPath = path.join(
|
|
760
|
-
confectDirectory,
|
|
761
|
-
generatedConvexSchemaPath,
|
|
762
|
-
);
|
|
763
|
-
|
|
764
|
-
const bindings = yield* tableModuleBindings(tableModules, convexSchemaPath);
|
|
765
|
-
const contents = yield* templates.convexSchema({ tableModules: bindings });
|
|
766
|
-
|
|
767
|
-
yield* writeFileStringAndLog(convexSchemaPath, contents);
|
|
768
|
-
});
|
|
769
|
-
|
|
770
|
-
const generateConvexSchemaReexport = Effect.gen(function* () {
|
|
771
|
-
const path = yield* Path.Path;
|
|
772
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
773
|
-
const convexDirectory = yield* ConvexDirectory.get;
|
|
774
|
-
const generatedConvexSchemaRelativePath = yield* GENERATED_CONVEX_SCHEMA_PATH;
|
|
775
|
-
|
|
776
|
-
const convexSchemaPath = path.join(convexDirectory, "schema.ts");
|
|
777
|
-
const generatedConvexSchemaPath = path.join(
|
|
778
|
-
confectDirectory,
|
|
779
|
-
generatedConvexSchemaRelativePath,
|
|
780
|
-
);
|
|
781
|
-
|
|
782
|
-
const convexSchemaImportPath = yield* toModuleImportPath(
|
|
783
|
-
path.relative(path.dirname(convexSchemaPath), generatedConvexSchemaPath),
|
|
784
|
-
);
|
|
785
|
-
|
|
786
|
-
const schemaContents = yield* templates.schema({
|
|
787
|
-
convexSchemaImportPath,
|
|
788
|
-
});
|
|
789
|
-
|
|
790
|
-
yield* writeFileStringAndLog(convexSchemaPath, schemaContents);
|
|
791
|
-
});
|
|
792
|
-
|
|
793
|
-
const generateServices = Effect.gen(function* () {
|
|
794
|
-
const path = yield* Path.Path;
|
|
795
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
796
|
-
|
|
797
|
-
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
798
|
-
|
|
799
|
-
const servicesPath = path.join(confectGeneratedDirectory, "services.ts");
|
|
800
|
-
const generatedSchemaPath = yield* GENERATED_SCHEMA_PATH;
|
|
801
|
-
const schemaImportPath = yield* toModuleImportPath(
|
|
802
|
-
path.relative(
|
|
803
|
-
path.dirname(servicesPath),
|
|
804
|
-
path.join(confectDirectory, generatedSchemaPath),
|
|
805
|
-
),
|
|
806
|
-
);
|
|
807
|
-
|
|
808
|
-
const servicesContentsString = yield* templates.services({
|
|
809
|
-
schemaImportPath,
|
|
810
|
-
});
|
|
811
|
-
|
|
812
|
-
yield* writeFileStringAndLog(servicesPath, servicesContentsString);
|
|
813
|
-
});
|
|
814
|
-
|
|
815
|
-
const generateRefs = Effect.gen(function* () {
|
|
816
|
-
const path = yield* Path.Path;
|
|
817
|
-
const confectDirectory = yield* ConfectDirectory.get;
|
|
818
|
-
|
|
819
|
-
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
820
|
-
const refsPath = path.join(confectGeneratedDirectory, "refs.ts");
|
|
821
|
-
const refsDir = path.dirname(refsPath);
|
|
822
|
-
const generatedSpecPath = yield* GENERATED_SPEC_PATH;
|
|
823
|
-
|
|
824
|
-
const specImportPath = yield* toModuleImportPath(
|
|
825
|
-
path.relative(refsDir, path.join(confectDirectory, generatedSpecPath)),
|
|
826
|
-
);
|
|
827
|
-
|
|
828
|
-
const refsContents = yield* templates.refs({ specImportPath });
|
|
829
|
-
|
|
830
|
-
yield* writeFileStringAndLog(refsPath, refsContents);
|
|
831
|
-
});
|
|
832
|
-
|
|
833
|
-
const logGenerated = (effect: typeof generateHttp) =>
|
|
834
|
-
effect.pipe(
|
|
835
|
-
Effect.tap(
|
|
836
|
-
Option.match({
|
|
837
|
-
onNone: () => Effect.void,
|
|
838
|
-
onSome: ({ change, convexFilePath }) =>
|
|
839
|
-
Match.value(change).pipe(
|
|
840
|
-
Match.when("Added", () => logFileAdded(convexFilePath)),
|
|
841
|
-
Match.when("Modified", () => logFileModified(convexFilePath)),
|
|
842
|
-
Match.when("Unchanged", () => Effect.void),
|
|
843
|
-
Match.exhaustive,
|
|
844
|
-
),
|
|
845
|
-
}),
|
|
846
|
-
),
|
|
847
|
-
);
|