@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/src/CodegenError.ts
DELETED
|
@@ -1,344 +0,0 @@
|
|
|
1
|
-
import { Ansi, AnsiDoc } from "@effect/printer-ansi";
|
|
2
|
-
import { Effect, Match, Option, pipe, Schema } from "effect";
|
|
3
|
-
import { BuildError, isBuildError, renderBuildError } from "./BuildError";
|
|
4
|
-
import { formatPathDoc } from "./log";
|
|
5
|
-
|
|
6
|
-
// --- Variants ---
|
|
7
|
-
|
|
8
|
-
export class MissingImplFileError extends Schema.TaggedError<MissingImplFileError>()(
|
|
9
|
-
"MissingImplFileError",
|
|
10
|
-
{
|
|
11
|
-
specPath: Schema.String,
|
|
12
|
-
expectedImplPath: Schema.String,
|
|
13
|
-
},
|
|
14
|
-
) {}
|
|
15
|
-
|
|
16
|
-
export class MissingSpecFileError extends Schema.TaggedError<MissingSpecFileError>()(
|
|
17
|
-
"MissingSpecFileError",
|
|
18
|
-
{
|
|
19
|
-
implPath: Schema.String,
|
|
20
|
-
expectedSpecPath: Schema.String,
|
|
21
|
-
},
|
|
22
|
-
) {}
|
|
23
|
-
|
|
24
|
-
export class SpecMissingDefaultGroupSpecError extends Schema.TaggedError<SpecMissingDefaultGroupSpecError>()(
|
|
25
|
-
"SpecMissingDefaultGroupSpecError",
|
|
26
|
-
{
|
|
27
|
-
specPath: Schema.String,
|
|
28
|
-
},
|
|
29
|
-
) {}
|
|
30
|
-
|
|
31
|
-
export class SpecRuntimeMismatchError extends Schema.TaggedError<SpecRuntimeMismatchError>()(
|
|
32
|
-
"SpecRuntimeMismatchError",
|
|
33
|
-
{
|
|
34
|
-
specPath: Schema.String,
|
|
35
|
-
expectedRuntime: Schema.Literal("Convex", "Node"),
|
|
36
|
-
actualRuntime: Schema.Literal("Convex", "Node"),
|
|
37
|
-
},
|
|
38
|
-
) {}
|
|
39
|
-
|
|
40
|
-
export class ImplMissingSpecImportError extends Schema.TaggedError<ImplMissingSpecImportError>()(
|
|
41
|
-
"ImplMissingSpecImportError",
|
|
42
|
-
{
|
|
43
|
-
implPath: Schema.String,
|
|
44
|
-
expectedSpecPath: Schema.String,
|
|
45
|
-
},
|
|
46
|
-
) {}
|
|
47
|
-
|
|
48
|
-
export class ImplMissingDefaultLayerError extends Schema.TaggedError<ImplMissingDefaultLayerError>()(
|
|
49
|
-
"ImplMissingDefaultLayerError",
|
|
50
|
-
{
|
|
51
|
-
implPath: Schema.String,
|
|
52
|
-
},
|
|
53
|
-
) {}
|
|
54
|
-
|
|
55
|
-
export class ImplNotFinalizedError extends Schema.TaggedError<ImplNotFinalizedError>()(
|
|
56
|
-
"ImplNotFinalizedError",
|
|
57
|
-
{
|
|
58
|
-
implPath: Schema.String,
|
|
59
|
-
},
|
|
60
|
-
) {}
|
|
61
|
-
|
|
62
|
-
export class ImplMissingFunctionsError extends Schema.TaggedError<ImplMissingFunctionsError>()(
|
|
63
|
-
"ImplMissingFunctionsError",
|
|
64
|
-
{
|
|
65
|
-
implPath: Schema.String,
|
|
66
|
-
groupPath: Schema.String,
|
|
67
|
-
missingFunctionNames: Schema.Array(Schema.String),
|
|
68
|
-
},
|
|
69
|
-
) {}
|
|
70
|
-
|
|
71
|
-
export class SchemaInvalidDefaultExportError extends Schema.TaggedError<SchemaInvalidDefaultExportError>()(
|
|
72
|
-
"SchemaInvalidDefaultExportError",
|
|
73
|
-
{
|
|
74
|
-
schemaPath: Schema.String,
|
|
75
|
-
},
|
|
76
|
-
) {}
|
|
77
|
-
|
|
78
|
-
export class MissingSchemaFileError extends Schema.TaggedError<MissingSchemaFileError>()(
|
|
79
|
-
"MissingSchemaFileError",
|
|
80
|
-
{
|
|
81
|
-
schemaPath: Schema.String,
|
|
82
|
-
},
|
|
83
|
-
) {}
|
|
84
|
-
|
|
85
|
-
export const CodegenError = Schema.Union(
|
|
86
|
-
BuildError,
|
|
87
|
-
MissingImplFileError,
|
|
88
|
-
MissingSpecFileError,
|
|
89
|
-
SpecMissingDefaultGroupSpecError,
|
|
90
|
-
SpecRuntimeMismatchError,
|
|
91
|
-
ImplMissingSpecImportError,
|
|
92
|
-
ImplMissingDefaultLayerError,
|
|
93
|
-
ImplNotFinalizedError,
|
|
94
|
-
ImplMissingFunctionsError,
|
|
95
|
-
SchemaInvalidDefaultExportError,
|
|
96
|
-
MissingSchemaFileError,
|
|
97
|
-
);
|
|
98
|
-
export type CodegenError = typeof CodegenError.Type;
|
|
99
|
-
|
|
100
|
-
export const isCodegenError = (error: unknown): error is CodegenError => {
|
|
101
|
-
if (isBuildError(error)) return true;
|
|
102
|
-
return Schema.is(CodegenError)(error);
|
|
103
|
-
};
|
|
104
|
-
|
|
105
|
-
// --- Per-variant rendering ---
|
|
106
|
-
|
|
107
|
-
const cross = pipe(AnsiDoc.char("✘"), AnsiDoc.annotate(Ansi.red));
|
|
108
|
-
|
|
109
|
-
const stemFromSpecPath = (specPath: string): string => {
|
|
110
|
-
const lastSep = Math.max(
|
|
111
|
-
specPath.lastIndexOf("/"),
|
|
112
|
-
specPath.lastIndexOf("\\"),
|
|
113
|
-
);
|
|
114
|
-
const basename = lastSep < 0 ? specPath : specPath.slice(lastSep + 1);
|
|
115
|
-
return basename.endsWith(".spec.ts")
|
|
116
|
-
? basename.slice(0, -".spec.ts".length)
|
|
117
|
-
: basename;
|
|
118
|
-
};
|
|
119
|
-
|
|
120
|
-
const singleLine = (
|
|
121
|
-
...parts: ReadonlyArray<AnsiDoc.AnsiDoc>
|
|
122
|
-
): AnsiDoc.AnsiDoc => pipe(cross, AnsiDoc.catWithSpace(AnsiDoc.hcat(parts)));
|
|
123
|
-
|
|
124
|
-
const renderMissingImplFileError = (
|
|
125
|
-
error: MissingImplFileError,
|
|
126
|
-
): AnsiDoc.AnsiDoc =>
|
|
127
|
-
singleLine(
|
|
128
|
-
AnsiDoc.text("Spec "),
|
|
129
|
-
formatPathDoc(error.specPath),
|
|
130
|
-
AnsiDoc.text(" has no sibling impl; create "),
|
|
131
|
-
formatPathDoc(error.expectedImplPath),
|
|
132
|
-
AnsiDoc.text(" and default-export a GroupImpl layer from it."),
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const renderMissingSpecFileError = (
|
|
136
|
-
error: MissingSpecFileError,
|
|
137
|
-
): AnsiDoc.AnsiDoc =>
|
|
138
|
-
singleLine(
|
|
139
|
-
AnsiDoc.text("Impl "),
|
|
140
|
-
formatPathDoc(error.implPath),
|
|
141
|
-
AnsiDoc.text(" has no sibling spec; create "),
|
|
142
|
-
formatPathDoc(error.expectedSpecPath),
|
|
143
|
-
AnsiDoc.text(
|
|
144
|
-
" and default-export a GroupSpec from it, or remove the impl.",
|
|
145
|
-
),
|
|
146
|
-
);
|
|
147
|
-
|
|
148
|
-
const renderSpecMissingDefaultGroupSpecError = (
|
|
149
|
-
error: SpecMissingDefaultGroupSpecError,
|
|
150
|
-
): AnsiDoc.AnsiDoc =>
|
|
151
|
-
singleLine(
|
|
152
|
-
AnsiDoc.text("Spec "),
|
|
153
|
-
formatPathDoc(error.specPath),
|
|
154
|
-
AnsiDoc.text(
|
|
155
|
-
" must default-export a GroupSpec; build it with GroupSpec.make() or GroupSpec.makeNode().",
|
|
156
|
-
),
|
|
157
|
-
);
|
|
158
|
-
|
|
159
|
-
const renderSpecRuntimeMismatchError = (
|
|
160
|
-
error: SpecRuntimeMismatchError,
|
|
161
|
-
): AnsiDoc.AnsiDoc => {
|
|
162
|
-
const constructor =
|
|
163
|
-
error.expectedRuntime === "Node"
|
|
164
|
-
? "GroupSpec.makeNode()"
|
|
165
|
-
: "GroupSpec.make()";
|
|
166
|
-
const moveHint =
|
|
167
|
-
error.expectedRuntime === "Node"
|
|
168
|
-
? " or move the file into confect/node/."
|
|
169
|
-
: " or move the file out of confect/node/.";
|
|
170
|
-
return singleLine(
|
|
171
|
-
AnsiDoc.text("Spec "),
|
|
172
|
-
formatPathDoc(error.specPath),
|
|
173
|
-
AnsiDoc.text(
|
|
174
|
-
` declares a ${error.actualRuntime} GroupSpec but its location requires ${error.expectedRuntime}; use ${constructor}${moveHint}`,
|
|
175
|
-
),
|
|
176
|
-
);
|
|
177
|
-
};
|
|
178
|
-
|
|
179
|
-
const renderImplMissingSpecImportError = (
|
|
180
|
-
error: ImplMissingSpecImportError,
|
|
181
|
-
): AnsiDoc.AnsiDoc => {
|
|
182
|
-
const stem = stemFromSpecPath(error.expectedSpecPath);
|
|
183
|
-
return singleLine(
|
|
184
|
-
AnsiDoc.text("Impl "),
|
|
185
|
-
formatPathDoc(error.implPath),
|
|
186
|
-
AnsiDoc.text(
|
|
187
|
-
` does not import its sibling spec; add \`import ${stem} from "./${stem}.spec"\` and pass it to FunctionImpl.make / GroupImpl.make.`,
|
|
188
|
-
),
|
|
189
|
-
);
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
const renderImplMissingDefaultLayerError = (
|
|
193
|
-
error: ImplMissingDefaultLayerError,
|
|
194
|
-
): AnsiDoc.AnsiDoc =>
|
|
195
|
-
singleLine(
|
|
196
|
-
AnsiDoc.text("Impl "),
|
|
197
|
-
formatPathDoc(error.implPath),
|
|
198
|
-
AnsiDoc.text(
|
|
199
|
-
" must default-export a GroupImpl layer; wrap your handlers with `GroupImpl.make(api, groupSpec).pipe(Layer.provide(...))` and `export default` it.",
|
|
200
|
-
),
|
|
201
|
-
);
|
|
202
|
-
|
|
203
|
-
const renderImplNotFinalizedError = (
|
|
204
|
-
error: ImplNotFinalizedError,
|
|
205
|
-
): AnsiDoc.AnsiDoc =>
|
|
206
|
-
singleLine(
|
|
207
|
-
AnsiDoc.text("Impl "),
|
|
208
|
-
formatPathDoc(error.implPath),
|
|
209
|
-
AnsiDoc.text(
|
|
210
|
-
" is not finalized; append `GroupImpl.finalize` to the end of the pipeline (e.g. `GroupImpl.make(api, group).pipe(Layer.provide(...), GroupImpl.finalize)`).",
|
|
211
|
-
),
|
|
212
|
-
);
|
|
213
|
-
|
|
214
|
-
const renderImplMissingFunctionsError = (
|
|
215
|
-
error: ImplMissingFunctionsError,
|
|
216
|
-
): AnsiDoc.AnsiDoc => {
|
|
217
|
-
const names = error.missingFunctionNames.join(", ");
|
|
218
|
-
return singleLine(
|
|
219
|
-
AnsiDoc.text("Impl "),
|
|
220
|
-
formatPathDoc(error.implPath),
|
|
221
|
-
AnsiDoc.text(
|
|
222
|
-
` does not implement every function declared by group \`${error.groupPath}\`; missing: ${names}. Add a \`FunctionImpl.make\` for each missing function and provide it to the group layer.`,
|
|
223
|
-
),
|
|
224
|
-
);
|
|
225
|
-
};
|
|
226
|
-
|
|
227
|
-
const renderSchemaInvalidDefaultExportError = (
|
|
228
|
-
error: SchemaInvalidDefaultExportError,
|
|
229
|
-
): AnsiDoc.AnsiDoc =>
|
|
230
|
-
singleLine(
|
|
231
|
-
AnsiDoc.text("Schema "),
|
|
232
|
-
formatPathDoc(error.schemaPath),
|
|
233
|
-
AnsiDoc.text(
|
|
234
|
-
" must default-export a DatabaseSchema; build it with DatabaseSchema.make({ ... }).",
|
|
235
|
-
),
|
|
236
|
-
);
|
|
237
|
-
|
|
238
|
-
const renderMissingSchemaFileError = (
|
|
239
|
-
error: MissingSchemaFileError,
|
|
240
|
-
): AnsiDoc.AnsiDoc =>
|
|
241
|
-
singleLine(
|
|
242
|
-
AnsiDoc.text("Schema "),
|
|
243
|
-
formatPathDoc(error.schemaPath),
|
|
244
|
-
AnsiDoc.text(
|
|
245
|
-
" is required but is missing; create it and default-export a DatabaseSchema (DatabaseSchema.make({ ... })).",
|
|
246
|
-
),
|
|
247
|
-
);
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Render any {@link CodegenError} into a styled, ready-to-print string.
|
|
251
|
-
* Single-error variants render to a one-line `✘`-prefixed message;
|
|
252
|
-
* `BundleFailedError` (the only multi-error variant) renders to a header
|
|
253
|
-
* plus an esbuild diagnostic block.
|
|
254
|
-
*/
|
|
255
|
-
export const renderCodegenError = (error: CodegenError): string => {
|
|
256
|
-
if (isBuildError(error)) return renderBuildError(error);
|
|
257
|
-
return Match.value(error).pipe(
|
|
258
|
-
Match.tag("MissingImplFileError", (e) =>
|
|
259
|
-
pipe(renderMissingImplFileError(e), AnsiDoc.render({ style: "pretty" })),
|
|
260
|
-
),
|
|
261
|
-
Match.tag("MissingSpecFileError", (e) =>
|
|
262
|
-
pipe(renderMissingSpecFileError(e), AnsiDoc.render({ style: "pretty" })),
|
|
263
|
-
),
|
|
264
|
-
Match.tag("SpecMissingDefaultGroupSpecError", (e) =>
|
|
265
|
-
pipe(
|
|
266
|
-
renderSpecMissingDefaultGroupSpecError(e),
|
|
267
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
268
|
-
),
|
|
269
|
-
),
|
|
270
|
-
Match.tag("SpecRuntimeMismatchError", (e) =>
|
|
271
|
-
pipe(
|
|
272
|
-
renderSpecRuntimeMismatchError(e),
|
|
273
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
274
|
-
),
|
|
275
|
-
),
|
|
276
|
-
Match.tag("ImplMissingSpecImportError", (e) =>
|
|
277
|
-
pipe(
|
|
278
|
-
renderImplMissingSpecImportError(e),
|
|
279
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
280
|
-
),
|
|
281
|
-
),
|
|
282
|
-
Match.tag("ImplMissingDefaultLayerError", (e) =>
|
|
283
|
-
pipe(
|
|
284
|
-
renderImplMissingDefaultLayerError(e),
|
|
285
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
286
|
-
),
|
|
287
|
-
),
|
|
288
|
-
Match.tag("ImplNotFinalizedError", (e) =>
|
|
289
|
-
pipe(renderImplNotFinalizedError(e), AnsiDoc.render({ style: "pretty" })),
|
|
290
|
-
),
|
|
291
|
-
Match.tag("ImplMissingFunctionsError", (e) =>
|
|
292
|
-
pipe(
|
|
293
|
-
renderImplMissingFunctionsError(e),
|
|
294
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
295
|
-
),
|
|
296
|
-
),
|
|
297
|
-
Match.tag("SchemaInvalidDefaultExportError", (e) =>
|
|
298
|
-
pipe(
|
|
299
|
-
renderSchemaInvalidDefaultExportError(e),
|
|
300
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
301
|
-
),
|
|
302
|
-
),
|
|
303
|
-
Match.tag("MissingSchemaFileError", (e) =>
|
|
304
|
-
pipe(
|
|
305
|
-
renderMissingSchemaFileError(e),
|
|
306
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
307
|
-
),
|
|
308
|
-
),
|
|
309
|
-
Match.exhaustive,
|
|
310
|
-
);
|
|
311
|
-
};
|
|
312
|
-
|
|
313
|
-
export const logCodegenError = (error: CodegenError) =>
|
|
314
|
-
Effect.sync(() => console.error(renderCodegenError(error)));
|
|
315
|
-
|
|
316
|
-
// --- Effect combinators ---
|
|
317
|
-
|
|
318
|
-
/**
|
|
319
|
-
* Log any {@link CodegenError} thrown by `effect` and propagate the failure
|
|
320
|
-
* unchanged so the caller's error channel is preserved (used by the
|
|
321
|
-
* `codegen` command, which needs the failure to surface as a non-zero exit
|
|
322
|
-
* code).
|
|
323
|
-
*/
|
|
324
|
-
export const tapAndLog = <A, E, R>(
|
|
325
|
-
effect: Effect.Effect<A, E, R>,
|
|
326
|
-
): Effect.Effect<A, E, R> =>
|
|
327
|
-
effect.pipe(
|
|
328
|
-
Effect.tapError((error) =>
|
|
329
|
-
isCodegenError(error) ? logCodegenError(error) : Effect.void,
|
|
330
|
-
),
|
|
331
|
-
);
|
|
332
|
-
|
|
333
|
-
/**
|
|
334
|
-
* Catch any {@link CodegenError} thrown by `effect`, log it, and resolve to
|
|
335
|
-
* `Option.none()` (used by the `dev` command's sync loop, which continues
|
|
336
|
-
* after a failed sync rather than exiting). Success resolves to
|
|
337
|
-
* `Option.some(value)`.
|
|
338
|
-
*/
|
|
339
|
-
export const catchAndLog = <A, E, R>(
|
|
340
|
-
effect: Effect.Effect<A, E, R>,
|
|
341
|
-
): Effect.Effect<Option.Option<A>, Exclude<E, CodegenError>, R> =>
|
|
342
|
-
Effect.catchIf(Effect.map(effect, Option.some<A>), isCodegenError, (error) =>
|
|
343
|
-
logCodegenError(error).pipe(Effect.as(Option.none<A>())),
|
|
344
|
-
) as Effect.Effect<Option.Option<A>, Exclude<E, CodegenError>, R>;
|
package/src/ConfectDirectory.ts
DELETED
|
@@ -1,42 +0,0 @@
|
|
|
1
|
-
import { FileSystem, Path } from "@effect/platform";
|
|
2
|
-
import { Effect, Ref, Schema } from "effect";
|
|
3
|
-
import { ConvexDirectory } from "./ConvexDirectory";
|
|
4
|
-
|
|
5
|
-
export class ConfectDirectory extends Effect.Service<ConfectDirectory>()(
|
|
6
|
-
"@confect/cli/ConfectDirectory",
|
|
7
|
-
{
|
|
8
|
-
effect: Effect.gen(function* () {
|
|
9
|
-
const convexDirectory = yield* findConfectDirectory;
|
|
10
|
-
|
|
11
|
-
const ref = yield* Ref.make<string>(convexDirectory);
|
|
12
|
-
|
|
13
|
-
return { get: Ref.get(ref) } as const;
|
|
14
|
-
}),
|
|
15
|
-
dependencies: [ConvexDirectory.Default],
|
|
16
|
-
accessors: true,
|
|
17
|
-
},
|
|
18
|
-
) {}
|
|
19
|
-
|
|
20
|
-
export class ConfectDirectoryNotFoundError extends Schema.TaggedError<ConfectDirectoryNotFoundError>()(
|
|
21
|
-
"ConfectDirectoryNotFoundError",
|
|
22
|
-
{},
|
|
23
|
-
) {
|
|
24
|
-
override get message(): string {
|
|
25
|
-
return "Could not find Confect directory";
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export const findConfectDirectory = Effect.gen(function* () {
|
|
30
|
-
const fs = yield* FileSystem.FileSystem;
|
|
31
|
-
const path = yield* Path.Path;
|
|
32
|
-
|
|
33
|
-
const convexDirectory = yield* ConvexDirectory.get;
|
|
34
|
-
|
|
35
|
-
const confectDirectory = path.join(path.dirname(convexDirectory), "confect");
|
|
36
|
-
|
|
37
|
-
if (yield* fs.exists(confectDirectory)) {
|
|
38
|
-
return confectDirectory;
|
|
39
|
-
} else {
|
|
40
|
-
return yield* new ConfectDirectoryNotFoundError();
|
|
41
|
-
}
|
|
42
|
-
});
|
package/src/ConvexDirectory.ts
DELETED
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
import { FileSystem, Path } from "@effect/platform";
|
|
2
|
-
import { Effect, Option, Ref, Schema } from "effect";
|
|
3
|
-
import { ProjectRoot } from "./ProjectRoot";
|
|
4
|
-
|
|
5
|
-
export class ConvexDirectory extends Effect.Service<ConvexDirectory>()(
|
|
6
|
-
"@confect/cli/ConvexDirectory",
|
|
7
|
-
{
|
|
8
|
-
effect: Effect.gen(function* () {
|
|
9
|
-
const convexDirectory = yield* findConvexDirectory;
|
|
10
|
-
|
|
11
|
-
const ref = yield* Ref.make<string>(convexDirectory);
|
|
12
|
-
|
|
13
|
-
return { get: Ref.get(ref) } as const;
|
|
14
|
-
}),
|
|
15
|
-
dependencies: [ProjectRoot.Default],
|
|
16
|
-
accessors: true,
|
|
17
|
-
},
|
|
18
|
-
) {}
|
|
19
|
-
|
|
20
|
-
export class ConvexDirectoryNotFoundError extends Schema.TaggedError<ConvexDirectoryNotFoundError>()(
|
|
21
|
-
"ConvexDirectoryNotFoundError",
|
|
22
|
-
{},
|
|
23
|
-
) {
|
|
24
|
-
override get message(): string {
|
|
25
|
-
return "Could not find Convex directory";
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
/**
|
|
30
|
-
* Schema for `convex.json` configuration file.
|
|
31
|
-
* @see https://docs.convex.dev/production/project-configuration
|
|
32
|
-
*/
|
|
33
|
-
const ConvexJsonConfig = Schema.parseJson(
|
|
34
|
-
Schema.Struct({
|
|
35
|
-
functions: Schema.optional(Schema.String),
|
|
36
|
-
}),
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
const findConvexDirectory = Effect.gen(function* () {
|
|
40
|
-
const fs = yield* FileSystem.FileSystem;
|
|
41
|
-
const path = yield* Path.Path;
|
|
42
|
-
|
|
43
|
-
const projectRoot = yield* ProjectRoot.get;
|
|
44
|
-
|
|
45
|
-
const defaultPath = path.join(projectRoot, "convex");
|
|
46
|
-
|
|
47
|
-
const convexJsonPath = path.join(projectRoot, "convex.json");
|
|
48
|
-
|
|
49
|
-
const convexDirectory = yield* Effect.if(fs.exists(convexJsonPath), {
|
|
50
|
-
onTrue: () =>
|
|
51
|
-
fs.readFileString(convexJsonPath).pipe(
|
|
52
|
-
Effect.andThen(Schema.decodeOption(ConvexJsonConfig)),
|
|
53
|
-
Effect.map((config) =>
|
|
54
|
-
Option.fromNullable(config.functions).pipe(
|
|
55
|
-
Option.map((functionsDir) => path.join(projectRoot, functionsDir)),
|
|
56
|
-
),
|
|
57
|
-
),
|
|
58
|
-
Effect.andThen(Option.getOrElse(() => defaultPath)),
|
|
59
|
-
),
|
|
60
|
-
onFalse: () => Effect.succeed(defaultPath),
|
|
61
|
-
});
|
|
62
|
-
|
|
63
|
-
if (yield* fs.exists(convexDirectory)) {
|
|
64
|
-
return convexDirectory;
|
|
65
|
-
} else {
|
|
66
|
-
return yield* new ConvexDirectoryNotFoundError();
|
|
67
|
-
}
|
|
68
|
-
});
|
package/src/FunctionPath.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import { Schema } from "effect";
|
|
2
|
-
import * as GroupPath from "./GroupPath";
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* The path to a function in the Confect API.
|
|
6
|
-
*/
|
|
7
|
-
export class FunctionPath extends Schema.Class<FunctionPath>("FunctionPath")({
|
|
8
|
-
groupPath: GroupPath.GroupPath,
|
|
9
|
-
name: Schema.NonEmptyString,
|
|
10
|
-
}) {}
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Get the group path from a function path.
|
|
14
|
-
*/
|
|
15
|
-
export const groupPath = (functionPath: FunctionPath): GroupPath.GroupPath =>
|
|
16
|
-
functionPath.groupPath;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Get the function name from a function path.
|
|
20
|
-
*/
|
|
21
|
-
export const name = (functionPath: FunctionPath): string => functionPath.name;
|
|
22
|
-
|
|
23
|
-
/**
|
|
24
|
-
* Get the function path as a string.
|
|
25
|
-
*/
|
|
26
|
-
export const toString = (functionPath: FunctionPath): string =>
|
|
27
|
-
`${GroupPath.toString(functionPath.groupPath)}.${functionPath.name}`;
|
package/src/FunctionPaths.ts
DELETED
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
import type { GroupSpec, Spec } from "@confect/core";
|
|
2
|
-
import { HashSet, Option, pipe, Record, Schema } from "effect";
|
|
3
|
-
import * as FunctionPath from "./FunctionPath";
|
|
4
|
-
import * as GroupPath from "./GroupPath";
|
|
5
|
-
import * as GroupPaths from "./GroupPaths";
|
|
6
|
-
|
|
7
|
-
export const FunctionPaths = Schema.HashSetFromSelf(
|
|
8
|
-
FunctionPath.FunctionPath,
|
|
9
|
-
).pipe(Schema.brand("@confect/cli/FunctionPaths"));
|
|
10
|
-
export type FunctionPaths = typeof FunctionPaths.Type;
|
|
11
|
-
|
|
12
|
-
export const make = (spec: Spec.AnyWithProps): FunctionPaths =>
|
|
13
|
-
makeHelper(spec.groups, Option.none(), FunctionPaths.make(HashSet.empty()));
|
|
14
|
-
|
|
15
|
-
const makeHelper = (
|
|
16
|
-
groups: {
|
|
17
|
-
[key: string]: GroupSpec.AnyWithProps;
|
|
18
|
-
},
|
|
19
|
-
currentGroupPath: Option.Option<GroupPath.GroupPath>,
|
|
20
|
-
apiPaths: FunctionPaths,
|
|
21
|
-
): FunctionPaths =>
|
|
22
|
-
Record.reduce(groups, apiPaths, (acc, group, groupName) => {
|
|
23
|
-
const groupPath = Option.match(currentGroupPath, {
|
|
24
|
-
onNone: () => GroupPath.make([groupName]),
|
|
25
|
-
onSome: (path) => GroupPath.append(path, groupName),
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
const accWithFunctions = Record.reduce(
|
|
29
|
-
group.functions,
|
|
30
|
-
acc,
|
|
31
|
-
(acc_, _fn, functionName) =>
|
|
32
|
-
FunctionPaths.make(
|
|
33
|
-
HashSet.add(
|
|
34
|
-
acc_,
|
|
35
|
-
FunctionPath.FunctionPath.make({
|
|
36
|
-
groupPath,
|
|
37
|
-
name: functionName,
|
|
38
|
-
}),
|
|
39
|
-
),
|
|
40
|
-
),
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
return makeHelper(group.groups, Option.some(groupPath), accWithFunctions);
|
|
44
|
-
});
|
|
45
|
-
|
|
46
|
-
export const groupPaths = (
|
|
47
|
-
functionPaths: FunctionPaths,
|
|
48
|
-
): GroupPaths.GroupPaths =>
|
|
49
|
-
pipe(
|
|
50
|
-
functionPaths,
|
|
51
|
-
HashSet.map(FunctionPath.groupPath),
|
|
52
|
-
GroupPaths.GroupPaths.make,
|
|
53
|
-
);
|
|
54
|
-
|
|
55
|
-
export const diff = (
|
|
56
|
-
previousFunctions: FunctionPaths,
|
|
57
|
-
currentFunctions: FunctionPaths,
|
|
58
|
-
): {
|
|
59
|
-
functionsAdded: FunctionPaths;
|
|
60
|
-
functionsRemoved: FunctionPaths;
|
|
61
|
-
groupsRemoved: GroupPaths.GroupPaths;
|
|
62
|
-
groupsAdded: GroupPaths.GroupPaths;
|
|
63
|
-
groupsChanged: GroupPaths.GroupPaths;
|
|
64
|
-
} => {
|
|
65
|
-
const currentGroups = groupPaths(currentFunctions);
|
|
66
|
-
const previousGroups = groupPaths(previousFunctions);
|
|
67
|
-
|
|
68
|
-
const groupsAdded = GroupPaths.GroupPaths.make(
|
|
69
|
-
HashSet.difference(currentGroups, previousGroups),
|
|
70
|
-
);
|
|
71
|
-
const groupsRemoved = GroupPaths.GroupPaths.make(
|
|
72
|
-
HashSet.difference(previousGroups, currentGroups),
|
|
73
|
-
);
|
|
74
|
-
|
|
75
|
-
const functionsAdded = FunctionPaths.make(
|
|
76
|
-
HashSet.difference(currentFunctions, previousFunctions),
|
|
77
|
-
);
|
|
78
|
-
const existingGroupsToWhichFunctionsWereAdded = GroupPaths.GroupPaths.make(
|
|
79
|
-
HashSet.intersection(currentGroups, groupPaths(functionsAdded)),
|
|
80
|
-
);
|
|
81
|
-
|
|
82
|
-
const functionsRemoved = FunctionPaths.make(
|
|
83
|
-
HashSet.difference(previousFunctions, currentFunctions),
|
|
84
|
-
);
|
|
85
|
-
const existingGroupsToWhichFunctionsWereRemoved = GroupPaths.GroupPaths.make(
|
|
86
|
-
HashSet.intersection(previousGroups, groupPaths(functionsRemoved)),
|
|
87
|
-
);
|
|
88
|
-
|
|
89
|
-
const groupsChanged = pipe(
|
|
90
|
-
existingGroupsToWhichFunctionsWereAdded,
|
|
91
|
-
HashSet.union(existingGroupsToWhichFunctionsWereRemoved),
|
|
92
|
-
HashSet.difference(HashSet.union(groupsAdded, groupsRemoved)),
|
|
93
|
-
GroupPaths.GroupPaths.make,
|
|
94
|
-
);
|
|
95
|
-
|
|
96
|
-
return {
|
|
97
|
-
functionsAdded,
|
|
98
|
-
functionsRemoved,
|
|
99
|
-
groupsRemoved,
|
|
100
|
-
groupsAdded,
|
|
101
|
-
groupsChanged,
|
|
102
|
-
};
|
|
103
|
-
};
|
package/src/GroupPath.ts
DELETED
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import { type GroupSpec, type Spec } from "@confect/core";
|
|
2
|
-
import { Path } from "@effect/platform";
|
|
3
|
-
import {
|
|
4
|
-
Array,
|
|
5
|
-
Data,
|
|
6
|
-
Effect,
|
|
7
|
-
Option,
|
|
8
|
-
pipe,
|
|
9
|
-
Record,
|
|
10
|
-
Schema,
|
|
11
|
-
String,
|
|
12
|
-
} from "effect";
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* The path to a group in the Confect API.
|
|
16
|
-
*/
|
|
17
|
-
export class GroupPath extends Schema.Class<GroupPath>("GroupPath")({
|
|
18
|
-
pathSegments: Schema.Data(Schema.NonEmptyArray(Schema.NonEmptyString)),
|
|
19
|
-
}) {}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Create a GroupPath from path segments.
|
|
23
|
-
*/
|
|
24
|
-
export const make = (pathSegments: readonly [string, ...string[]]): GroupPath =>
|
|
25
|
-
GroupPath.make({ pathSegments: Data.array(pathSegments) });
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Append a group name to a GroupPath to create a new GroupPath.
|
|
29
|
-
*/
|
|
30
|
-
export const append = (groupPath: GroupPath, groupName: string): GroupPath =>
|
|
31
|
-
make([...groupPath.pathSegments, groupName]);
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Expects a path string of the form `./group1/group2.ts`, relative to the Convex functions directory.
|
|
35
|
-
*/
|
|
36
|
-
export const fromGroupModulePath = (groupModulePath: string) =>
|
|
37
|
-
Effect.gen(function* () {
|
|
38
|
-
const path = yield* Path.Path;
|
|
39
|
-
|
|
40
|
-
const { dir, name, ext } = path.parse(groupModulePath);
|
|
41
|
-
|
|
42
|
-
if (ext === ".ts") {
|
|
43
|
-
const dirSegments = Array.filter(
|
|
44
|
-
String.split(dir, path.sep),
|
|
45
|
-
String.isNonEmpty,
|
|
46
|
-
);
|
|
47
|
-
yield* Effect.logDebug(Array.append(dirSegments, name));
|
|
48
|
-
return make(Array.append(dirSegments, name));
|
|
49
|
-
} else {
|
|
50
|
-
return yield* new GroupModulePathIsNotATypeScriptFileError({
|
|
51
|
-
path: groupModulePath,
|
|
52
|
-
});
|
|
53
|
-
}
|
|
54
|
-
});
|
|
55
|
-
|
|
56
|
-
/**
|
|
57
|
-
* Get the module path for a group, relative to the Convex functions directory.
|
|
58
|
-
*/
|
|
59
|
-
export const modulePath = (groupPath: GroupPath) =>
|
|
60
|
-
Effect.gen(function* () {
|
|
61
|
-
const path = yield* Path.Path;
|
|
62
|
-
|
|
63
|
-
return path.join(...groupPath.pathSegments) + ".ts";
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
export const getGroupSpec = (
|
|
67
|
-
spec: Spec.AnyWithProps,
|
|
68
|
-
groupPath: GroupPath,
|
|
69
|
-
): Option.Option<GroupSpec.AnyWithProps> =>
|
|
70
|
-
pipe(
|
|
71
|
-
groupPath.pathSegments,
|
|
72
|
-
Array.matchLeft({
|
|
73
|
-
onEmpty: () => Option.none(),
|
|
74
|
-
onNonEmpty: (head, tail) =>
|
|
75
|
-
pipe(
|
|
76
|
-
Record.get(spec.groups, head),
|
|
77
|
-
Option.flatMap((group) =>
|
|
78
|
-
Array.isNonEmptyArray(tail)
|
|
79
|
-
? getGroupSpecHelper(group, tail)
|
|
80
|
-
: Option.some(group),
|
|
81
|
-
),
|
|
82
|
-
),
|
|
83
|
-
}),
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
const getGroupSpecHelper = (
|
|
87
|
-
group: GroupSpec.AnyWithProps,
|
|
88
|
-
remainingPath: ReadonlyArray<string>,
|
|
89
|
-
): Option.Option<GroupSpec.AnyWithProps> =>
|
|
90
|
-
pipe(
|
|
91
|
-
remainingPath,
|
|
92
|
-
Array.matchLeft({
|
|
93
|
-
onEmpty: () => Option.some(group),
|
|
94
|
-
onNonEmpty: (head, tail) =>
|
|
95
|
-
pipe(
|
|
96
|
-
Record.get(group.groups, head),
|
|
97
|
-
Option.flatMap((subGroup) =>
|
|
98
|
-
Array.isNonEmptyArray(tail)
|
|
99
|
-
? getGroupSpecHelper(subGroup, tail)
|
|
100
|
-
: Option.some(subGroup),
|
|
101
|
-
),
|
|
102
|
-
),
|
|
103
|
-
}),
|
|
104
|
-
);
|
|
105
|
-
|
|
106
|
-
export const toString = (groupPath: GroupPath) =>
|
|
107
|
-
Array.join(groupPath.pathSegments, ".");
|
|
108
|
-
|
|
109
|
-
export class GroupModulePathIsNotATypeScriptFileError extends Schema.TaggedError<GroupModulePathIsNotATypeScriptFileError>()(
|
|
110
|
-
"GroupModulePathIsNotATypeScriptFileError",
|
|
111
|
-
{
|
|
112
|
-
path: Schema.NonEmptyString,
|
|
113
|
-
},
|
|
114
|
-
) {
|
|
115
|
-
override get message(): string {
|
|
116
|
-
return `Expected group module path to end with .ts, got ${this.path}`;
|
|
117
|
-
}
|
|
118
|
-
}
|