@confect/cli 9.0.0-next.9 → 9.0.1
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 +173 -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 +6 -24
- 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/CodegenError.ts
DELETED
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
import * as Ansi from "@effect/printer-ansi/Ansi";
|
|
2
|
-
import * as AnsiDoc from "@effect/printer-ansi/AnsiDoc";
|
|
3
|
-
import { pipe } from "effect/Function";
|
|
4
|
-
import * as Effect from "effect/Effect";
|
|
5
|
-
import * as Match from "effect/Match";
|
|
6
|
-
import * as Option from "effect/Option";
|
|
7
|
-
import * as Schema from "effect/Schema";
|
|
8
|
-
import { BuildError, isBuildError, renderBuildError } from "./BuildError";
|
|
9
|
-
import { formatPathDoc } from "./log";
|
|
10
|
-
|
|
11
|
-
// --- Variants ---
|
|
12
|
-
|
|
13
|
-
export class MissingImplFileError extends Schema.TaggedError<MissingImplFileError>()(
|
|
14
|
-
"MissingImplFileError",
|
|
15
|
-
{
|
|
16
|
-
specPath: Schema.String,
|
|
17
|
-
expectedImplPath: Schema.String,
|
|
18
|
-
},
|
|
19
|
-
) {}
|
|
20
|
-
|
|
21
|
-
export class MissingSpecFileError extends Schema.TaggedError<MissingSpecFileError>()(
|
|
22
|
-
"MissingSpecFileError",
|
|
23
|
-
{
|
|
24
|
-
implPath: Schema.String,
|
|
25
|
-
expectedSpecPath: Schema.String,
|
|
26
|
-
},
|
|
27
|
-
) {}
|
|
28
|
-
|
|
29
|
-
export class SpecMissingDefaultGroupSpecError extends Schema.TaggedError<SpecMissingDefaultGroupSpecError>()(
|
|
30
|
-
"SpecMissingDefaultGroupSpecError",
|
|
31
|
-
{
|
|
32
|
-
specPath: Schema.String,
|
|
33
|
-
},
|
|
34
|
-
) {}
|
|
35
|
-
|
|
36
|
-
export class ImplMissingSpecImportError extends Schema.TaggedError<ImplMissingSpecImportError>()(
|
|
37
|
-
"ImplMissingSpecImportError",
|
|
38
|
-
{
|
|
39
|
-
implPath: Schema.String,
|
|
40
|
-
expectedSpecPath: Schema.String,
|
|
41
|
-
},
|
|
42
|
-
) {}
|
|
43
|
-
|
|
44
|
-
export class ImplMissingDefaultLayerError extends Schema.TaggedError<ImplMissingDefaultLayerError>()(
|
|
45
|
-
"ImplMissingDefaultLayerError",
|
|
46
|
-
{
|
|
47
|
-
implPath: Schema.String,
|
|
48
|
-
},
|
|
49
|
-
) {}
|
|
50
|
-
|
|
51
|
-
export class ImplNotFinalizedError extends Schema.TaggedError<ImplNotFinalizedError>()(
|
|
52
|
-
"ImplNotFinalizedError",
|
|
53
|
-
{
|
|
54
|
-
implPath: Schema.String,
|
|
55
|
-
},
|
|
56
|
-
) {}
|
|
57
|
-
|
|
58
|
-
export class ImplMissingFunctionsError extends Schema.TaggedError<ImplMissingFunctionsError>()(
|
|
59
|
-
"ImplMissingFunctionsError",
|
|
60
|
-
{
|
|
61
|
-
implPath: Schema.String,
|
|
62
|
-
groupPath: Schema.String,
|
|
63
|
-
missingFunctionNames: Schema.Array(Schema.String),
|
|
64
|
-
},
|
|
65
|
-
) {}
|
|
66
|
-
|
|
67
|
-
export class ParentChildNameCollisionError extends Schema.TaggedError<ParentChildNameCollisionError>()(
|
|
68
|
-
"ParentChildNameCollisionError",
|
|
69
|
-
{
|
|
70
|
-
parentSpecPath: Schema.String,
|
|
71
|
-
childSpecPath: Schema.String,
|
|
72
|
-
collisionName: Schema.String,
|
|
73
|
-
collisionKind: Schema.Literal("function", "group"),
|
|
74
|
-
},
|
|
75
|
-
) {}
|
|
76
|
-
|
|
77
|
-
export class InvalidTableDefaultExportError extends Schema.TaggedError<InvalidTableDefaultExportError>()(
|
|
78
|
-
"InvalidTableDefaultExportError",
|
|
79
|
-
{
|
|
80
|
-
tablePath: Schema.String,
|
|
81
|
-
},
|
|
82
|
-
) {}
|
|
83
|
-
|
|
84
|
-
export class InvalidTableFilenameError extends Schema.TaggedError<InvalidTableFilenameError>()(
|
|
85
|
-
"InvalidTableFilenameError",
|
|
86
|
-
{
|
|
87
|
-
tablePath: Schema.String,
|
|
88
|
-
reason: Schema.String,
|
|
89
|
-
},
|
|
90
|
-
) {}
|
|
91
|
-
|
|
92
|
-
export class DuplicateTableNameError extends Schema.TaggedError<DuplicateTableNameError>()(
|
|
93
|
-
"DuplicateTableNameError",
|
|
94
|
-
{
|
|
95
|
-
// Every table name that more than one file resolves to, each paired with
|
|
96
|
-
// the colliding file paths. All collisions are captured in a single pass
|
|
97
|
-
// so the user can fix them together rather than re-running codegen once
|
|
98
|
-
// per conflict.
|
|
99
|
-
collisions: Schema.Array(
|
|
100
|
-
Schema.Struct({
|
|
101
|
-
tableName: Schema.String,
|
|
102
|
-
tablePaths: Schema.Array(Schema.String),
|
|
103
|
-
}),
|
|
104
|
-
),
|
|
105
|
-
},
|
|
106
|
-
) {}
|
|
107
|
-
|
|
108
|
-
export class LegacySchemaFileError extends Schema.TaggedError<LegacySchemaFileError>()(
|
|
109
|
-
"LegacySchemaFileError",
|
|
110
|
-
{
|
|
111
|
-
schemaPath: Schema.String,
|
|
112
|
-
},
|
|
113
|
-
) {}
|
|
114
|
-
|
|
115
|
-
export const CodegenError = Schema.Union(
|
|
116
|
-
BuildError,
|
|
117
|
-
MissingImplFileError,
|
|
118
|
-
MissingSpecFileError,
|
|
119
|
-
SpecMissingDefaultGroupSpecError,
|
|
120
|
-
ImplMissingSpecImportError,
|
|
121
|
-
ImplMissingDefaultLayerError,
|
|
122
|
-
ImplNotFinalizedError,
|
|
123
|
-
ImplMissingFunctionsError,
|
|
124
|
-
ParentChildNameCollisionError,
|
|
125
|
-
InvalidTableDefaultExportError,
|
|
126
|
-
InvalidTableFilenameError,
|
|
127
|
-
DuplicateTableNameError,
|
|
128
|
-
LegacySchemaFileError,
|
|
129
|
-
);
|
|
130
|
-
export type CodegenError = typeof CodegenError.Type;
|
|
131
|
-
|
|
132
|
-
export const isCodegenError = (error: unknown): error is CodegenError => {
|
|
133
|
-
if (isBuildError(error)) return true;
|
|
134
|
-
return Schema.is(CodegenError)(error);
|
|
135
|
-
};
|
|
136
|
-
|
|
137
|
-
// --- Per-variant rendering ---
|
|
138
|
-
|
|
139
|
-
const cross = pipe(AnsiDoc.char("✘"), AnsiDoc.annotate(Ansi.red));
|
|
140
|
-
|
|
141
|
-
const stemFromSpecPath = (specPath: string): string => {
|
|
142
|
-
const lastSep = Math.max(
|
|
143
|
-
specPath.lastIndexOf("/"),
|
|
144
|
-
specPath.lastIndexOf("\\"),
|
|
145
|
-
);
|
|
146
|
-
const basename = lastSep < 0 ? specPath : specPath.slice(lastSep + 1);
|
|
147
|
-
return basename.endsWith(".spec.ts")
|
|
148
|
-
? basename.slice(0, -".spec.ts".length)
|
|
149
|
-
: basename;
|
|
150
|
-
};
|
|
151
|
-
|
|
152
|
-
const singleLine = (
|
|
153
|
-
...parts: ReadonlyArray<AnsiDoc.AnsiDoc>
|
|
154
|
-
): AnsiDoc.AnsiDoc => pipe(cross, AnsiDoc.catWithSpace(AnsiDoc.hcat(parts)));
|
|
155
|
-
|
|
156
|
-
const renderMissingImplFileError = (
|
|
157
|
-
error: MissingImplFileError,
|
|
158
|
-
): AnsiDoc.AnsiDoc =>
|
|
159
|
-
singleLine(
|
|
160
|
-
AnsiDoc.text("Spec "),
|
|
161
|
-
formatPathDoc(error.specPath),
|
|
162
|
-
AnsiDoc.text(" has no sibling impl; create "),
|
|
163
|
-
formatPathDoc(error.expectedImplPath),
|
|
164
|
-
AnsiDoc.text(" and default-export a GroupImpl layer from it."),
|
|
165
|
-
);
|
|
166
|
-
|
|
167
|
-
const renderMissingSpecFileError = (
|
|
168
|
-
error: MissingSpecFileError,
|
|
169
|
-
): AnsiDoc.AnsiDoc =>
|
|
170
|
-
singleLine(
|
|
171
|
-
AnsiDoc.text("Impl "),
|
|
172
|
-
formatPathDoc(error.implPath),
|
|
173
|
-
AnsiDoc.text(" has no sibling spec; create "),
|
|
174
|
-
formatPathDoc(error.expectedSpecPath),
|
|
175
|
-
AnsiDoc.text(
|
|
176
|
-
" and default-export a GroupSpec from it, or remove the impl.",
|
|
177
|
-
),
|
|
178
|
-
);
|
|
179
|
-
|
|
180
|
-
const renderSpecMissingDefaultGroupSpecError = (
|
|
181
|
-
error: SpecMissingDefaultGroupSpecError,
|
|
182
|
-
): AnsiDoc.AnsiDoc =>
|
|
183
|
-
singleLine(
|
|
184
|
-
AnsiDoc.text("Spec "),
|
|
185
|
-
formatPathDoc(error.specPath),
|
|
186
|
-
AnsiDoc.text(
|
|
187
|
-
" must default-export a GroupSpec; build it with GroupSpec.make() or GroupSpec.makeNode().",
|
|
188
|
-
),
|
|
189
|
-
);
|
|
190
|
-
|
|
191
|
-
const renderImplMissingSpecImportError = (
|
|
192
|
-
error: ImplMissingSpecImportError,
|
|
193
|
-
): AnsiDoc.AnsiDoc => {
|
|
194
|
-
const stem = stemFromSpecPath(error.expectedSpecPath);
|
|
195
|
-
return singleLine(
|
|
196
|
-
AnsiDoc.text("Impl "),
|
|
197
|
-
formatPathDoc(error.implPath),
|
|
198
|
-
AnsiDoc.text(
|
|
199
|
-
` does not import its sibling spec; add \`import ${stem} from "./${stem}.spec"\` and pass it to FunctionImpl.make / GroupImpl.make.`,
|
|
200
|
-
),
|
|
201
|
-
);
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
const renderImplMissingDefaultLayerError = (
|
|
205
|
-
error: ImplMissingDefaultLayerError,
|
|
206
|
-
): AnsiDoc.AnsiDoc =>
|
|
207
|
-
singleLine(
|
|
208
|
-
AnsiDoc.text("Impl "),
|
|
209
|
-
formatPathDoc(error.implPath),
|
|
210
|
-
AnsiDoc.text(
|
|
211
|
-
" must default-export a GroupImpl layer; wrap your handlers with `GroupImpl.make(databaseSchema, groupSpec).pipe(Layer.provide(...))` and `export default` it.",
|
|
212
|
-
),
|
|
213
|
-
);
|
|
214
|
-
|
|
215
|
-
const renderImplNotFinalizedError = (
|
|
216
|
-
error: ImplNotFinalizedError,
|
|
217
|
-
): AnsiDoc.AnsiDoc =>
|
|
218
|
-
singleLine(
|
|
219
|
-
AnsiDoc.text("Impl "),
|
|
220
|
-
formatPathDoc(error.implPath),
|
|
221
|
-
AnsiDoc.text(
|
|
222
|
-
" is not finalized; append `GroupImpl.finalize` to the end of the pipeline (e.g. `GroupImpl.make(databaseSchema, group).pipe(Layer.provide(...), GroupImpl.finalize)`).",
|
|
223
|
-
),
|
|
224
|
-
);
|
|
225
|
-
|
|
226
|
-
const renderImplMissingFunctionsError = (
|
|
227
|
-
error: ImplMissingFunctionsError,
|
|
228
|
-
): AnsiDoc.AnsiDoc => {
|
|
229
|
-
const names = error.missingFunctionNames.join(", ");
|
|
230
|
-
return singleLine(
|
|
231
|
-
AnsiDoc.text("Impl "),
|
|
232
|
-
formatPathDoc(error.implPath),
|
|
233
|
-
AnsiDoc.text(
|
|
234
|
-
` 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.`,
|
|
235
|
-
),
|
|
236
|
-
);
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
const renderInvalidTableDefaultExportError = (
|
|
240
|
-
error: InvalidTableDefaultExportError,
|
|
241
|
-
): AnsiDoc.AnsiDoc =>
|
|
242
|
-
singleLine(
|
|
243
|
-
AnsiDoc.text("Table "),
|
|
244
|
-
formatPathDoc(error.tablePath),
|
|
245
|
-
AnsiDoc.text(
|
|
246
|
-
" must default-export a Table (e.g. `export default Table.make({ ... })`); convert any named export to a default export.",
|
|
247
|
-
),
|
|
248
|
-
);
|
|
249
|
-
|
|
250
|
-
const renderInvalidTableFilenameError = (
|
|
251
|
-
error: InvalidTableFilenameError,
|
|
252
|
-
): AnsiDoc.AnsiDoc =>
|
|
253
|
-
singleLine(
|
|
254
|
-
AnsiDoc.text("Table "),
|
|
255
|
-
formatPathDoc(error.tablePath),
|
|
256
|
-
AnsiDoc.text(
|
|
257
|
-
` has an invalid filename: ${error.reason} Convex table names must start with a letter and contain only letters, numbers, and underscores; leading underscores are reserved for system tables.`,
|
|
258
|
-
),
|
|
259
|
-
);
|
|
260
|
-
|
|
261
|
-
const renderDuplicateTableNameError = (
|
|
262
|
-
error: DuplicateTableNameError,
|
|
263
|
-
): AnsiDoc.AnsiDoc => {
|
|
264
|
-
const conflicts = error.collisions
|
|
265
|
-
.map(
|
|
266
|
-
({ tableName, tablePaths }) =>
|
|
267
|
-
`\`${tableName}\` (${tablePaths.join(", ")})`,
|
|
268
|
-
)
|
|
269
|
-
.join("; ");
|
|
270
|
-
return singleLine(
|
|
271
|
-
AnsiDoc.text(
|
|
272
|
-
`Multiple files under \`confect/tables/\` resolve to the same table name. Table names are derived from filenames, so each must be unique across the directory (including subdirectories); rename or remove all but one. Conflicts: ${conflicts}.`,
|
|
273
|
-
),
|
|
274
|
-
);
|
|
275
|
-
};
|
|
276
|
-
|
|
277
|
-
const renderLegacySchemaFileError = (
|
|
278
|
-
error: LegacySchemaFileError,
|
|
279
|
-
): AnsiDoc.AnsiDoc =>
|
|
280
|
-
singleLine(
|
|
281
|
-
AnsiDoc.text("Found a legacy "),
|
|
282
|
-
formatPathDoc(error.schemaPath),
|
|
283
|
-
AnsiDoc.text(
|
|
284
|
-
". Delete it: tables in `confect/tables/*.ts` are now the single source of truth, and the runtime schema is generated as `confect/_generated/schema.ts`.",
|
|
285
|
-
),
|
|
286
|
-
);
|
|
287
|
-
|
|
288
|
-
const renderParentChildNameCollisionError = (
|
|
289
|
-
error: ParentChildNameCollisionError,
|
|
290
|
-
): AnsiDoc.AnsiDoc =>
|
|
291
|
-
singleLine(
|
|
292
|
-
AnsiDoc.text("Spec "),
|
|
293
|
-
formatPathDoc(error.parentSpecPath),
|
|
294
|
-
AnsiDoc.text(
|
|
295
|
-
` declares a ${error.collisionKind} \`${error.collisionName}\` whose name collides with the sibling subdirectory spec `,
|
|
296
|
-
),
|
|
297
|
-
formatPathDoc(error.childSpecPath),
|
|
298
|
-
AnsiDoc.text(
|
|
299
|
-
`. Rename one of them so the assembled spec has a unique key at this path.`,
|
|
300
|
-
),
|
|
301
|
-
);
|
|
302
|
-
|
|
303
|
-
/**
|
|
304
|
-
* Render any {@link CodegenError} into a styled, ready-to-print string.
|
|
305
|
-
* Single-error variants render to a one-line `✘`-prefixed message;
|
|
306
|
-
* `BundleFailedError` (the only multi-error variant) renders to a header
|
|
307
|
-
* plus an esbuild diagnostic block.
|
|
308
|
-
*/
|
|
309
|
-
export const renderCodegenError = (error: CodegenError): string => {
|
|
310
|
-
if (isBuildError(error)) return renderBuildError(error);
|
|
311
|
-
return Match.value(error).pipe(
|
|
312
|
-
Match.tag("MissingImplFileError", (e) =>
|
|
313
|
-
pipe(renderMissingImplFileError(e), AnsiDoc.render({ style: "pretty" })),
|
|
314
|
-
),
|
|
315
|
-
Match.tag("MissingSpecFileError", (e) =>
|
|
316
|
-
pipe(renderMissingSpecFileError(e), AnsiDoc.render({ style: "pretty" })),
|
|
317
|
-
),
|
|
318
|
-
Match.tag("SpecMissingDefaultGroupSpecError", (e) =>
|
|
319
|
-
pipe(
|
|
320
|
-
renderSpecMissingDefaultGroupSpecError(e),
|
|
321
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
322
|
-
),
|
|
323
|
-
),
|
|
324
|
-
Match.tag("ImplMissingSpecImportError", (e) =>
|
|
325
|
-
pipe(
|
|
326
|
-
renderImplMissingSpecImportError(e),
|
|
327
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
328
|
-
),
|
|
329
|
-
),
|
|
330
|
-
Match.tag("ImplMissingDefaultLayerError", (e) =>
|
|
331
|
-
pipe(
|
|
332
|
-
renderImplMissingDefaultLayerError(e),
|
|
333
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
334
|
-
),
|
|
335
|
-
),
|
|
336
|
-
Match.tag("ImplNotFinalizedError", (e) =>
|
|
337
|
-
pipe(renderImplNotFinalizedError(e), AnsiDoc.render({ style: "pretty" })),
|
|
338
|
-
),
|
|
339
|
-
Match.tag("ImplMissingFunctionsError", (e) =>
|
|
340
|
-
pipe(
|
|
341
|
-
renderImplMissingFunctionsError(e),
|
|
342
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
343
|
-
),
|
|
344
|
-
),
|
|
345
|
-
Match.tag("ParentChildNameCollisionError", (e) =>
|
|
346
|
-
pipe(
|
|
347
|
-
renderParentChildNameCollisionError(e),
|
|
348
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
349
|
-
),
|
|
350
|
-
),
|
|
351
|
-
Match.tag("InvalidTableDefaultExportError", (e) =>
|
|
352
|
-
pipe(
|
|
353
|
-
renderInvalidTableDefaultExportError(e),
|
|
354
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
355
|
-
),
|
|
356
|
-
),
|
|
357
|
-
Match.tag("InvalidTableFilenameError", (e) =>
|
|
358
|
-
pipe(
|
|
359
|
-
renderInvalidTableFilenameError(e),
|
|
360
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
361
|
-
),
|
|
362
|
-
),
|
|
363
|
-
Match.tag("DuplicateTableNameError", (e) =>
|
|
364
|
-
pipe(
|
|
365
|
-
renderDuplicateTableNameError(e),
|
|
366
|
-
AnsiDoc.render({ style: "pretty" }),
|
|
367
|
-
),
|
|
368
|
-
),
|
|
369
|
-
Match.tag("LegacySchemaFileError", (e) =>
|
|
370
|
-
pipe(renderLegacySchemaFileError(e), AnsiDoc.render({ style: "pretty" })),
|
|
371
|
-
),
|
|
372
|
-
Match.exhaustive,
|
|
373
|
-
);
|
|
374
|
-
};
|
|
375
|
-
|
|
376
|
-
export const logCodegenError = (error: CodegenError) =>
|
|
377
|
-
Effect.sync(() => console.error(renderCodegenError(error)));
|
|
378
|
-
|
|
379
|
-
// --- Effect combinators ---
|
|
380
|
-
|
|
381
|
-
/**
|
|
382
|
-
* Log any {@link CodegenError} thrown by `effect` and propagate the failure
|
|
383
|
-
* unchanged so the caller's error channel is preserved (used by the
|
|
384
|
-
* `codegen` command, which needs the failure to surface as a non-zero exit
|
|
385
|
-
* code).
|
|
386
|
-
*/
|
|
387
|
-
export const tapAndLog = <A, E, R>(
|
|
388
|
-
effect: Effect.Effect<A, E, R>,
|
|
389
|
-
): Effect.Effect<A, E, R> =>
|
|
390
|
-
effect.pipe(
|
|
391
|
-
Effect.tapError((error) =>
|
|
392
|
-
isCodegenError(error) ? logCodegenError(error) : Effect.void,
|
|
393
|
-
),
|
|
394
|
-
);
|
|
395
|
-
|
|
396
|
-
/**
|
|
397
|
-
* Catch any {@link CodegenError} thrown by `effect`, log it, and resolve to
|
|
398
|
-
* `Option.none()` (used by the `dev` command's sync loop, which continues
|
|
399
|
-
* after a failed sync rather than exiting). Success resolves to
|
|
400
|
-
* `Option.some(value)`.
|
|
401
|
-
*/
|
|
402
|
-
export const catchAndLog = <A, E, R>(
|
|
403
|
-
effect: Effect.Effect<A, E, R>,
|
|
404
|
-
): Effect.Effect<Option.Option<A>, Exclude<E, CodegenError>, R> =>
|
|
405
|
-
Effect.catchIf(Effect.map(effect, Option.some<A>), isCodegenError, (error) =>
|
|
406
|
-
logCodegenError(error).pipe(Effect.as(Option.none<A>())),
|
|
407
|
-
) as Effect.Effect<Option.Option<A>, Exclude<E, CodegenError>, R>;
|
package/src/ConfectDirectory.ts
DELETED
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
import * as FileSystem from "@effect/platform/FileSystem";
|
|
2
|
-
import * as Path from "@effect/platform/Path";
|
|
3
|
-
import * as Effect from "effect/Effect";
|
|
4
|
-
import * as Ref from "effect/Ref";
|
|
5
|
-
import * as Schema from "effect/Schema";
|
|
6
|
-
import { ConvexDirectory } from "./ConvexDirectory";
|
|
7
|
-
|
|
8
|
-
export class ConfectDirectory extends Effect.Service<ConfectDirectory>()(
|
|
9
|
-
"@confect/cli/ConfectDirectory",
|
|
10
|
-
{
|
|
11
|
-
effect: Effect.gen(function* () {
|
|
12
|
-
const convexDirectory = yield* findConfectDirectory;
|
|
13
|
-
|
|
14
|
-
const ref = yield* Ref.make<string>(convexDirectory);
|
|
15
|
-
|
|
16
|
-
return { get: Ref.get(ref) } as const;
|
|
17
|
-
}),
|
|
18
|
-
dependencies: [ConvexDirectory.Default],
|
|
19
|
-
accessors: true,
|
|
20
|
-
},
|
|
21
|
-
) {}
|
|
22
|
-
|
|
23
|
-
export class ConfectDirectoryNotFoundError extends Schema.TaggedError<ConfectDirectoryNotFoundError>()(
|
|
24
|
-
"ConfectDirectoryNotFoundError",
|
|
25
|
-
{},
|
|
26
|
-
) {
|
|
27
|
-
override get message(): string {
|
|
28
|
-
return "Could not find Confect directory";
|
|
29
|
-
}
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export const findConfectDirectory = Effect.gen(function* () {
|
|
33
|
-
const fs = yield* FileSystem.FileSystem;
|
|
34
|
-
const path = yield* Path.Path;
|
|
35
|
-
|
|
36
|
-
const convexDirectory = yield* ConvexDirectory.get;
|
|
37
|
-
|
|
38
|
-
const confectDirectory = path.join(path.dirname(convexDirectory), "confect");
|
|
39
|
-
|
|
40
|
-
if (yield* fs.exists(confectDirectory)) {
|
|
41
|
-
return confectDirectory;
|
|
42
|
-
} else {
|
|
43
|
-
return yield* new ConfectDirectoryNotFoundError();
|
|
44
|
-
}
|
|
45
|
-
});
|
package/src/ConvexDirectory.ts
DELETED
|
@@ -1,72 +0,0 @@
|
|
|
1
|
-
import * as FileSystem from "@effect/platform/FileSystem";
|
|
2
|
-
import * as Path from "@effect/platform/Path";
|
|
3
|
-
import * as Effect from "effect/Effect";
|
|
4
|
-
import * as Option from "effect/Option";
|
|
5
|
-
import * as Ref from "effect/Ref";
|
|
6
|
-
import * as Schema from "effect/Schema";
|
|
7
|
-
import { ProjectRoot } from "./ProjectRoot";
|
|
8
|
-
|
|
9
|
-
export class ConvexDirectory extends Effect.Service<ConvexDirectory>()(
|
|
10
|
-
"@confect/cli/ConvexDirectory",
|
|
11
|
-
{
|
|
12
|
-
effect: Effect.gen(function* () {
|
|
13
|
-
const convexDirectory = yield* findConvexDirectory;
|
|
14
|
-
|
|
15
|
-
const ref = yield* Ref.make<string>(convexDirectory);
|
|
16
|
-
|
|
17
|
-
return { get: Ref.get(ref) } as const;
|
|
18
|
-
}),
|
|
19
|
-
dependencies: [ProjectRoot.Default],
|
|
20
|
-
accessors: true,
|
|
21
|
-
},
|
|
22
|
-
) {}
|
|
23
|
-
|
|
24
|
-
export class ConvexDirectoryNotFoundError extends Schema.TaggedError<ConvexDirectoryNotFoundError>()(
|
|
25
|
-
"ConvexDirectoryNotFoundError",
|
|
26
|
-
{},
|
|
27
|
-
) {
|
|
28
|
-
override get message(): string {
|
|
29
|
-
return "Could not find Convex directory";
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Schema for `convex.json` configuration file.
|
|
35
|
-
* @see https://docs.convex.dev/production/project-configuration
|
|
36
|
-
*/
|
|
37
|
-
const ConvexJsonConfig = Schema.parseJson(
|
|
38
|
-
Schema.Struct({
|
|
39
|
-
functions: Schema.optional(Schema.String),
|
|
40
|
-
}),
|
|
41
|
-
);
|
|
42
|
-
|
|
43
|
-
const findConvexDirectory = Effect.gen(function* () {
|
|
44
|
-
const fs = yield* FileSystem.FileSystem;
|
|
45
|
-
const path = yield* Path.Path;
|
|
46
|
-
|
|
47
|
-
const projectRoot = yield* ProjectRoot.get;
|
|
48
|
-
|
|
49
|
-
const defaultPath = path.join(projectRoot, "convex");
|
|
50
|
-
|
|
51
|
-
const convexJsonPath = path.join(projectRoot, "convex.json");
|
|
52
|
-
|
|
53
|
-
const convexDirectory = yield* Effect.if(fs.exists(convexJsonPath), {
|
|
54
|
-
onTrue: () =>
|
|
55
|
-
fs.readFileString(convexJsonPath).pipe(
|
|
56
|
-
Effect.andThen(Schema.decodeOption(ConvexJsonConfig)),
|
|
57
|
-
Effect.map((config) =>
|
|
58
|
-
Option.fromNullable(config.functions).pipe(
|
|
59
|
-
Option.map((functionsDir) => path.join(projectRoot, functionsDir)),
|
|
60
|
-
),
|
|
61
|
-
),
|
|
62
|
-
Effect.andThen(Option.getOrElse(() => defaultPath)),
|
|
63
|
-
),
|
|
64
|
-
onFalse: () => Effect.succeed(defaultPath),
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
if (yield* fs.exists(convexDirectory)) {
|
|
68
|
-
return convexDirectory;
|
|
69
|
-
} else {
|
|
70
|
-
return yield* new ConvexDirectoryNotFoundError();
|
|
71
|
-
}
|
|
72
|
-
});
|
package/src/FunctionPath.ts
DELETED
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
import * as Schema from "effect/Schema";
|
|
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,107 +0,0 @@
|
|
|
1
|
-
import type { GroupSpec, Spec } from "@confect/core";
|
|
2
|
-
import { pipe } from "effect/Function";
|
|
3
|
-
import * as HashSet from "effect/HashSet";
|
|
4
|
-
import * as Option from "effect/Option";
|
|
5
|
-
import * as Record from "effect/Record";
|
|
6
|
-
import * as Schema from "effect/Schema";
|
|
7
|
-
import * as FunctionPath from "./FunctionPath";
|
|
8
|
-
import * as GroupPath from "./GroupPath";
|
|
9
|
-
import * as GroupPaths from "./GroupPaths";
|
|
10
|
-
|
|
11
|
-
export const FunctionPaths = Schema.HashSetFromSelf(
|
|
12
|
-
FunctionPath.FunctionPath,
|
|
13
|
-
).pipe(Schema.brand("@confect/cli/FunctionPaths"));
|
|
14
|
-
export type FunctionPaths = typeof FunctionPaths.Type;
|
|
15
|
-
|
|
16
|
-
export const make = (spec: Spec.AnyWithProps): FunctionPaths =>
|
|
17
|
-
makeHelper(spec.groups, Option.none(), FunctionPaths.make(HashSet.empty()));
|
|
18
|
-
|
|
19
|
-
const makeHelper = (
|
|
20
|
-
groups: {
|
|
21
|
-
[key: string]: GroupSpec.AnyWithProps;
|
|
22
|
-
},
|
|
23
|
-
currentGroupPath: Option.Option<GroupPath.GroupPath>,
|
|
24
|
-
apiPaths: FunctionPaths,
|
|
25
|
-
): FunctionPaths =>
|
|
26
|
-
Record.reduce(groups, apiPaths, (acc, group, groupName) => {
|
|
27
|
-
const groupPath = Option.match(currentGroupPath, {
|
|
28
|
-
onNone: () => GroupPath.make([groupName]),
|
|
29
|
-
onSome: (path) => GroupPath.append(path, groupName),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
const accWithFunctions = Record.reduce(
|
|
33
|
-
group.functions,
|
|
34
|
-
acc,
|
|
35
|
-
(acc_, _fn, functionName) =>
|
|
36
|
-
FunctionPaths.make(
|
|
37
|
-
HashSet.add(
|
|
38
|
-
acc_,
|
|
39
|
-
FunctionPath.FunctionPath.make({
|
|
40
|
-
groupPath,
|
|
41
|
-
name: functionName,
|
|
42
|
-
}),
|
|
43
|
-
),
|
|
44
|
-
),
|
|
45
|
-
);
|
|
46
|
-
|
|
47
|
-
return makeHelper(group.groups, Option.some(groupPath), accWithFunctions);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
export const groupPaths = (
|
|
51
|
-
functionPaths: FunctionPaths,
|
|
52
|
-
): GroupPaths.GroupPaths =>
|
|
53
|
-
pipe(
|
|
54
|
-
functionPaths,
|
|
55
|
-
HashSet.map(FunctionPath.groupPath),
|
|
56
|
-
GroupPaths.GroupPaths.make,
|
|
57
|
-
);
|
|
58
|
-
|
|
59
|
-
export const diff = (
|
|
60
|
-
previousFunctions: FunctionPaths,
|
|
61
|
-
currentFunctions: FunctionPaths,
|
|
62
|
-
): {
|
|
63
|
-
functionsAdded: FunctionPaths;
|
|
64
|
-
functionsRemoved: FunctionPaths;
|
|
65
|
-
groupsRemoved: GroupPaths.GroupPaths;
|
|
66
|
-
groupsAdded: GroupPaths.GroupPaths;
|
|
67
|
-
groupsChanged: GroupPaths.GroupPaths;
|
|
68
|
-
} => {
|
|
69
|
-
const currentGroups = groupPaths(currentFunctions);
|
|
70
|
-
const previousGroups = groupPaths(previousFunctions);
|
|
71
|
-
|
|
72
|
-
const groupsAdded = GroupPaths.GroupPaths.make(
|
|
73
|
-
HashSet.difference(currentGroups, previousGroups),
|
|
74
|
-
);
|
|
75
|
-
const groupsRemoved = GroupPaths.GroupPaths.make(
|
|
76
|
-
HashSet.difference(previousGroups, currentGroups),
|
|
77
|
-
);
|
|
78
|
-
|
|
79
|
-
const functionsAdded = FunctionPaths.make(
|
|
80
|
-
HashSet.difference(currentFunctions, previousFunctions),
|
|
81
|
-
);
|
|
82
|
-
const existingGroupsToWhichFunctionsWereAdded = GroupPaths.GroupPaths.make(
|
|
83
|
-
HashSet.intersection(currentGroups, groupPaths(functionsAdded)),
|
|
84
|
-
);
|
|
85
|
-
|
|
86
|
-
const functionsRemoved = FunctionPaths.make(
|
|
87
|
-
HashSet.difference(previousFunctions, currentFunctions),
|
|
88
|
-
);
|
|
89
|
-
const existingGroupsToWhichFunctionsWereRemoved = GroupPaths.GroupPaths.make(
|
|
90
|
-
HashSet.intersection(previousGroups, groupPaths(functionsRemoved)),
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
const groupsChanged = pipe(
|
|
94
|
-
existingGroupsToWhichFunctionsWereAdded,
|
|
95
|
-
HashSet.union(existingGroupsToWhichFunctionsWereRemoved),
|
|
96
|
-
HashSet.difference(HashSet.union(groupsAdded, groupsRemoved)),
|
|
97
|
-
GroupPaths.GroupPaths.make,
|
|
98
|
-
);
|
|
99
|
-
|
|
100
|
-
return {
|
|
101
|
-
functionsAdded,
|
|
102
|
-
functionsRemoved,
|
|
103
|
-
groupsRemoved,
|
|
104
|
-
groupsAdded,
|
|
105
|
-
groupsChanged,
|
|
106
|
-
};
|
|
107
|
-
};
|