@confect/cli 1.0.0-next.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 +12 -0
- package/LICENSE +7 -0
- package/dist/FunctionPath.mjs +19 -0
- package/dist/FunctionPath.mjs.map +1 -0
- package/dist/FunctionPaths.mjs +41 -0
- package/dist/FunctionPaths.mjs.map +1 -0
- package/dist/GroupPath.mjs +52 -0
- package/dist/GroupPath.mjs.map +1 -0
- package/dist/GroupPaths.mjs +9 -0
- package/dist/GroupPaths.mjs.map +1 -0
- package/dist/_virtual/rolldown_runtime.mjs +28 -0
- package/dist/cliApp.mjs +13 -0
- package/dist/cliApp.mjs.map +1 -0
- package/dist/confect/codegen.mjs +111 -0
- package/dist/confect/codegen.mjs.map +1 -0
- package/dist/confect/dev.mjs +253 -0
- package/dist/confect/dev.mjs.map +1 -0
- package/dist/confect.mjs +14 -0
- package/dist/confect.mjs.map +1 -0
- package/dist/index.d.mts +1 -0
- package/dist/index.mjs +19 -0
- package/dist/index.mjs.map +1 -0
- package/dist/log.mjs +25 -0
- package/dist/log.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeCommandExecutor.mjs +12 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeCommandExecutor.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeFileSystem.mjs +15 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeFileSystem.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodePath.mjs +25 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodePath.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeRuntime.mjs +12 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeRuntime.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeTerminal.mjs +17 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/NodeTerminal.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/commandExecutor.mjs +129 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/commandExecutor.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/error.mjs +43 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/error.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/fileSystem.mjs +329 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/fileSystem.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/path.mjs +51 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/path.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/runtime.mjs +31 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/runtime.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/sink.mjs +24 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/sink.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/stream.mjs +91 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/stream.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/terminal.mjs +75 -0
- package/dist/node_modules/.pnpm/@effect_platform-node-shared@0.53.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effe_e0eeb3aae5ffec3060feb87d17ffb17c/node_modules/@effect/platform-node-shared/dist/esm/internal/terminal.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeContext.mjs +21 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeContext.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeFileSystem.mjs +15 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeFileSystem.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeRuntime.mjs +15 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeRuntime.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeWorker.mjs +27 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/NodeWorker.mjs.map +1 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/internal/worker.mjs +71 -0
- package/dist/node_modules/.pnpm/@effect_platform-node@0.100.0_@effect_cluster@0.52.9_@effect_platform@0.93.2_effect@3.1_a15ca1802d939cd85cc564105ef862ac/node_modules/@effect/platform-node/dist/esm/internal/worker.mjs.map +1 -0
- package/dist/packages/cli/package.mjs +6 -0
- package/dist/packages/cli/package.mjs.map +1 -0
- package/dist/packages/core/dist/Spec.mjs +23 -0
- package/dist/packages/core/dist/Spec.mjs.map +1 -0
- package/dist/packages/core/dist/_virtual/rolldown_runtime.mjs +14 -0
- package/dist/packages/core/dist/_virtual/rolldown_runtime.mjs.map +1 -0
- package/dist/services/ConfectDirectory.mjs +31 -0
- package/dist/services/ConfectDirectory.mjs.map +1 -0
- package/dist/services/ConvexDirectory.mjs +41 -0
- package/dist/services/ConvexDirectory.mjs.map +1 -0
- package/dist/services/ProjectRoot.mjs +35 -0
- package/dist/services/ProjectRoot.mjs.map +1 -0
- package/dist/templates.mjs +204 -0
- package/dist/templates.mjs.map +1 -0
- package/dist/utils.mjs +162 -0
- package/dist/utils.mjs.map +1 -0
- package/package.json +86 -0
- package/src/FunctionPath.ts +28 -0
- package/src/FunctionPaths.ts +103 -0
- package/src/GroupPath.ts +117 -0
- package/src/GroupPaths.ts +7 -0
- package/src/cliApp.ts +8 -0
- package/src/confect/codegen.ts +228 -0
- package/src/confect/dev.ts +611 -0
- package/src/confect.ts +19 -0
- package/src/index.ts +22 -0
- package/src/log.ts +106 -0
- package/src/services/ConfectDirectory.ts +41 -0
- package/src/services/ConvexDirectory.ts +67 -0
- package/src/services/ProjectRoot.ts +49 -0
- package/src/templates.ts +380 -0
- package/src/utils.ts +332 -0
package/CHANGELOG.md
ADDED
package/LICENSE
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright 2024 RJ Dellecese
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies.
|
|
6
|
+
|
|
7
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GroupPath } from "./GroupPath.mjs";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src/FunctionPath.ts
|
|
5
|
+
/**
|
|
6
|
+
* The path to a function in the Confect API.
|
|
7
|
+
*/
|
|
8
|
+
var FunctionPath = class extends Schema.Class("FunctionPath")({
|
|
9
|
+
groupPath: GroupPath,
|
|
10
|
+
name: Schema.NonEmptyString
|
|
11
|
+
}) {};
|
|
12
|
+
/**
|
|
13
|
+
* Get the group path from a function path.
|
|
14
|
+
*/
|
|
15
|
+
const groupPath = (functionPath) => functionPath.groupPath;
|
|
16
|
+
|
|
17
|
+
//#endregion
|
|
18
|
+
export { FunctionPath, groupPath };
|
|
19
|
+
//# sourceMappingURL=FunctionPath.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FunctionPath.mjs","names":["GroupPath.GroupPath"],"sources":["../src/FunctionPath.ts"],"sourcesContent":["import { Schema } from \"effect\";\nimport * as GroupPath from \"./GroupPath\";\n\n/**\n * The path to a function in the Confect API.\n */\nexport class FunctionPath extends Schema.Class<FunctionPath>(\"FunctionPath\")({\n // TODO: Support root-level functions (also must be supported in the other packages, e.g. `core` and `server`)\n groupPath: GroupPath.GroupPath,\n name: Schema.NonEmptyString,\n}) {}\n\n/**\n * Get the group path from a function path.\n */\nexport const groupPath = (functionPath: FunctionPath): GroupPath.GroupPath =>\n functionPath.groupPath;\n\n/**\n * Get the function name from a function path.\n */\nexport const name = (functionPath: FunctionPath): string => functionPath.name;\n\n/**\n * Get the function path as a string.\n */\nexport const toString = (functionPath: FunctionPath): string =>\n `${GroupPath.toString(functionPath.groupPath)}.${functionPath.name}`;\n"],"mappings":";;;;;;;AAMA,IAAa,eAAb,cAAkC,OAAO,MAAoB,eAAe,CAAC;CAE3E,WAAWA;CACX,MAAM,OAAO;CACd,CAAC,CAAC;;;;AAKH,MAAa,aAAa,iBACxB,aAAa"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { append, make as make$1 } from "./GroupPath.mjs";
|
|
2
|
+
import { FunctionPath, groupPath } from "./FunctionPath.mjs";
|
|
3
|
+
import { GroupPaths } from "./GroupPaths.mjs";
|
|
4
|
+
import { HashSet, Option, Record, Schema, pipe } from "effect";
|
|
5
|
+
|
|
6
|
+
//#region src/FunctionPaths.ts
|
|
7
|
+
const FunctionPaths = Schema.HashSetFromSelf(FunctionPath).pipe(Schema.brand("@confect/cli/FunctionPaths"));
|
|
8
|
+
const make = (spec) => makeHelper(spec.groups, Option.none(), FunctionPaths.make(HashSet.empty()));
|
|
9
|
+
const makeHelper = (groups, currentGroupPath, apiPaths) => Record.reduce(groups, apiPaths, (acc, group, groupName) => {
|
|
10
|
+
const groupPath$1 = Option.match(currentGroupPath, {
|
|
11
|
+
onNone: () => make$1([groupName]),
|
|
12
|
+
onSome: (path) => append(path, groupName)
|
|
13
|
+
});
|
|
14
|
+
const accWithFunctions = Record.reduce(group.functions, acc, (acc_, _fn, functionName) => FunctionPaths.make(HashSet.add(acc_, FunctionPath.make({
|
|
15
|
+
groupPath: groupPath$1,
|
|
16
|
+
name: functionName
|
|
17
|
+
}))));
|
|
18
|
+
return makeHelper(group.groups, Option.some(groupPath$1), accWithFunctions);
|
|
19
|
+
});
|
|
20
|
+
const groupPaths = (functionPaths) => pipe(functionPaths, HashSet.map(groupPath), GroupPaths.make);
|
|
21
|
+
const diff = (previousFunctions, currentFunctions) => {
|
|
22
|
+
const currentGroups = groupPaths(currentFunctions);
|
|
23
|
+
const previousGroups = groupPaths(previousFunctions);
|
|
24
|
+
const groupsAdded = GroupPaths.make(HashSet.difference(currentGroups, previousGroups));
|
|
25
|
+
const groupsRemoved = GroupPaths.make(HashSet.difference(previousGroups, currentGroups));
|
|
26
|
+
const functionsAdded = FunctionPaths.make(HashSet.difference(currentFunctions, previousFunctions));
|
|
27
|
+
const existingGroupsToWhichFunctionsWereAdded = GroupPaths.make(HashSet.intersection(currentGroups, groupPaths(functionsAdded)));
|
|
28
|
+
const functionsRemoved = FunctionPaths.make(HashSet.difference(previousFunctions, currentFunctions));
|
|
29
|
+
const existingGroupsToWhichFunctionsWereRemoved = GroupPaths.make(HashSet.intersection(previousGroups, groupPaths(functionsRemoved)));
|
|
30
|
+
return {
|
|
31
|
+
functionsAdded,
|
|
32
|
+
functionsRemoved,
|
|
33
|
+
groupsRemoved,
|
|
34
|
+
groupsAdded,
|
|
35
|
+
groupsChanged: pipe(existingGroupsToWhichFunctionsWereAdded, HashSet.union(existingGroupsToWhichFunctionsWereRemoved), HashSet.difference(HashSet.union(groupsAdded, groupsRemoved)), GroupPaths.make)
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
//#endregion
|
|
40
|
+
export { diff, groupPaths, make };
|
|
41
|
+
//# sourceMappingURL=FunctionPaths.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"FunctionPaths.mjs","names":["FunctionPath.FunctionPath","groupPath","GroupPath.make","GroupPath.append","FunctionPath.groupPath"],"sources":["../src/FunctionPaths.ts"],"sourcesContent":["import type { GroupSpec, Spec } from \"@confect/core\";\nimport { HashSet, Option, pipe, Record, Schema } from \"effect\";\nimport * as FunctionPath from \"./FunctionPath\";\nimport * as GroupPath from \"./GroupPath\";\nimport * as GroupPaths from \"./GroupPaths\";\n\nexport const FunctionPaths = Schema.HashSetFromSelf(\n FunctionPath.FunctionPath,\n).pipe(Schema.brand(\"@confect/cli/FunctionPaths\"));\nexport type FunctionPaths = typeof FunctionPaths.Type;\n\nexport const make = (spec: Spec.AnyWithProps): FunctionPaths =>\n makeHelper(spec.groups, Option.none(), FunctionPaths.make(HashSet.empty()));\n\nconst makeHelper = (\n groups: {\n [key: string]: GroupSpec.AnyWithProps;\n },\n currentGroupPath: Option.Option<GroupPath.GroupPath>,\n apiPaths: FunctionPaths,\n): FunctionPaths =>\n Record.reduce(groups, apiPaths, (acc, group, groupName) => {\n const groupPath = Option.match(currentGroupPath, {\n onNone: () => GroupPath.make([groupName]),\n onSome: (path) => GroupPath.append(path, groupName),\n });\n\n const accWithFunctions = Record.reduce(\n group.functions,\n acc,\n (acc_, _fn, functionName) =>\n FunctionPaths.make(\n HashSet.add(\n acc_,\n FunctionPath.FunctionPath.make({\n groupPath,\n name: functionName,\n }),\n ),\n ),\n );\n\n return makeHelper(group.groups, Option.some(groupPath), accWithFunctions);\n });\n\nexport const groupPaths = (\n functionPaths: FunctionPaths,\n): GroupPaths.GroupPaths =>\n pipe(\n functionPaths,\n HashSet.map(FunctionPath.groupPath),\n GroupPaths.GroupPaths.make,\n );\n\nexport const diff = (\n previousFunctions: FunctionPaths,\n currentFunctions: FunctionPaths,\n): {\n functionsAdded: FunctionPaths;\n functionsRemoved: FunctionPaths;\n groupsRemoved: GroupPaths.GroupPaths;\n groupsAdded: GroupPaths.GroupPaths;\n groupsChanged: GroupPaths.GroupPaths;\n} => {\n const currentGroups = groupPaths(currentFunctions);\n const previousGroups = groupPaths(previousFunctions);\n\n const groupsAdded = GroupPaths.GroupPaths.make(\n HashSet.difference(currentGroups, previousGroups),\n );\n const groupsRemoved = GroupPaths.GroupPaths.make(\n HashSet.difference(previousGroups, currentGroups),\n );\n\n const functionsAdded = FunctionPaths.make(\n HashSet.difference(currentFunctions, previousFunctions),\n );\n const existingGroupsToWhichFunctionsWereAdded = GroupPaths.GroupPaths.make(\n HashSet.intersection(currentGroups, groupPaths(functionsAdded)),\n );\n\n const functionsRemoved = FunctionPaths.make(\n HashSet.difference(previousFunctions, currentFunctions),\n );\n const existingGroupsToWhichFunctionsWereRemoved = GroupPaths.GroupPaths.make(\n HashSet.intersection(previousGroups, groupPaths(functionsRemoved)),\n );\n\n const groupsChanged = pipe(\n existingGroupsToWhichFunctionsWereAdded,\n HashSet.union(existingGroupsToWhichFunctionsWereRemoved),\n HashSet.difference(HashSet.union(groupsAdded, groupsRemoved)),\n GroupPaths.GroupPaths.make,\n );\n\n return {\n functionsAdded,\n functionsRemoved,\n groupsRemoved,\n groupsAdded,\n groupsChanged,\n };\n};\n"],"mappings":";;;;;;AAMA,MAAa,gBAAgB,OAAO,gBAClCA,aACD,CAAC,KAAK,OAAO,MAAM,6BAA6B,CAAC;AAGlD,MAAa,QAAQ,SACnB,WAAW,KAAK,QAAQ,OAAO,MAAM,EAAE,cAAc,KAAK,QAAQ,OAAO,CAAC,CAAC;AAE7E,MAAM,cACJ,QAGA,kBACA,aAEA,OAAO,OAAO,QAAQ,WAAW,KAAK,OAAO,cAAc;CACzD,MAAMC,cAAY,OAAO,MAAM,kBAAkB;EAC/C,cAAcC,OAAe,CAAC,UAAU,CAAC;EACzC,SAAS,SAASC,OAAiB,MAAM,UAAU;EACpD,CAAC;CAEF,MAAM,mBAAmB,OAAO,OAC9B,MAAM,WACN,MACC,MAAM,KAAK,iBACV,cAAc,KACZ,QAAQ,IACN,mBAC0B,KAAK;EAC7B;EACA,MAAM;EACP,CAAC,CACH,CACF,CACJ;AAED,QAAO,WAAW,MAAM,QAAQ,OAAO,KAAKF,YAAU,EAAE,iBAAiB;EACzE;AAEJ,MAAa,cACX,kBAEA,KACE,eACA,QAAQ,IAAIG,UAAuB,aACb,KACvB;AAEH,MAAa,QACX,mBACA,qBAOG;CACH,MAAM,gBAAgB,WAAW,iBAAiB;CAClD,MAAM,iBAAiB,WAAW,kBAAkB;CAEpD,MAAM,yBAAoC,KACxC,QAAQ,WAAW,eAAe,eAAe,CAClD;CACD,MAAM,2BAAsC,KAC1C,QAAQ,WAAW,gBAAgB,cAAc,CAClD;CAED,MAAM,iBAAiB,cAAc,KACnC,QAAQ,WAAW,kBAAkB,kBAAkB,CACxD;CACD,MAAM,qDAAgE,KACpE,QAAQ,aAAa,eAAe,WAAW,eAAe,CAAC,CAChE;CAED,MAAM,mBAAmB,cAAc,KACrC,QAAQ,WAAW,mBAAmB,iBAAiB,CACxD;CACD,MAAM,uDAAkE,KACtE,QAAQ,aAAa,gBAAgB,WAAW,iBAAiB,CAAC,CACnE;AASD,QAAO;EACL;EACA;EACA;EACA;EACA,eAZoB,KACpB,yCACA,QAAQ,MAAM,0CAA0C,EACxD,QAAQ,WAAW,QAAQ,MAAM,aAAa,cAAc,CAAC,aACvC,KACvB;EAQA"}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Array, Data, Effect, Option, Record, Schema, String, pipe } from "effect";
|
|
2
|
+
import { Path } from "@effect/platform";
|
|
3
|
+
|
|
4
|
+
//#region src/GroupPath.ts
|
|
5
|
+
/**
|
|
6
|
+
* The path to a group in the Confect API.
|
|
7
|
+
*/
|
|
8
|
+
var GroupPath = class extends Schema.Class("GroupPath")({ pathSegments: Schema.Data(Schema.NonEmptyArray(Schema.NonEmptyString)) }) {};
|
|
9
|
+
/**
|
|
10
|
+
* Create a GroupPath from path segments.
|
|
11
|
+
*/
|
|
12
|
+
const make = (pathSegments) => GroupPath.make({ pathSegments: Data.array(pathSegments) });
|
|
13
|
+
/**
|
|
14
|
+
* Append a group name to a GroupPath to create a new GroupPath.
|
|
15
|
+
*/
|
|
16
|
+
const append = (groupPath, groupName) => make([...groupPath.pathSegments, groupName]);
|
|
17
|
+
/**
|
|
18
|
+
* Expects a path string of the form `./group1/group2.ts`, relative to the Convex functions directory.
|
|
19
|
+
*/
|
|
20
|
+
const fromGroupModulePath = (groupModulePath) => Effect.gen(function* () {
|
|
21
|
+
const path = yield* Path.Path;
|
|
22
|
+
const { dir, name, ext } = path.parse(groupModulePath);
|
|
23
|
+
if (ext === ".ts") {
|
|
24
|
+
const dirSegments = Array.filter(String.split(dir, path.sep), String.isNonEmpty);
|
|
25
|
+
yield* Effect.logDebug(Array.append(dirSegments, name));
|
|
26
|
+
return make(Array.append(dirSegments, name));
|
|
27
|
+
} else return yield* Effect.fail(new GroupModulePathIsNotATypeScriptFileError({ path: groupModulePath }));
|
|
28
|
+
});
|
|
29
|
+
/**
|
|
30
|
+
* Get the module path for a group, relative to the Convex functions directory.
|
|
31
|
+
*/
|
|
32
|
+
const modulePath = (groupPath) => Effect.gen(function* () {
|
|
33
|
+
return (yield* Path.Path).join(...groupPath.pathSegments) + ".ts";
|
|
34
|
+
});
|
|
35
|
+
const getGroupSpec = (spec, groupPath) => pipe(groupPath.pathSegments, Array.matchLeft({
|
|
36
|
+
onEmpty: () => Option.none(),
|
|
37
|
+
onNonEmpty: (head, tail) => pipe(Record.get(spec.groups, head), Option.flatMap((group) => Array.isNonEmptyArray(tail) ? getGroupSpecHelper(group, tail) : Option.some(group)))
|
|
38
|
+
}));
|
|
39
|
+
const getGroupSpecHelper = (group, remainingPath) => pipe(remainingPath, Array.matchLeft({
|
|
40
|
+
onEmpty: () => Option.some(group),
|
|
41
|
+
onNonEmpty: (head, tail) => pipe(Record.get(group.groups, head), Option.flatMap((subGroup) => Array.isNonEmptyArray(tail) ? getGroupSpecHelper(subGroup, tail) : Option.some(subGroup)))
|
|
42
|
+
}));
|
|
43
|
+
const toString = (groupPath) => Array.join(groupPath.pathSegments, ".");
|
|
44
|
+
var GroupModulePathIsNotATypeScriptFileError = class extends Schema.TaggedError("GroupModulePathIsNotATypeScriptFileError")("GroupModulePathIsNotATypeScriptFileError", { path: Schema.NonEmptyString }) {
|
|
45
|
+
get message() {
|
|
46
|
+
return `Expected group module path to end with .ts, got ${this.path}`;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
export { GroupPath, append, fromGroupModulePath, getGroupSpec, make, modulePath, toString };
|
|
52
|
+
//# sourceMappingURL=GroupPath.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GroupPath.mjs","names":[],"sources":["../src/GroupPath.ts"],"sourcesContent":["import { type GroupSpec, type Spec } from \"@confect/core\";\nimport { Path } from \"@effect/platform\";\nimport {\n Array,\n Data,\n Effect,\n Option,\n pipe,\n Record,\n Schema,\n String,\n} from \"effect\";\n\n/**\n * The path to a group in the Confect API.\n */\nexport class GroupPath extends Schema.Class<GroupPath>(\"GroupPath\")({\n pathSegments: Schema.Data(Schema.NonEmptyArray(Schema.NonEmptyString)),\n}) {}\n\n/**\n * Create a GroupPath from path segments.\n */\nexport const make = (pathSegments: readonly [string, ...string[]]): GroupPath =>\n GroupPath.make({ pathSegments: Data.array(pathSegments) });\n\n/**\n * Append a group name to a GroupPath to create a new GroupPath.\n */\nexport const append = (groupPath: GroupPath, groupName: string): GroupPath =>\n make([...groupPath.pathSegments, groupName]);\n\n/**\n * Expects a path string of the form `./group1/group2.ts`, relative to the Convex functions directory.\n */\nexport const fromGroupModulePath = (groupModulePath: string) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n\n const { dir, name, ext } = path.parse(groupModulePath);\n\n if (ext === \".ts\") {\n const dirSegments = Array.filter(\n String.split(dir, path.sep),\n String.isNonEmpty,\n );\n yield* Effect.logDebug(Array.append(dirSegments, name));\n return make(Array.append(dirSegments, name));\n } else {\n return yield* Effect.fail(\n new GroupModulePathIsNotATypeScriptFileError({ path: groupModulePath }),\n );\n }\n });\n\n/**\n * Get the module path for a group, relative to the Convex functions directory.\n */\nexport const modulePath = (groupPath: GroupPath) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n\n return path.join(...groupPath.pathSegments) + \".ts\";\n });\n\nexport const getGroupSpec = (\n spec: Spec.AnyWithProps,\n groupPath: GroupPath,\n): Option.Option<GroupSpec.AnyWithProps> =>\n pipe(\n groupPath.pathSegments,\n Array.matchLeft({\n onEmpty: () => Option.none(),\n onNonEmpty: (head, tail) =>\n pipe(\n Record.get(spec.groups, head),\n Option.flatMap((group) =>\n Array.isNonEmptyArray(tail)\n ? getGroupSpecHelper(group, tail)\n : Option.some(group),\n ),\n ),\n }),\n );\n\nconst getGroupSpecHelper = (\n group: GroupSpec.AnyWithProps,\n remainingPath: ReadonlyArray<string>,\n): Option.Option<GroupSpec.AnyWithProps> =>\n pipe(\n remainingPath,\n Array.matchLeft({\n onEmpty: () => Option.some(group),\n onNonEmpty: (head, tail) =>\n pipe(\n Record.get(group.groups, head),\n Option.flatMap((subGroup) =>\n Array.isNonEmptyArray(tail)\n ? getGroupSpecHelper(subGroup, tail)\n : Option.some(subGroup),\n ),\n ),\n }),\n );\n\nexport const toString = (groupPath: GroupPath) =>\n Array.join(groupPath.pathSegments, \".\");\n\nexport class GroupModulePathIsNotATypeScriptFileError extends Schema.TaggedError<GroupModulePathIsNotATypeScriptFileError>(\n \"GroupModulePathIsNotATypeScriptFileError\",\n)(\"GroupModulePathIsNotATypeScriptFileError\", {\n path: Schema.NonEmptyString,\n}) {\n override get message(): string {\n return `Expected group module path to end with .ts, got ${this.path}`;\n }\n}\n"],"mappings":";;;;;;;AAgBA,IAAa,YAAb,cAA+B,OAAO,MAAiB,YAAY,CAAC,EAClE,cAAc,OAAO,KAAK,OAAO,cAAc,OAAO,eAAe,CAAC,EACvE,CAAC,CAAC;;;;AAKH,MAAa,QAAQ,iBACnB,UAAU,KAAK,EAAE,cAAc,KAAK,MAAM,aAAa,EAAE,CAAC;;;;AAK5D,MAAa,UAAU,WAAsB,cAC3C,KAAK,CAAC,GAAG,UAAU,cAAc,UAAU,CAAC;;;;AAK9C,MAAa,uBAAuB,oBAClC,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CAEzB,MAAM,EAAE,KAAK,MAAM,QAAQ,KAAK,MAAM,gBAAgB;AAEtD,KAAI,QAAQ,OAAO;EACjB,MAAM,cAAc,MAAM,OACxB,OAAO,MAAM,KAAK,KAAK,IAAI,EAC3B,OAAO,WACR;AACD,SAAO,OAAO,SAAS,MAAM,OAAO,aAAa,KAAK,CAAC;AACvD,SAAO,KAAK,MAAM,OAAO,aAAa,KAAK,CAAC;OAE5C,QAAO,OAAO,OAAO,KACnB,IAAI,yCAAyC,EAAE,MAAM,iBAAiB,CAAC,CACxE;EAEH;;;;AAKJ,MAAa,cAAc,cACzB,OAAO,IAAI,aAAa;AAGtB,SAFa,OAAO,KAAK,MAEb,KAAK,GAAG,UAAU,aAAa,GAAG;EAC9C;AAEJ,MAAa,gBACX,MACA,cAEA,KACE,UAAU,cACV,MAAM,UAAU;CACd,eAAe,OAAO,MAAM;CAC5B,aAAa,MAAM,SACjB,KACE,OAAO,IAAI,KAAK,QAAQ,KAAK,EAC7B,OAAO,SAAS,UACd,MAAM,gBAAgB,KAAK,GACvB,mBAAmB,OAAO,KAAK,GAC/B,OAAO,KAAK,MAAM,CACvB,CACF;CACJ,CAAC,CACH;AAEH,MAAM,sBACJ,OACA,kBAEA,KACE,eACA,MAAM,UAAU;CACd,eAAe,OAAO,KAAK,MAAM;CACjC,aAAa,MAAM,SACjB,KACE,OAAO,IAAI,MAAM,QAAQ,KAAK,EAC9B,OAAO,SAAS,aACd,MAAM,gBAAgB,KAAK,GACvB,mBAAmB,UAAU,KAAK,GAClC,OAAO,KAAK,SAAS,CAC1B,CACF;CACJ,CAAC,CACH;AAEH,MAAa,YAAY,cACvB,MAAM,KAAK,UAAU,cAAc,IAAI;AAEzC,IAAa,2CAAb,cAA8D,OAAO,YACnE,2CACD,CAAC,4CAA4C,EAC5C,MAAM,OAAO,gBACd,CAAC,CAAC;CACD,IAAa,UAAkB;AAC7B,SAAO,mDAAmD,KAAK"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { GroupPath } from "./GroupPath.mjs";
|
|
2
|
+
import { Schema } from "effect";
|
|
3
|
+
|
|
4
|
+
//#region src/GroupPaths.ts
|
|
5
|
+
const GroupPaths = Schema.HashSetFromSelf(GroupPath).pipe(Schema.brand("@confect/cli/GroupPaths"));
|
|
6
|
+
|
|
7
|
+
//#endregion
|
|
8
|
+
export { GroupPaths };
|
|
9
|
+
//# sourceMappingURL=GroupPaths.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"GroupPaths.mjs","names":["GroupPath.GroupPath"],"sources":["../src/GroupPaths.ts"],"sourcesContent":["import { Schema } from \"effect\";\nimport * as GroupPath from \"./GroupPath\";\n\nexport const GroupPaths = Schema.HashSetFromSelf(GroupPath.GroupPath).pipe(\n Schema.brand(\"@confect/cli/GroupPaths\"),\n);\nexport type GroupPaths = typeof GroupPaths.Type;\n"],"mappings":";;;;AAGA,MAAa,aAAa,OAAO,gBAAgBA,UAAoB,CAAC,KACpE,OAAO,MAAM,0BAA0B,CACxC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { createRequire } from "node:module";
|
|
2
|
+
|
|
3
|
+
//#region rolldown:runtime
|
|
4
|
+
var __create = Object.create;
|
|
5
|
+
var __defProp = Object.defineProperty;
|
|
6
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
7
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
8
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
9
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
10
|
+
var __copyProps = (to, from, except, desc) => {
|
|
11
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
12
|
+
key = keys[i];
|
|
13
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
14
|
+
get: ((k) => from[k]).bind(null, key),
|
|
15
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __reExport = (target, mod, secondTarget) => (__copyProps(target, mod, "default"), secondTarget && __copyProps(secondTarget, mod, "default"));
|
|
21
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
22
|
+
value: mod,
|
|
23
|
+
enumerable: true
|
|
24
|
+
}) : target, mod));
|
|
25
|
+
var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
26
|
+
|
|
27
|
+
//#endregion
|
|
28
|
+
export { };
|
package/dist/cliApp.mjs
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { version } from "./packages/cli/package.mjs";
|
|
2
|
+
import { confect } from "./confect.mjs";
|
|
3
|
+
import { Command } from "@effect/cli";
|
|
4
|
+
|
|
5
|
+
//#region src/cliApp.ts
|
|
6
|
+
const cliApp = Command.run(confect, {
|
|
7
|
+
name: "Confect",
|
|
8
|
+
version
|
|
9
|
+
});
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { cliApp };
|
|
13
|
+
//# sourceMappingURL=cliApp.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cliApp.mjs","names":["packageJson.version"],"sources":["../src/cliApp.ts"],"sourcesContent":["import { Command } from \"@effect/cli\";\nimport packageJson from \"../package.json\" with { type: \"json\" };\nimport { confect } from \"./confect\";\n\nexport const cliApp = Command.run(confect, {\n name: \"Confect\",\n version: packageJson.version,\n});\n"],"mappings":";;;;;AAIA,MAAa,SAAS,QAAQ,IAAI,SAAS;CACzC,MAAM;CACGA;CACV,CAAC"}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { Spec_exports } from "../packages/core/dist/Spec.mjs";
|
|
2
|
+
import { logCompleted, logFileAdded, logFileModified } from "../log.mjs";
|
|
3
|
+
import { ConvexDirectory } from "../services/ConvexDirectory.mjs";
|
|
4
|
+
import { ConfectDirectory } from "../services/ConfectDirectory.mjs";
|
|
5
|
+
import { api, refs, registeredFunctions, schema, services } from "../templates.mjs";
|
|
6
|
+
import { generateAuthConfig, generateConvexConfig, generateCrons, generateFunctions, generateHttp, removePathExtension, writeFileStringAndLog } from "../utils.mjs";
|
|
7
|
+
import { Effect, Match, Option } from "effect";
|
|
8
|
+
import { Command } from "@effect/cli";
|
|
9
|
+
import { DatabaseSchema } from "@confect/server";
|
|
10
|
+
import { FileSystem, Path } from "@effect/platform";
|
|
11
|
+
import * as tsx from "tsx/esm/api";
|
|
12
|
+
|
|
13
|
+
//#region src/confect/codegen.ts
|
|
14
|
+
const codegen = Command.make("codegen", {}, () => Effect.gen(function* () {
|
|
15
|
+
yield* codegenHandler;
|
|
16
|
+
yield* logCompleted("Generated files are up-to-date");
|
|
17
|
+
})).pipe(Command.withDescription("Generate `confect/_generated` files and the contents of the `convex` directory (except `tsconfig.json`)"));
|
|
18
|
+
const codegenHandler = Effect.gen(function* () {
|
|
19
|
+
yield* generateConfectGeneratedDirectory;
|
|
20
|
+
yield* Effect.all([
|
|
21
|
+
generateApi,
|
|
22
|
+
generateRefs,
|
|
23
|
+
generateRegisteredFunctions,
|
|
24
|
+
generateServices
|
|
25
|
+
], { concurrency: "unbounded" });
|
|
26
|
+
const [functionPaths] = yield* Effect.all([
|
|
27
|
+
generateFunctionModules,
|
|
28
|
+
generateSchema,
|
|
29
|
+
logGenerated(generateHttp),
|
|
30
|
+
logGenerated(generateConvexConfig),
|
|
31
|
+
logGenerated(generateCrons),
|
|
32
|
+
logGenerated(generateAuthConfig)
|
|
33
|
+
], { concurrency: "unbounded" });
|
|
34
|
+
return functionPaths;
|
|
35
|
+
});
|
|
36
|
+
const generateConfectGeneratedDirectory = Effect.gen(function* () {
|
|
37
|
+
const fs = yield* FileSystem.FileSystem;
|
|
38
|
+
const path = yield* Path.Path;
|
|
39
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
40
|
+
if (!(yield* fs.exists(path.join(confectDirectory, "_generated")))) {
|
|
41
|
+
yield* fs.makeDirectory(path.join(confectDirectory, "_generated"), { recursive: true });
|
|
42
|
+
yield* logFileAdded(path.join(confectDirectory, "_generated") + "/");
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
const generateApi = Effect.gen(function* () {
|
|
46
|
+
const path = yield* Path.Path;
|
|
47
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
48
|
+
const apiPath = path.join(confectDirectory, "_generated", "api.ts");
|
|
49
|
+
const schemaImportPath = yield* removePathExtension(path.relative(path.dirname(apiPath), path.join(confectDirectory, "schema.ts")));
|
|
50
|
+
const specImportPath = yield* removePathExtension(path.relative(path.dirname(apiPath), path.join(confectDirectory, "spec.ts")));
|
|
51
|
+
yield* writeFileStringAndLog(apiPath, yield* api({
|
|
52
|
+
schemaImportPath,
|
|
53
|
+
specImportPath
|
|
54
|
+
}));
|
|
55
|
+
});
|
|
56
|
+
const generateFunctionModules = Effect.gen(function* () {
|
|
57
|
+
const path = yield* Path.Path;
|
|
58
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
59
|
+
const specPath = path.join(confectDirectory, "spec.ts");
|
|
60
|
+
const specPathUrl = yield* path.toFileUrl(specPath);
|
|
61
|
+
const spec = (yield* Effect.promise(() => tsx.tsImport(specPathUrl.href, import.meta.url))).default;
|
|
62
|
+
if (!Spec_exports.isSpec(spec)) return yield* Effect.die("spec.ts does not export a valid Spec");
|
|
63
|
+
return yield* generateFunctions(spec);
|
|
64
|
+
});
|
|
65
|
+
const generateSchema = Effect.gen(function* () {
|
|
66
|
+
const path = yield* Path.Path;
|
|
67
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
68
|
+
const convexDirectory = yield* ConvexDirectory.get;
|
|
69
|
+
const confectSchemaPath = path.join(confectDirectory, "schema.ts");
|
|
70
|
+
const confectSchemaUrl = yield* path.toFileUrl(confectSchemaPath);
|
|
71
|
+
yield* Effect.promise(() => tsx.tsImport(confectSchemaUrl.href, import.meta.url)).pipe(Effect.andThen((schemaModule) => {
|
|
72
|
+
const defaultExport = schemaModule.default;
|
|
73
|
+
return DatabaseSchema.isSchema(defaultExport) ? Effect.succeed(defaultExport) : Effect.die("Invalid schema module");
|
|
74
|
+
}));
|
|
75
|
+
const convexSchemaPath = path.join(convexDirectory, "schema.ts");
|
|
76
|
+
const importPathWithoutExt = yield* removePathExtension(path.relative(path.dirname(convexSchemaPath), confectSchemaPath));
|
|
77
|
+
yield* writeFileStringAndLog(convexSchemaPath, yield* schema({ schemaImportPath: importPathWithoutExt }));
|
|
78
|
+
});
|
|
79
|
+
const generateServices = Effect.gen(function* () {
|
|
80
|
+
const path = yield* Path.Path;
|
|
81
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
82
|
+
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
83
|
+
const servicesPath = path.join(confectGeneratedDirectory, "services.ts");
|
|
84
|
+
const schemaImportPath = path.relative(path.dirname(servicesPath), path.join(confectDirectory, "schema"));
|
|
85
|
+
yield* writeFileStringAndLog(servicesPath, yield* services({ schemaImportPath }));
|
|
86
|
+
});
|
|
87
|
+
const generateRegisteredFunctions = Effect.gen(function* () {
|
|
88
|
+
const path = yield* Path.Path;
|
|
89
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
90
|
+
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
91
|
+
const registeredFunctionsPath = path.join(confectGeneratedDirectory, "registeredFunctions.ts");
|
|
92
|
+
const implImportPath = yield* removePathExtension(path.relative(path.dirname(registeredFunctionsPath), path.join(confectDirectory, "impl.ts")));
|
|
93
|
+
yield* writeFileStringAndLog(registeredFunctionsPath, yield* registeredFunctions({ implImportPath }));
|
|
94
|
+
});
|
|
95
|
+
const generateRefs = Effect.gen(function* () {
|
|
96
|
+
const path = yield* Path.Path;
|
|
97
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
98
|
+
const confectGeneratedDirectory = path.join(confectDirectory, "_generated");
|
|
99
|
+
const confectSpecPath = path.join(confectDirectory, "spec.ts");
|
|
100
|
+
const refsPath = path.join(confectGeneratedDirectory, "refs.ts");
|
|
101
|
+
const importPathWithoutExt = yield* removePathExtension(path.relative(path.dirname(refsPath), confectSpecPath));
|
|
102
|
+
yield* writeFileStringAndLog(refsPath, yield* refs({ specImportPath: importPathWithoutExt }));
|
|
103
|
+
});
|
|
104
|
+
const logGenerated = (effect) => effect.pipe(Effect.tap(Option.match({
|
|
105
|
+
onNone: () => Effect.void,
|
|
106
|
+
onSome: ({ change, convexFilePath }) => Match.value(change).pipe(Match.when("Added", () => logFileAdded(convexFilePath)), Match.when("Modified", () => logFileModified(convexFilePath)), Match.when("Unchanged", () => Effect.void), Match.exhaustive)
|
|
107
|
+
})));
|
|
108
|
+
|
|
109
|
+
//#endregion
|
|
110
|
+
export { codegen, codegenHandler };
|
|
111
|
+
//# sourceMappingURL=codegen.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"codegen.mjs","names":["templates.api","Spec","templates.schema","templates.services","templates.registeredFunctions","templates.refs"],"sources":["../../src/confect/codegen.ts"],"sourcesContent":["import { Spec } from \"@confect/core\";\nimport { DatabaseSchema } from \"@confect/server\";\nimport { Command } from \"@effect/cli\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Effect, Match, Option } from \"effect\";\nimport * as tsx from \"tsx/esm/api\";\nimport { logCompleted, logFileAdded, logFileModified } from \"../log\";\nimport { ConfectDirectory } from \"../services/ConfectDirectory\";\nimport { ConvexDirectory } from \"../services/ConvexDirectory\";\nimport * as templates from \"../templates\";\nimport {\n generateAuthConfig,\n generateConvexConfig,\n generateCrons,\n generateFunctions,\n generateHttp,\n removePathExtension,\n writeFileStringAndLog,\n} from \"../utils\";\n\nexport const codegen = Command.make(\"codegen\", {}, () =>\n Effect.gen(function* () {\n yield* codegenHandler;\n yield* logCompleted(\"Generated files are up-to-date\");\n }),\n).pipe(\n Command.withDescription(\n \"Generate `confect/_generated` files and the contents of the `convex` directory (except `tsconfig.json`)\",\n ),\n);\n\nexport const codegenHandler = Effect.gen(function* () {\n yield* generateConfectGeneratedDirectory;\n yield* Effect.all(\n [generateApi, generateRefs, generateRegisteredFunctions, generateServices],\n { concurrency: \"unbounded\" },\n );\n const [functionPaths] = yield* Effect.all(\n [\n generateFunctionModules,\n generateSchema,\n logGenerated(generateHttp),\n logGenerated(generateConvexConfig),\n logGenerated(generateCrons),\n logGenerated(generateAuthConfig),\n ],\n { concurrency: \"unbounded\" },\n );\n return functionPaths;\n});\n\nconst generateConfectGeneratedDirectory = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n if (!(yield* fs.exists(path.join(confectDirectory, \"_generated\")))) {\n yield* fs.makeDirectory(path.join(confectDirectory, \"_generated\"), {\n recursive: true,\n });\n yield* logFileAdded(path.join(confectDirectory, \"_generated\") + \"/\");\n }\n});\n\nconst generateApi = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const apiPath = path.join(confectDirectory, \"_generated\", \"api.ts\");\n\n const schemaImportPath = yield* removePathExtension(\n path.relative(\n path.dirname(apiPath),\n path.join(confectDirectory, \"schema.ts\"),\n ),\n );\n\n const specImportPath = yield* removePathExtension(\n path.relative(\n path.dirname(apiPath),\n path.join(confectDirectory, \"spec.ts\"),\n ),\n );\n\n const apiContents = yield* templates.api({\n schemaImportPath,\n specImportPath,\n });\n\n yield* writeFileStringAndLog(apiPath, apiContents);\n});\n\nconst generateFunctionModules = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const specPath = path.join(confectDirectory, \"spec.ts\");\n const specPathUrl = yield* path.toFileUrl(specPath);\n\n const specModule = yield* Effect.promise(() =>\n tsx.tsImport(specPathUrl.href, import.meta.url),\n );\n const spec = specModule.default;\n\n if (!Spec.isSpec(spec)) {\n return yield* Effect.die(\"spec.ts does not export a valid Spec\");\n }\n\n return yield* generateFunctions(spec);\n});\n\nconst generateSchema = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const convexDirectory = yield* ConvexDirectory.get;\n\n const confectSchemaPath = path.join(confectDirectory, \"schema.ts\");\n const confectSchemaUrl = yield* path.toFileUrl(confectSchemaPath);\n\n yield* Effect.promise(() =>\n tsx.tsImport(confectSchemaUrl.href, import.meta.url),\n ).pipe(\n Effect.andThen((schemaModule) => {\n const defaultExport = schemaModule.default;\n\n return DatabaseSchema.isSchema(defaultExport)\n ? Effect.succeed(defaultExport)\n : Effect.die(\"Invalid schema module\");\n }),\n );\n\n const convexSchemaPath = path.join(convexDirectory, \"schema.ts\");\n\n const relativeImportPath = path.relative(\n path.dirname(convexSchemaPath),\n confectSchemaPath,\n );\n const importPathWithoutExt = yield* removePathExtension(relativeImportPath);\n const schemaContents = yield* templates.schema({\n schemaImportPath: importPathWithoutExt,\n });\n\n yield* writeFileStringAndLog(convexSchemaPath, schemaContents);\n});\n\nconst generateServices = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n\n const servicesPath = path.join(confectGeneratedDirectory, \"services.ts\");\n const schemaImportPath = path.relative(\n path.dirname(servicesPath),\n path.join(confectDirectory, \"schema\"),\n );\n\n const servicesContentsString = yield* templates.services({\n schemaImportPath,\n });\n\n yield* writeFileStringAndLog(servicesPath, servicesContentsString);\n});\n\nconst generateRegisteredFunctions = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n\n const registeredFunctionsPath = path.join(\n confectGeneratedDirectory,\n \"registeredFunctions.ts\",\n );\n const implImportPath = yield* removePathExtension(\n path.relative(\n path.dirname(registeredFunctionsPath),\n path.join(confectDirectory, \"impl.ts\"),\n ),\n );\n\n const registeredFunctionsContents = yield* templates.registeredFunctions({\n implImportPath,\n });\n\n yield* writeFileStringAndLog(\n registeredFunctionsPath,\n registeredFunctionsContents,\n );\n});\n\nconst generateRefs = Effect.gen(function* () {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const confectGeneratedDirectory = path.join(confectDirectory, \"_generated\");\n\n const confectSpecPath = path.join(confectDirectory, \"spec.ts\");\n const refsPath = path.join(confectGeneratedDirectory, \"refs.ts\");\n\n const relativeImportPath = path.relative(\n path.dirname(refsPath),\n confectSpecPath,\n );\n const importPathWithoutExt = yield* removePathExtension(relativeImportPath);\n\n const refsContents = yield* templates.refs({\n specImportPath: importPathWithoutExt,\n });\n\n yield* writeFileStringAndLog(refsPath, refsContents);\n});\n\nconst logGenerated = (effect: typeof generateHttp) =>\n effect.pipe(\n Effect.tap(\n Option.match({\n onNone: () => Effect.void,\n onSome: ({ change, convexFilePath }) =>\n Match.value(change).pipe(\n Match.when(\"Added\", () => logFileAdded(convexFilePath)),\n Match.when(\"Modified\", () => logFileModified(convexFilePath)),\n Match.when(\"Unchanged\", () => Effect.void),\n Match.exhaustive,\n ),\n }),\n ),\n );\n"],"mappings":"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"}
|
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
import { Spec_exports } from "../packages/core/dist/Spec.mjs";
|
|
2
|
+
import { modulePath, toString } from "../GroupPath.mjs";
|
|
3
|
+
import { ProjectRoot } from "../services/ProjectRoot.mjs";
|
|
4
|
+
import { logCompleted, logFailed } from "../log.mjs";
|
|
5
|
+
import { ConvexDirectory } from "../services/ConvexDirectory.mjs";
|
|
6
|
+
import { ConfectDirectory } from "../services/ConfectDirectory.mjs";
|
|
7
|
+
import { diff, make } from "../FunctionPaths.mjs";
|
|
8
|
+
import { generateAuthConfig, generateConvexConfig, generateCrons, generateHttp, removeGroups, writeGroups } from "../utils.mjs";
|
|
9
|
+
import { codegenHandler } from "./codegen.mjs";
|
|
10
|
+
import { Array, Console, Data, Duration, Effect, Equal, HashSet, Match, Option, Queue, Ref, Schema, Stream, String, pipe } from "effect";
|
|
11
|
+
import { Command } from "@effect/cli";
|
|
12
|
+
import { FileSystem, Path } from "@effect/platform";
|
|
13
|
+
import * as tsx from "tsx/esm/api";
|
|
14
|
+
import { Ansi, AnsiDoc } from "@effect/printer-ansi";
|
|
15
|
+
import * as esbuild from "esbuild";
|
|
16
|
+
|
|
17
|
+
//#region src/confect/dev.ts
|
|
18
|
+
const pendingInit = {
|
|
19
|
+
specDirty: false,
|
|
20
|
+
httpDirty: false,
|
|
21
|
+
appDirty: false,
|
|
22
|
+
cronsDirty: false,
|
|
23
|
+
authDirty: false
|
|
24
|
+
};
|
|
25
|
+
const FileChange = Data.taggedEnum();
|
|
26
|
+
const logChangeReport = (changes) => Effect.gen(function* () {
|
|
27
|
+
yield* logCompleted("Generated files are up-to-date");
|
|
28
|
+
yield* Effect.when(Effect.forEach(changes, (change) => FileChange.$match(change, {
|
|
29
|
+
OptionalFile: ({ change: c, filePath }) => logFileChangeIndented(c, filePath),
|
|
30
|
+
GroupModule: ({ change: c, filePath, functionsAdded, functionsRemoved }) => Effect.gen(function* () {
|
|
31
|
+
yield* logFileChangeIndented(c, filePath);
|
|
32
|
+
yield* Effect.forEach(functionsAdded, logFunctionAddedIndented);
|
|
33
|
+
yield* Effect.forEach(functionsRemoved, logFunctionRemovedIndented);
|
|
34
|
+
})
|
|
35
|
+
})), () => Array.isNonEmptyReadonlyArray(changes));
|
|
36
|
+
});
|
|
37
|
+
const changeChar = (change) => Match.value(change).pipe(Match.when("Added", () => ({
|
|
38
|
+
char: "+",
|
|
39
|
+
color: Ansi.green
|
|
40
|
+
})), Match.when("Removed", () => ({
|
|
41
|
+
char: "-",
|
|
42
|
+
color: Ansi.red
|
|
43
|
+
})), Match.when("Modified", () => ({
|
|
44
|
+
char: "~",
|
|
45
|
+
color: Ansi.yellow
|
|
46
|
+
})), Match.exhaustive);
|
|
47
|
+
const logFileChangeIndented = (change, fullPath) => Effect.gen(function* () {
|
|
48
|
+
const prefix = (yield* ProjectRoot.get) + (yield* Path.Path).sep;
|
|
49
|
+
const suffix = pipe(fullPath, String.startsWith(prefix)) ? pipe(fullPath, String.slice(prefix.length)) : fullPath;
|
|
50
|
+
const { char, color } = changeChar(change);
|
|
51
|
+
yield* Console.log(pipe(AnsiDoc.text(" "), AnsiDoc.cat(pipe(AnsiDoc.char(char), AnsiDoc.annotate(color))), AnsiDoc.catWithSpace(AnsiDoc.hcat([pipe(AnsiDoc.text(prefix), AnsiDoc.annotate(Ansi.blackBright)), pipe(AnsiDoc.text(suffix), AnsiDoc.annotate(color))])), AnsiDoc.render({ style: "pretty" })));
|
|
52
|
+
});
|
|
53
|
+
const logFunctionAddedIndented = (functionPath) => Console.log(pipe(AnsiDoc.text(" "), AnsiDoc.cat(pipe(AnsiDoc.char("+"), AnsiDoc.annotate(Ansi.green))), AnsiDoc.catWithSpace(AnsiDoc.hcat([pipe(AnsiDoc.text(toString(functionPath.groupPath) + "."), AnsiDoc.annotate(Ansi.blackBright)), pipe(AnsiDoc.text(functionPath.name), AnsiDoc.annotate(Ansi.green))])), AnsiDoc.render({ style: "pretty" })));
|
|
54
|
+
const logFunctionRemovedIndented = (functionPath) => Console.log(pipe(AnsiDoc.text(" "), AnsiDoc.cat(pipe(AnsiDoc.char("-"), AnsiDoc.annotate(Ansi.red))), AnsiDoc.catWithSpace(AnsiDoc.hcat([pipe(AnsiDoc.text(toString(functionPath.groupPath) + "."), AnsiDoc.annotate(Ansi.blackBright)), pipe(AnsiDoc.text(functionPath.name), AnsiDoc.annotate(Ansi.red))])), AnsiDoc.render({ style: "pretty" })));
|
|
55
|
+
const dev = Command.make("dev", {}, () => Effect.gen(function* () {
|
|
56
|
+
const initialFunctionPaths = yield* codegenHandler;
|
|
57
|
+
const pendingRef = yield* Ref.make(pendingInit);
|
|
58
|
+
const signal = yield* Queue.sliding(1);
|
|
59
|
+
yield* Effect.all([
|
|
60
|
+
specFileWatcher(signal, pendingRef),
|
|
61
|
+
confectDirectoryWatcher(signal, pendingRef),
|
|
62
|
+
syncLoop(signal, pendingRef, initialFunctionPaths)
|
|
63
|
+
], { concurrency: "unbounded" });
|
|
64
|
+
})).pipe(Command.withDescription("Start the Confect development server"));
|
|
65
|
+
const syncLoop = (signal, pendingRef, initialFunctionPaths) => Effect.gen(function* () {
|
|
66
|
+
const functionPathsRef = yield* Ref.make(initialFunctionPaths);
|
|
67
|
+
const changesRef = yield* Ref.make([]);
|
|
68
|
+
return yield* Effect.forever(Effect.gen(function* () {
|
|
69
|
+
yield* Effect.logDebug("Running sync loop...");
|
|
70
|
+
yield* Queue.take(signal);
|
|
71
|
+
const pending = yield* Ref.getAndSet(pendingRef, pendingInit);
|
|
72
|
+
const specResult = yield* Effect.if(pending.specDirty, {
|
|
73
|
+
onTrue: () => loadSpec.pipe(Effect.andThen(Effect.fn(function* (spec) {
|
|
74
|
+
yield* Effect.logDebug("Spec loaded");
|
|
75
|
+
const previous = yield* Ref.get(functionPathsRef);
|
|
76
|
+
const path = yield* Path.Path;
|
|
77
|
+
const convexDirectory = yield* ConvexDirectory.get;
|
|
78
|
+
const current = make(spec);
|
|
79
|
+
const { functionsAdded, functionsRemoved, groupsRemoved, groupsAdded, groupsChanged } = diff(previous, current);
|
|
80
|
+
yield* removeGroups(groupsRemoved);
|
|
81
|
+
const removedChanges = yield* Effect.forEach(groupsRemoved, (gp) => Effect.gen(function* () {
|
|
82
|
+
const relativeModulePath = yield* modulePath(gp);
|
|
83
|
+
return FileChange.GroupModule({
|
|
84
|
+
change: "Removed",
|
|
85
|
+
filePath: path.join(convexDirectory, relativeModulePath),
|
|
86
|
+
functionsAdded: [],
|
|
87
|
+
functionsRemoved: Array.fromIterable(HashSet.filter(functionsRemoved, (fp) => Equal.equals(fp.groupPath, gp)))
|
|
88
|
+
});
|
|
89
|
+
}));
|
|
90
|
+
yield* writeGroups(spec, groupsAdded);
|
|
91
|
+
const addedChanges = yield* Effect.forEach(groupsAdded, (gp) => Effect.gen(function* () {
|
|
92
|
+
const relativeModulePath = yield* modulePath(gp);
|
|
93
|
+
return FileChange.GroupModule({
|
|
94
|
+
change: "Added",
|
|
95
|
+
filePath: path.join(convexDirectory, relativeModulePath),
|
|
96
|
+
functionsAdded: Array.fromIterable(HashSet.filter(functionsAdded, (fp) => Equal.equals(fp.groupPath, gp))),
|
|
97
|
+
functionsRemoved: []
|
|
98
|
+
});
|
|
99
|
+
}));
|
|
100
|
+
yield* writeGroups(spec, groupsChanged);
|
|
101
|
+
const changedChanges = yield* Effect.forEach(groupsChanged, (gp) => Effect.gen(function* () {
|
|
102
|
+
const relativeModulePath = yield* modulePath(gp);
|
|
103
|
+
return FileChange.GroupModule({
|
|
104
|
+
change: "Modified",
|
|
105
|
+
filePath: path.join(convexDirectory, relativeModulePath),
|
|
106
|
+
functionsAdded: Array.fromIterable(HashSet.filter(functionsAdded, (fp) => Equal.equals(fp.groupPath, gp))),
|
|
107
|
+
functionsRemoved: Array.fromIterable(HashSet.filter(functionsRemoved, (fp) => Equal.equals(fp.groupPath, gp)))
|
|
108
|
+
});
|
|
109
|
+
}));
|
|
110
|
+
yield* Ref.set(functionPathsRef, current);
|
|
111
|
+
return Option.some([
|
|
112
|
+
...removedChanges,
|
|
113
|
+
...addedChanges,
|
|
114
|
+
...changedChanges
|
|
115
|
+
]);
|
|
116
|
+
})), Effect.catchTag("SpecImportFailedError", () => logFailed("Spec import failed").pipe(Effect.as(Option.none()))), Effect.catchTag("SpecFileDoesNotExportSpecError", () => logFailed("Spec file does not default export a spec").pipe(Effect.as(Option.none())))),
|
|
117
|
+
onFalse: () => Effect.succeed(Option.some([]))
|
|
118
|
+
});
|
|
119
|
+
const specChanges = Option.getOrElse(specResult, () => []);
|
|
120
|
+
const dirtyOptionalFiles = [
|
|
121
|
+
...pending.httpDirty ? [syncOptionalFile(generateHttp, "http.ts")] : [],
|
|
122
|
+
...pending.appDirty ? [syncOptionalFile(generateConvexConfig, "convex.config.ts")] : [],
|
|
123
|
+
...pending.cronsDirty ? [syncOptionalFile(generateCrons, "crons.ts")] : [],
|
|
124
|
+
...pending.authDirty ? [syncOptionalFile(generateAuthConfig, "auth.config.ts")] : []
|
|
125
|
+
];
|
|
126
|
+
const optionalChanges = Array.isNonEmptyReadonlyArray(dirtyOptionalFiles) ? yield* pipe(Effect.all(dirtyOptionalFiles, { concurrency: "unbounded" }), Effect.map(Array.getSomes)) : [];
|
|
127
|
+
yield* Ref.update(changesRef, (prev) => [
|
|
128
|
+
...prev,
|
|
129
|
+
...specChanges,
|
|
130
|
+
...optionalChanges
|
|
131
|
+
]);
|
|
132
|
+
yield* Option.match(specResult, {
|
|
133
|
+
onSome: () => Effect.gen(function* () {
|
|
134
|
+
const pendingSize = yield* Queue.size(signal);
|
|
135
|
+
yield* Effect.when(Effect.gen(function* () {
|
|
136
|
+
yield* logChangeReport(yield* Ref.getAndSet(changesRef, []));
|
|
137
|
+
}), () => pendingSize === 0);
|
|
138
|
+
}),
|
|
139
|
+
onNone: () => Ref.set(changesRef, [])
|
|
140
|
+
});
|
|
141
|
+
}));
|
|
142
|
+
});
|
|
143
|
+
const loadSpec = Effect.gen(function* () {
|
|
144
|
+
const specPathUrl = yield* (yield* Path.Path).toFileUrl(yield* getSpecPath);
|
|
145
|
+
const spec = (yield* Effect.tryPromise({
|
|
146
|
+
try: () => tsx.tsImport(specPathUrl.href, import.meta.url),
|
|
147
|
+
catch: (error) => new SpecImportFailedError({ error })
|
|
148
|
+
})).default;
|
|
149
|
+
if (Spec_exports.isSpec(spec)) return spec;
|
|
150
|
+
else return yield* Effect.fail(new SpecFileDoesNotExportSpecError());
|
|
151
|
+
});
|
|
152
|
+
const getSpecPath = Effect.gen(function* () {
|
|
153
|
+
const path = yield* Path.Path;
|
|
154
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
155
|
+
return path.join(confectDirectory, "spec.ts");
|
|
156
|
+
});
|
|
157
|
+
const specFileWatcher = (signal, pendingRef) => Effect.gen(function* () {
|
|
158
|
+
const specPath = yield* getSpecPath;
|
|
159
|
+
yield* pipe(Stream.asyncPush((emit) => Effect.acquireRelease(Effect.promise(async () => {
|
|
160
|
+
const ctx = await esbuild.context({
|
|
161
|
+
entryPoints: [specPath],
|
|
162
|
+
bundle: true,
|
|
163
|
+
write: false,
|
|
164
|
+
metafile: true,
|
|
165
|
+
platform: "node",
|
|
166
|
+
format: "esm",
|
|
167
|
+
logLevel: "silent",
|
|
168
|
+
external: [
|
|
169
|
+
"@confect/core",
|
|
170
|
+
"@confect/server",
|
|
171
|
+
"effect",
|
|
172
|
+
"@effect/*"
|
|
173
|
+
],
|
|
174
|
+
plugins: [{
|
|
175
|
+
name: "notify-rebuild",
|
|
176
|
+
setup(build) {
|
|
177
|
+
build.onEnd((result) => {
|
|
178
|
+
if (result.errors.length === 0) emit.single();
|
|
179
|
+
else Effect.runPromise(Effect.gen(function* () {
|
|
180
|
+
yield* logFailed("Build errors");
|
|
181
|
+
const formattedMessages = yield* Effect.promise(() => esbuild.formatMessages(result.errors, {
|
|
182
|
+
kind: "error",
|
|
183
|
+
color: true,
|
|
184
|
+
terminalWidth: 80
|
|
185
|
+
}));
|
|
186
|
+
const output = formatBuildErrors(result.errors, formattedMessages);
|
|
187
|
+
yield* Console.error("\n" + output + "\n");
|
|
188
|
+
}));
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
}]
|
|
192
|
+
});
|
|
193
|
+
await ctx.watch();
|
|
194
|
+
return ctx;
|
|
195
|
+
}), (ctx) => Effect.promise(() => ctx.dispose()).pipe(Effect.tap(() => Effect.logDebug("esbuild watcher disposed")))), {
|
|
196
|
+
bufferSize: 1,
|
|
197
|
+
strategy: "sliding"
|
|
198
|
+
}), Stream.debounce(Duration.millis(200)), Stream.runForEach(() => Ref.update(pendingRef, (pending) => ({
|
|
199
|
+
...pending,
|
|
200
|
+
specDirty: true
|
|
201
|
+
})).pipe(Effect.andThen(Queue.offer(signal, void 0)))));
|
|
202
|
+
});
|
|
203
|
+
const formatBuildError = (error, formattedMessage) => {
|
|
204
|
+
const lines = String.split(formattedMessage, "\n");
|
|
205
|
+
const redErrorText = pipe(AnsiDoc.text(error?.text ?? ""), AnsiDoc.annotate(Ansi.red), AnsiDoc.render({ style: "pretty" }));
|
|
206
|
+
return pipe(pipe(Array.findFirstIndex(lines, (l) => pipe(l, String.trim, String.isNonEmpty)), Option.match({
|
|
207
|
+
onNone: () => lines,
|
|
208
|
+
onSome: (idx) => Array.modify(lines, idx, () => redErrorText)
|
|
209
|
+
})), Array.map((l) => pipe(l, String.trim, String.isNonEmpty) ? ` ${l}` : l), Array.join("\n"));
|
|
210
|
+
};
|
|
211
|
+
const formatBuildErrors = (errors, formattedMessages) => pipe(formattedMessages, Array.map((message, i) => formatBuildError(errors[i], message)), Array.join(""), String.trimEnd);
|
|
212
|
+
var SpecFileDoesNotExportSpecError = class extends Schema.TaggedError("SpecFileDoesNotExportSpecError")("SpecFileDoesNotExportSpecError", {}) {};
|
|
213
|
+
var SpecImportFailedError = class extends Schema.TaggedError("SpecImportFailedError")("SpecImportFailedError", { error: Schema.Unknown }) {};
|
|
214
|
+
const syncOptionalFile = (generate, convexFile) => pipe(generate, Effect.andThen(Option.match({
|
|
215
|
+
onSome: ({ change, convexFilePath }) => Match.value(change).pipe(Match.when("Unchanged", () => Effect.succeed(Option.none())), Match.whenOr("Added", "Modified", (addedOrModified) => Effect.succeed(Option.some(FileChange.OptionalFile({
|
|
216
|
+
change: addedOrModified,
|
|
217
|
+
filePath: convexFilePath
|
|
218
|
+
})))), Match.exhaustive),
|
|
219
|
+
onNone: () => Effect.gen(function* () {
|
|
220
|
+
const fs = yield* FileSystem.FileSystem;
|
|
221
|
+
const path = yield* Path.Path;
|
|
222
|
+
const convexDirectory = yield* ConvexDirectory.get;
|
|
223
|
+
const convexFilePath = path.join(convexDirectory, convexFile);
|
|
224
|
+
if (yield* fs.exists(convexFilePath)) {
|
|
225
|
+
yield* fs.remove(convexFilePath);
|
|
226
|
+
return Option.some(FileChange.OptionalFile({
|
|
227
|
+
change: "Removed",
|
|
228
|
+
filePath: convexFilePath
|
|
229
|
+
}));
|
|
230
|
+
} else return Option.none();
|
|
231
|
+
})
|
|
232
|
+
})));
|
|
233
|
+
const optionalConfectFiles = {
|
|
234
|
+
"http.ts": "httpDirty",
|
|
235
|
+
"app.ts": "appDirty",
|
|
236
|
+
"crons.ts": "cronsDirty",
|
|
237
|
+
"auth.ts": "authDirty"
|
|
238
|
+
};
|
|
239
|
+
const confectDirectoryWatcher = (signal, pendingRef) => Effect.gen(function* () {
|
|
240
|
+
const fs = yield* FileSystem.FileSystem;
|
|
241
|
+
const confectDirectory = yield* ConfectDirectory.get;
|
|
242
|
+
yield* pipe(fs.watch(confectDirectory), Stream.runForEach((event) => pipe(Option.fromNullable(optionalConfectFiles[event.path]), Option.match({
|
|
243
|
+
onNone: () => Effect.void,
|
|
244
|
+
onSome: (pendingKey) => pipe(pendingRef, Ref.update((pending) => ({
|
|
245
|
+
...pending,
|
|
246
|
+
[pendingKey]: true
|
|
247
|
+
})), Effect.andThen(Queue.offer(signal, void 0)))
|
|
248
|
+
}))));
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
//#endregion
|
|
252
|
+
export { dev };
|
|
253
|
+
//# sourceMappingURL=dev.mjs.map
|