@gaganref/confect-cli 0.0.0-stage → 10.3.1-fork.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.
Files changed (99) hide show
  1. package/CHANGELOG.md +1824 -0
  2. package/LICENSE +7 -0
  3. package/README.md +16 -2
  4. package/bin/confect.mjs +26 -0
  5. package/dist/Ansi.mjs +18 -0
  6. package/dist/Ansi.mjs.map +1 -0
  7. package/dist/BuildError.mjs +30 -0
  8. package/dist/BuildError.mjs.map +1 -0
  9. package/dist/Bundler.mjs +426 -0
  10. package/dist/Bundler.mjs.map +1 -0
  11. package/dist/CodeBlockWriter.mjs +55 -0
  12. package/dist/CodeBlockWriter.mjs.map +1 -0
  13. package/dist/CodegenError.mjs +194 -0
  14. package/dist/CodegenError.mjs.map +1 -0
  15. package/dist/ConfectDirectory.mjs +35 -0
  16. package/dist/ConfectDirectory.mjs.map +1 -0
  17. package/dist/ConvexConfig.mjs +198 -0
  18. package/dist/ConvexConfig.mjs.map +1 -0
  19. package/dist/ConvexDirectory.mjs +54 -0
  20. package/dist/ConvexDirectory.mjs.map +1 -0
  21. package/dist/Declaration.mjs +258 -0
  22. package/dist/Declaration.mjs.map +1 -0
  23. package/dist/DirectoryOptions.mjs +42 -0
  24. package/dist/DirectoryOptions.mjs.map +1 -0
  25. package/dist/DocName.mjs +25 -0
  26. package/dist/DocName.mjs.map +1 -0
  27. package/dist/EntryPoint.mjs +111 -0
  28. package/dist/EntryPoint.mjs.map +1 -0
  29. package/dist/Evaluation.mjs +117 -0
  30. package/dist/Evaluation.mjs.map +1 -0
  31. package/dist/EvaluationWorker.mjs +46 -0
  32. package/dist/EvaluationWorker.mjs.map +1 -0
  33. package/dist/FunctionModule.mjs +137 -0
  34. package/dist/FunctionModule.mjs.map +1 -0
  35. package/dist/FunctionPath.mjs +18 -0
  36. package/dist/FunctionPath.mjs.map +1 -0
  37. package/dist/FunctionPaths.mjs +45 -0
  38. package/dist/FunctionPaths.mjs.map +1 -0
  39. package/dist/GenerationSnapshot.mjs +183 -0
  40. package/dist/GenerationSnapshot.mjs.map +1 -0
  41. package/dist/GroupPath.mjs +51 -0
  42. package/dist/GroupPath.mjs.map +1 -0
  43. package/dist/GroupPaths.mjs +8 -0
  44. package/dist/GroupPaths.mjs.map +1 -0
  45. package/dist/ImplModulePath.mjs +8 -0
  46. package/dist/ImplModulePath.mjs.map +1 -0
  47. package/dist/LeafModule.mjs +285 -0
  48. package/dist/LeafModule.mjs.map +1 -0
  49. package/dist/ProjectRoot.mjs +38 -0
  50. package/dist/ProjectRoot.mjs.map +1 -0
  51. package/dist/RelativeModulePath.mjs +13 -0
  52. package/dist/RelativeModulePath.mjs.map +1 -0
  53. package/dist/Remove.mjs +24 -0
  54. package/dist/Remove.mjs.map +1 -0
  55. package/dist/RestoreWatcher.mjs +31 -0
  56. package/dist/RestoreWatcher.mjs.map +1 -0
  57. package/dist/Rollback.mjs +50 -0
  58. package/dist/Rollback.mjs.map +1 -0
  59. package/dist/SnapshotError.mjs +21 -0
  60. package/dist/SnapshotError.mjs.map +1 -0
  61. package/dist/SpecAssemblyNode.mjs +91 -0
  62. package/dist/SpecAssemblyNode.mjs.map +1 -0
  63. package/dist/SpecModulePath.mjs +10 -0
  64. package/dist/SpecModulePath.mjs.map +1 -0
  65. package/dist/SyncState.mjs +122 -0
  66. package/dist/SyncState.mjs.map +1 -0
  67. package/dist/TableModule.mjs +97 -0
  68. package/dist/TableModule.mjs.map +1 -0
  69. package/dist/TableModulePath.mjs +8 -0
  70. package/dist/TableModulePath.mjs.map +1 -0
  71. package/dist/WatchBuild.mjs +190 -0
  72. package/dist/WatchBuild.mjs.map +1 -0
  73. package/dist/WatchGraph.mjs +35 -0
  74. package/dist/WatchGraph.mjs.map +1 -0
  75. package/dist/WatchTopology.mjs +55 -0
  76. package/dist/WatchTopology.mjs.map +1 -0
  77. package/dist/Watcher.mjs +377 -0
  78. package/dist/Watcher.mjs.map +1 -0
  79. package/dist/cliApp.mjs +9 -0
  80. package/dist/cliApp.mjs.map +1 -0
  81. package/dist/confect/codegen.mjs +570 -0
  82. package/dist/confect/codegen.mjs.map +1 -0
  83. package/dist/confect/declaration.mjs +219 -0
  84. package/dist/confect/declaration.mjs.map +1 -0
  85. package/dist/confect/dev.mjs +182 -0
  86. package/dist/confect/dev.mjs.map +1 -0
  87. package/dist/confect.mjs +9 -0
  88. package/dist/confect.mjs.map +1 -0
  89. package/dist/index.mjs +19 -0
  90. package/dist/index.mjs.map +1 -0
  91. package/dist/log.mjs +107 -0
  92. package/dist/log.mjs.map +1 -0
  93. package/dist/package.mjs +6 -0
  94. package/dist/package.mjs.map +1 -0
  95. package/dist/templates.mjs +448 -0
  96. package/dist/templates.mjs.map +1 -0
  97. package/dist/utils.mjs +105 -0
  98. package/dist/utils.mjs.map +1 -0
  99. package/package.json +51 -3
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GenerationSnapshot.mjs","names":["GroupPath.GroupPath","GroupPath.make","GroupPath.append"],"sources":["../src/GenerationSnapshot.ts"],"sourcesContent":["import { type GroupSpec, Identifier } from \"@confect/core\";\nimport * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as HashMap from \"effect/HashMap\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as Match from \"effect/Match\";\nimport * as Option from \"effect/Option\";\nimport * as Record from \"effect/Record\";\nimport * as Result from \"effect/Result\";\nimport * as Schema from \"effect/Schema\";\nimport * as SchemaGetter from \"effect/SchemaGetter\";\nimport * as SchemaIssue from \"effect/SchemaIssue\";\nimport * as String from \"effect/String\";\nimport { ParentChildNameCollisionError } from \"./CodegenError.js\";\nimport * as FunctionPath from \"./FunctionPath.js\";\nimport * as FunctionPaths from \"./FunctionPaths.js\";\nimport * as GroupPath from \"./GroupPath.js\";\nimport type { LoadedLeafModule } from \"./LeafModule.js\";\nimport * as SpecModulePath from \"./SpecModulePath.js\";\nimport { SnapshotError } from \"./SnapshotError.js\";\n\nexport { SnapshotError } from \"./SnapshotError.js\";\n\nexport const GroupSummary = Schema.Struct({\n path: GroupPath.GroupPath,\n runtime: Schema.Literals([\"Convex\", \"Node\"]),\n functionNames: Schema.Array(Schema.NonEmptyString),\n});\nexport interface GroupSummary extends Schema.Schema.Type<typeof GroupSummary> {}\n\nexport interface GroupTree {\n readonly runtime: \"Convex\" | \"Node\";\n readonly functionNames: ReadonlyArray<string>;\n readonly groups: { readonly [name: string]: GroupTree };\n}\n\nexport const GroupTree: Schema.Codec<GroupTree> = Schema.Struct({\n runtime: GroupSummary.fields.runtime,\n functionNames: GroupSummary.fields.functionNames,\n groups: Schema.Record(\n Schema.String,\n Schema.suspend(() => GroupTree),\n ),\n});\n\nclass Snapshot {\n readonly #leaves: HashMap.HashMap<SpecModulePath.SpecModulePath, GroupTree>;\n\n private constructor(\n leaves: HashMap.HashMap<SpecModulePath.SpecModulePath, GroupTree>,\n ) {\n this.#leaves = leaves;\n }\n\n static parseLeaves(\n leaves: HashMap.HashMap<SpecModulePath.SpecModulePath, GroupTree>,\n ): Result.Result<GenerationSnapshot, ParentChildNameCollisionError> {\n return Result.map(\n parseNoParentChildNameCollisions(leaves),\n (parsed) => new Snapshot(parsed),\n );\n }\n\n get leaves(): HashMap.HashMap<SpecModulePath.SpecModulePath, GroupTree> {\n return this.#leaves;\n }\n}\n\nexport interface GenerationSnapshot extends Snapshot {}\n\nconst Entries = Schema.Array(Schema.Tuple([Schema.String, GroupTree]));\nconst CanonicalSpecPath = SpecModulePath.SpecModulePath.check(\n Schema.makeFilter((path) => !path.includes(\"\\\\\")),\n);\nconst rootPath = (path: SpecModulePath.SpecModulePath) =>\n GroupPath.make(String.split(path.slice(0, -\".spec.ts\".length), \"/\"));\n\nconst parseTree = (\n relativePath: SpecModulePath.SpecModulePath,\n path: GroupPath.GroupPath,\n tree: GroupTree,\n): Result.Result<GroupTree, SnapshotError> =>\n Result.gen(function* () {\n yield* Result.try({\n try: () => {\n for (const name of tree.functionNames)\n Identifier.validateConfectFunctionIdentifier(name);\n },\n catch: () =>\n new SnapshotError({\n reason: \"NoncanonicalFunctionNames\",\n relativePath,\n pathSegments: path.pathSegments,\n }),\n });\n if (\n HashSet.size(HashSet.fromIterable(tree.functionNames)) !==\n tree.functionNames.length\n ) {\n return yield* Result.fail(\n new SnapshotError({\n reason: \"NoncanonicalFunctionNames\",\n relativePath,\n pathSegments: path.pathSegments,\n }),\n );\n }\n for (const [name, child] of Object.entries(tree.groups)) {\n yield* Result.try({\n try: () => Identifier.validateConfectFunctionIdentifier(name),\n catch: () =>\n new SnapshotError({\n reason: \"NoncanonicalPath\",\n relativePath,\n pathSegments: [...path.pathSegments, name],\n }),\n });\n yield* parseTree(relativePath, GroupPath.append(path, name), child);\n }\n return tree;\n });\n\nconst fromEntries = (\n entries: ReadonlyArray<readonly [string, GroupTree]>,\n): Result.Result<\n GenerationSnapshot,\n SnapshotError | ParentChildNameCollisionError\n> =>\n Result.gen(function* () {\n let leaves = HashMap.empty<SpecModulePath.SpecModulePath, GroupTree>();\n for (const [key, tree] of entries) {\n const relativePath = yield* Schema.decodeResult(CanonicalSpecPath)(\n key,\n ).pipe(\n Result.mapError(\n (cause) =>\n new SnapshotError({\n reason: \"NoncanonicalPath\",\n relativePath: key,\n pathSegments: [],\n cause,\n }),\n ),\n );\n const path = rootPath(relativePath);\n yield* Result.try({\n try: () => {\n for (const segment of path.pathSegments)\n Identifier.validateConfectFunctionIdentifier(segment);\n },\n catch: () =>\n new SnapshotError({\n reason: \"NoncanonicalPath\",\n relativePath,\n pathSegments: path.pathSegments,\n }),\n });\n if (HashMap.has(leaves, relativePath)) {\n return yield* Result.fail(\n new SnapshotError({\n reason: \"DuplicateLeaf\",\n relativePath,\n pathSegments: path.pathSegments,\n }),\n );\n }\n const parsed = yield* parseTree(relativePath, path, tree);\n leaves = HashMap.set(leaves, relativePath, parsed);\n }\n return yield* Snapshot.parseLeaves(leaves);\n });\n\nexport const parse = (\n input: unknown,\n): Result.Result<\n GenerationSnapshot,\n SnapshotError | ParentChildNameCollisionError\n> =>\n Schema.decodeUnknownResult(Entries)(input).pipe(\n Result.mapError(\n (cause) =>\n new SnapshotError({\n reason: \"InvalidShape\",\n relativePath: \"\",\n pathSegments: [],\n cause,\n }),\n ),\n Result.flatMap(fromEntries),\n );\n\nexport const GenerationSnapshot = Entries.pipe(\n Schema.decodeTo(\n Schema.declare<GenerationSnapshot>((input) => input instanceof Snapshot),\n {\n decode: SchemaGetter.transformEffect((entries, options) =>\n Effect.fromResult(fromEntries(entries)).pipe(\n Effect.mapError(\n (error) =>\n new SchemaIssue.InvalidValue(\n {\n message: Match.value(error).pipe(\n Match.tagsExhaustive({\n SnapshotError: (failure) => failure.message,\n ParentChildNameCollisionError: (failure) =>\n `${failure.parentSpecPath} declares a ${failure.collisionKind} named ${failure.collisionName} that collides with ${failure.childSpecPath}`,\n }),\n ),\n },\n entries,\n options,\n ),\n ),\n ),\n ),\n encode: SchemaGetter.transform((snapshot) =>\n HashMap.toEntries(snapshot.leaves),\n ),\n },\n ),\n);\n\nconst summarize = (group: GroupSpec.AnyWithProps): GroupTree => ({\n runtime: group.runtime,\n functionNames: Record.keys(group.functions),\n groups: Record.map(group.groups, summarize),\n});\n\nexport const update = (\n snapshot: GenerationSnapshot,\n leaves: ReadonlyArray<LoadedLeafModule>,\n): Result.Result<\n GenerationSnapshot,\n SnapshotError | ParentChildNameCollisionError\n> =>\n Result.gen(function* () {\n if (leaves.length === 0) return snapshot;\n const replacement = yield* fromLoadedLeaves(leaves);\n return yield* Snapshot.parseLeaves(\n HashMap.union(snapshot.leaves, replacement.leaves),\n );\n });\n\nexport const fromLoadedLeaves = (\n leaves: ReadonlyArray<LoadedLeafModule>,\n): Result.Result<\n GenerationSnapshot,\n SnapshotError | ParentChildNameCollisionError\n> =>\n parse(\n leaves.map(\n (leaf) =>\n [\n leaf.relativePath.replaceAll(\"\\\\\", \"/\"),\n summarize(leaf.spec),\n ] as const,\n ),\n );\n\nconst groups = (snapshot: GenerationSnapshot): ReadonlyArray<GroupSummary> => {\n const visit = (\n path: GroupPath.GroupPath,\n tree: GroupTree,\n ): ReadonlyArray<GroupSummary> => [\n {\n path,\n runtime: tree.runtime,\n functionNames: Array.sort(tree.functionNames, String.Order),\n },\n ...Array.flatMap(\n Array.sort(Object.keys(tree.groups), String.Order),\n (name: string) => visit(GroupPath.append(path, name), tree.groups[name]!),\n ),\n ];\n return Array.flatMap(Array.fromIterable(snapshot.leaves), ([path, tree]) =>\n visit(rootPath(path), tree),\n );\n};\n\nexport const functionPaths = (\n snapshot: GenerationSnapshot,\n): FunctionPaths.FunctionPaths =>\n FunctionPaths.FunctionPaths.make(\n HashSet.fromIterable(\n Array.flatMap(groups(snapshot), (group) =>\n group.functionNames.map((name) =>\n FunctionPath.FunctionPath.make({ groupPath: group.path, name }),\n ),\n ),\n ),\n );\n\nconst generatedGroups = (snapshot: GenerationSnapshot) =>\n HashMap.fromIterable(\n Array.filterMap(groups(snapshot), (group) =>\n group.functionNames.length === 0\n ? Result.fail(undefined)\n : Result.succeed([group.path, group] as const),\n ),\n );\n\nexport interface ModulePlan {\n readonly removed: ReadonlyArray<GroupPath.GroupPath>;\n readonly changed: ReadonlyArray<GroupSummary>;\n}\n\nexport const plan = (\n snapshot: GenerationSnapshot,\n existing: Iterable<GroupPath.GroupPath>,\n): ModulePlan => {\n const current = generatedGroups(snapshot);\n return {\n removed: Array.fromIterable(existing).filter(\n (path) => !HashMap.has(current, path),\n ),\n changed: Array.fromIterable(HashMap.values(current)),\n };\n};\n\nconst equivalentGroup = Schema.toEquivalence(GroupSummary);\n\nexport const diff = (\n before: GenerationSnapshot,\n after: GenerationSnapshot,\n): ModulePlan => {\n const previous = generatedGroups(before);\n const current = generatedGroups(after);\n return {\n removed: Array.filter(\n HashMap.keys(previous),\n (path) => !HashMap.has(current, path),\n ),\n changed: Array.filter(HashMap.values(current), (group) => {\n const old = HashMap.get(previous, group.path);\n return Option.isNone(old) || !equivalentGroup(old.value, group);\n }),\n };\n};\n\nconst parseNoParentChildNameCollisions = (\n leaves: HashMap.HashMap<SpecModulePath.SpecModulePath, GroupTree>,\n): Result.Result<\n HashMap.HashMap<SpecModulePath.SpecModulePath, GroupTree>,\n ParentChildNameCollisionError\n> => {\n for (const childPath of HashMap.keys(leaves)) {\n const segments = rootPath(childPath).pathSegments;\n for (let depth = 1; depth < segments.length; depth++) {\n const parentPath = SpecModulePath.SpecModulePath.make(\n `${segments.slice(0, depth).join(\"/\")}.spec.ts`,\n );\n const parent = HashMap.get(leaves, parentPath);\n if (Option.isNone(parent)) continue;\n const collisionName = segments[depth]!;\n const collisionKind = parent.value.functionNames.includes(collisionName)\n ? \"function\"\n : Object.hasOwn(parent.value.groups, collisionName)\n ? \"group\"\n : undefined;\n if (collisionKind !== undefined) {\n return Result.fail(\n new ParentChildNameCollisionError({\n parentSpecPath: parentPath,\n childSpecPath: childPath,\n collisionName,\n collisionKind,\n }),\n );\n }\n }\n }\n return Result.succeed(leaves);\n};\n"],"mappings":"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"}
@@ -0,0 +1,51 @@
1
+ import * as Effect from "effect/Effect";
2
+ import "@confect/core";
3
+ import * as Path from "effect/Path";
4
+ import * as Array from "effect/Array";
5
+ import "effect/Function";
6
+ import "effect/Option";
7
+ import "effect/Record";
8
+ import * as Schema from "effect/Schema";
9
+ import * as String from "effect/String";
10
+ //#region src/GroupPath.ts
11
+ /**
12
+ * The path to a group in the Confect API.
13
+ */
14
+ var GroupPath = class extends Schema.Class("GroupPath")({ pathSegments: Schema.NonEmptyArray(Schema.NonEmptyString) }) {};
15
+ /**
16
+ * Create a GroupPath from path segments.
17
+ */
18
+ const make = (pathSegments) => GroupPath.make({ pathSegments });
19
+ /**
20
+ * Append a group name to a GroupPath to create a new GroupPath.
21
+ */
22
+ const append = (groupPath, groupName) => make([...groupPath.pathSegments, groupName]);
23
+ /**
24
+ * Expects a path string of the form `./group1/group2.ts`, relative to the
25
+ * Convex functions directory.
26
+ */
27
+ const fromGroupModulePath = Effect.fnUntraced(function* (groupModulePath) {
28
+ const path = yield* Path.Path;
29
+ const { dir, name, ext } = path.parse(groupModulePath);
30
+ if (ext === ".ts") {
31
+ const dirSegments = Array.filter(String.split(dir, path.sep), String.isNonEmpty);
32
+ yield* Effect.logDebug(Array.append(dirSegments, name));
33
+ return make(Array.append(dirSegments, name));
34
+ } else return yield* new GroupModulePathIsNotATypeScriptFileError({ path: groupModulePath });
35
+ });
36
+ /**
37
+ * Get the module path for a group, relative to the Convex functions directory.
38
+ */
39
+ const modulePath = Effect.fnUntraced(function* (groupPath) {
40
+ return (yield* Path.Path).join(...groupPath.pathSegments) + ".ts";
41
+ });
42
+ const toString = (groupPath) => Array.join(groupPath.pathSegments, ".");
43
+ var GroupModulePathIsNotATypeScriptFileError = class extends Schema.TaggedError()("GroupModulePathIsNotATypeScriptFileError", { path: Schema.NonEmptyString }) {
44
+ get message() {
45
+ return `Expected group module path to end with .ts, got ${this.path}`;
46
+ }
47
+ };
48
+ //#endregion
49
+ export { GroupModulePathIsNotATypeScriptFileError, GroupPath, append, fromGroupModulePath, make, modulePath, toString };
50
+
51
+ //# sourceMappingURL=GroupPath.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupPath.mjs","names":[],"sources":["../src/GroupPath.ts"],"sourcesContent":["import { type GroupSpec, Spec } from \"@confect/core\";\nimport * as Path from \"effect/Path\";\nimport { pipe } from \"effect/Function\";\nimport * as Array from \"effect/Array\";\nimport * as Effect from \"effect/Effect\";\nimport * as Option from \"effect/Option\";\nimport * as Record from \"effect/Record\";\nimport * as Schema from \"effect/Schema\";\nimport * as String from \"effect/String\";\n\n/**\n * The path to a group in the Confect API.\n */\nexport class GroupPath extends Schema.Class<GroupPath>(\"GroupPath\")({\n pathSegments: 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 });\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\n * Convex functions directory.\n */\nexport const fromGroupModulePath = Effect.fnUntraced(function* (\n groupModulePath: string,\n) {\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* new GroupModulePathIsNotATypeScriptFileError({\n 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 = Effect.fnUntraced(function* (groupPath: GroupPath) {\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(spec), head),\n Option.flatMap((group) =>\n Array.isArrayNonEmpty(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.isArrayNonEmpty(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 {\n path: Schema.NonEmptyString,\n },\n) {\n override get message(): string {\n return `Expected group module path to end with .ts, got ${this.path}`;\n }\n}\n"],"mappings":";;;;;;;;;;;;;AAaA,IAAa,YAAb,cAA+B,OAAO,MAAiB,WAAW,CAAC,CAAC,EAClE,cAAc,OAAO,cAAc,OAAO,cAAc,EAC1D,CAAC,CAAC,CAAC,CAAC;;;;AAKJ,MAAa,QAAQ,iBACnB,UAAU,KAAK,EAAE,aAAa,CAAC;;;;AAKjC,MAAa,UAAU,WAAsB,cAC3C,KAAK,CAAC,GAAG,UAAU,cAAc,SAAS,CAAC;;;;;AAM7C,MAAa,sBAAsB,OAAO,WAAW,WACnD,iBACA;CACA,MAAM,OAAO,OAAO,KAAK;CAEzB,MAAM,EAAE,KAAK,MAAM,QAAQ,KAAK,MAAM,eAAe;CAErD,IAAI,QAAQ,OAAO;EACjB,MAAM,cAAc,MAAM,OACxB,OAAO,MAAM,KAAK,KAAK,GAAG,GAC1B,OAAO,UACT;EACA,OAAO,OAAO,SAAS,MAAM,OAAO,aAAa,IAAI,CAAC;EACtD,OAAO,KAAK,MAAM,OAAO,aAAa,IAAI,CAAC;CAC7C,OACE,OAAO,OAAO,IAAI,yCAAyC,EACzD,MAAM,gBACR,CAAC;AAEL,CAAC;;;;AAKD,MAAa,aAAa,OAAO,WAAW,WAAW,WAAsB;CAG3E,QAAO,OAFa,KAAK,KAAA,CAEb,KAAK,GAAG,UAAU,YAAY,IAAI;AAChD,CAAC;AA0CD,MAAa,YAAY,cACvB,MAAM,KAAK,UAAU,cAAc,GAAG;AAExC,IAAa,2CAAb,cAA8D,OAAO,YAAsD,CAAC,CAC1H,4CACA,EACE,MAAM,OAAO,eACf,CACF,CAAC,CAAC;CACA,IAAa,UAAkB;EAC7B,OAAO,mDAAmD,KAAK;CACjE;AACF"}
@@ -0,0 +1,8 @@
1
+ import { GroupPath } from "./GroupPath.mjs";
2
+ import * as Schema from "effect/Schema";
3
+ //#region src/GroupPaths.ts
4
+ const GroupPaths = Schema.HashSet(GroupPath).pipe(Schema.brand("@confect/cli/GroupPaths"));
5
+ //#endregion
6
+ export { GroupPaths };
7
+
8
+ //# sourceMappingURL=GroupPaths.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"GroupPaths.mjs","names":["GroupPath.GroupPath"],"sources":["../src/GroupPaths.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\";\nimport * as GroupPath from \"./GroupPath.js\";\n\nexport const GroupPaths = Schema.HashSet(GroupPath.GroupPath).pipe(\n Schema.brand(\"@confect/cli/GroupPaths\"),\n);\nexport type GroupPaths = typeof GroupPaths.Type;\n"],"mappings":";;;AAGA,MAAa,aAAa,OAAO,QAAQA,SAAmB,CAAC,CAAC,KAC5D,OAAO,MAAM,yBAAyB,CACxC"}
@@ -0,0 +1,8 @@
1
+ import { withSuffix } from "./RelativeModulePath.mjs";
2
+ import * as Schema from "effect/Schema";
3
+ //#region src/ImplModulePath.ts
4
+ const ImplModulePath = withSuffix(".impl.ts").pipe(Schema.brand("@confect/cli/ImplModulePath"));
5
+ //#endregion
6
+ export { ImplModulePath };
7
+
8
+ //# sourceMappingURL=ImplModulePath.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ImplModulePath.mjs","names":["RelativeModulePath.withSuffix"],"sources":["../src/ImplModulePath.ts"],"sourcesContent":["import * as Schema from \"effect/Schema\";\nimport * as RelativeModulePath from \"./RelativeModulePath.js\";\n\nexport const ImplModulePath = RelativeModulePath.withSuffix(\".impl.ts\").pipe(\n Schema.brand(\"@confect/cli/ImplModulePath\"),\n);\n\nexport type ImplModulePath = typeof ImplModulePath.Type;\n"],"mappings":";;;AAGA,MAAa,iBAAiBA,WAA8B,UAAU,CAAC,CAAC,KACtE,OAAO,MAAM,6BAA6B,CAC5C"}
@@ -0,0 +1,285 @@
1
+ import { fromBundlerError } from "./BuildError.mjs";
2
+ import { bundle, directlyImports, importersOfPackage, withBatch } from "./Bundler.mjs";
3
+ import { ImplMissingDefaultLayerError, ImplMissingFunctionsError, ImplMissingMiddlewareError, ImplMissingSpecImportError, ImplNotFinalizedError, InvalidMiddlewareAttachmentError, MissingImplFileError, MissingSpecFileError, SpecImportsServerError, SpecMissingDefaultGroupSpecError } from "./CodegenError.mjs";
4
+ import { ConfectDirectory } from "./ConfectDirectory.mjs";
5
+ import { removePathExtension, toPosixPath } from "./utils.mjs";
6
+ import { ImplModulePath } from "./ImplModulePath.mjs";
7
+ import * as Effect from "effect/Effect";
8
+ import { GroupSpec, MiddlewareAttachment } from "@confect/core";
9
+ import * as FileSystem from "effect/FileSystem";
10
+ import * as Path from "effect/Path";
11
+ import * as Array from "effect/Array";
12
+ import * as HashSet from "effect/HashSet";
13
+ import * as Option from "effect/Option";
14
+ import * as Ref from "effect/Ref";
15
+ import * as Schema from "effect/Schema";
16
+ import * as String from "effect/String";
17
+ import * as Layer from "effect/Layer";
18
+ import * as Brand from "effect/Brand";
19
+ import { Registry } from "@confect/server";
20
+ import * as GroupImpl from "@confect/server/GroupImpl";
21
+ //#region src/LeafModule.ts
22
+ const LoadedLeafModule = Brand.nominal();
23
+ const VALIDATION_CONCURRENCY = 4;
24
+ const SPEC_SUFFIX = ".spec.ts";
25
+ const IMPL_SUFFIX = ".impl.ts";
26
+ const swapModuleSuffix = Effect.fnUntraced(function* (relativePath, fromSuffix, toSuffix) {
27
+ const path = yield* Path.Path;
28
+ const { dir, name, ext } = path.parse(relativePath);
29
+ if (ext !== ".ts" || !name.endsWith(fromSuffix.slice(0, -3))) return relativePath;
30
+ const nextName = `${name.slice(0, -fromSuffix.slice(0, -3).length)}${toSuffix.slice(0, -3)}`;
31
+ return dir.length > 0 ? path.join(dir, `${nextName}${ext}`) : `${nextName}${ext}`;
32
+ });
33
+ const isLeafSpecPath = (relativePath) => relativePath.endsWith(SPEC_SUFFIX);
34
+ const exportNameFromModulePath = Effect.fnUntraced(function* (relativePath) {
35
+ const { name, ext } = (yield* Path.Path).parse(relativePath);
36
+ if (ext !== ".ts") return name;
37
+ return name.endsWith(".spec") ? name.slice(0, -5) : name;
38
+ });
39
+ const groupPathFromRelativeModulePath = Effect.fnUntraced(function* (relativePath) {
40
+ const path = yield* Path.Path;
41
+ const { dir, name, ext } = path.parse(relativePath);
42
+ const stem = ext === ".ts" && name.endsWith(".spec") ? name.slice(0, -5) : name;
43
+ const dirSegments = Array.filter(String.split(dir, path.sep), String.isNonEmpty);
44
+ const pathSegments = Array.append(dirSegments, stem);
45
+ return {
46
+ pathSegments,
47
+ groupPathDot: Array.join(pathSegments, ".")
48
+ };
49
+ });
50
+ const specImportPathFromGenerated = Effect.fnUntraced(function* (specRelativePath) {
51
+ const path = yield* Path.Path;
52
+ return `../${toPosixPath(path, yield* removePathExtension(specRelativePath))}.js`;
53
+ });
54
+ const specPathForImpl = (implRelativePath) => swapModuleSuffix(implRelativePath, IMPL_SUFFIX, SPEC_SUFFIX);
55
+ const implPathForSpec = (specRelativePath) => swapModuleSuffix(specRelativePath, SPEC_SUFFIX, IMPL_SUFFIX);
56
+ const registeredFunctionsRelativePath = Effect.fnUntraced(function* (leaf) {
57
+ return (yield* Path.Path).join("registeredFunctions", ...leaf.pathSegments) + ".ts";
58
+ });
59
+ const discoverLeafSpecFiles = Effect.gen(function* () {
60
+ const fs = yield* FileSystem.FileSystem;
61
+ const path = yield* Path.Path;
62
+ const confectDirectory = yield* ConfectDirectory.get;
63
+ const excludedDirs = HashSet.make("_generated", "tables", "middleware");
64
+ const excludedFiles = HashSet.make("nodeSpec.ts", "spec.ts");
65
+ const allPaths = yield* fs.readDirectory(confectDirectory, { recursive: true });
66
+ return Array.filter(allPaths, (relativePath) => {
67
+ if (!isLeafSpecPath(relativePath)) return false;
68
+ if (HashSet.has(excludedFiles, relativePath)) return false;
69
+ const segments = String.split(relativePath, path.sep);
70
+ return !Array.some(segments, (segment) => HashSet.has(excludedDirs, segment));
71
+ });
72
+ });
73
+ const discoverLeafImplFiles = Effect.gen(function* () {
74
+ const fs = yield* FileSystem.FileSystem;
75
+ const path = yield* Path.Path;
76
+ const confectDirectory = yield* ConfectDirectory.get;
77
+ const excludedDirs = HashSet.make("_generated", "tables", "middleware");
78
+ const allPaths = yield* fs.readDirectory(confectDirectory, { recursive: true });
79
+ return Array.filter(Array.filterMap(allPaths, (relativePath) => Schema.decodeResult(ImplModulePath)(relativePath)), (relativePath) => {
80
+ const segments = String.split(relativePath, path.sep);
81
+ return !Array.some(segments, (segment) => HashSet.has(excludedDirs, segment));
82
+ });
83
+ });
84
+ const toLeafModule = Effect.fnUntraced(function* (specRelativePath) {
85
+ const exportName = yield* exportNameFromModulePath(specRelativePath);
86
+ const { pathSegments, groupPathDot } = yield* groupPathFromRelativeModulePath(specRelativePath);
87
+ return {
88
+ relativePath: specRelativePath,
89
+ pathSegments,
90
+ groupPathDot,
91
+ exportName,
92
+ specImportPath: yield* specImportPathFromGenerated(specRelativePath)
93
+ };
94
+ });
95
+ const absoluteModulePath = Effect.fnUntraced(function* (relativePath) {
96
+ const confectDirectory = yield* ConfectDirectory.get;
97
+ return (yield* Path.Path).resolve(confectDirectory, relativePath);
98
+ });
99
+ /**
100
+ * Every `*.spec.ts` is reachable from `_generated/spec.ts`, which the client
101
+ * imports through `_generated/refs.ts`—so a spec's whole import graph is
102
+ * bundled into the browser whether or not the client calls those functions.
103
+ * Server logic co-located with a declaration therefore ships to users,
104
+ * silently.
105
+ *
106
+ * `@confect/server` is a sound proxy for "server logic lives here": an
107
+ * implementation can't be written without `FunctionImpl`/`MiddlewareImpl`, both
108
+ * of which live there. Table `Doc`/`Fields` schemas live in `@confect/core`, so
109
+ * the documented `notes.Doc` pattern in a spec must not reach `@confect/server`
110
+ * even through `tables/` or `_generated/`. The check runs over the spec
111
+ * bundle's transitive inputs rather than the spec module alone, so it also
112
+ * covers middleware declarations under `confect/middleware/` (which codegen
113
+ * otherwise never visits) and any shared helper a spec pulls in—while only ever
114
+ * flagging modules that genuinely reach the client.
115
+ */
116
+ const validateClientSafety = Effect.fnUntraced(function* (leaf, bundled) {
117
+ const path = yield* Path.Path;
118
+ const confectDirectory = path.resolve(yield* ConfectDirectory.get);
119
+ const isCheckedUserModule = (absolutePath) => {
120
+ const relative = path.relative(confectDirectory, absolutePath);
121
+ return !relative.startsWith("..") && !path.isAbsolute(relative);
122
+ };
123
+ const importers = importersOfPackage(bundled, "@confect/server", isCheckedUserModule);
124
+ if (importers.length > 0) return yield* new SpecImportsServerError({
125
+ specPath: leaf.relativePath,
126
+ importerPaths: Array.map(importers, (absolutePath) => toPosixPath(path, path.relative(confectDirectory, absolutePath)))
127
+ });
128
+ });
129
+ /**
130
+ * Validate that the leaf's spec file default-exports a `GroupSpec`, and that
131
+ * nothing it reaches drags server code into the client (see
132
+ * {@link validateClientSafety}). Returns the leaf paired with its validated
133
+ * spec, so subsequent operations reuse that association without re-bundling.
134
+ * The runtime is declared by the spec, not its file location.
135
+ */
136
+ const loadSpec = Effect.fn("LeafModule.loadSpec")(function* (leaf) {
137
+ const absolutePath = yield* absoluteModulePath(leaf.relativePath);
138
+ const bundled = yield* bundle(absolutePath).pipe(Effect.mapError((error) => fromBundlerError(leaf.relativePath, error)));
139
+ const groupSpec = bundled.module.default;
140
+ if (!GroupSpec.isGroupSpec(groupSpec)) return yield* new SpecMissingDefaultGroupSpecError({ specPath: leaf.relativePath });
141
+ yield* validateClientSafety(leaf, bundled);
142
+ yield* Effect.fromResult(GroupSpec.validateMiddleware(groupSpec)).pipe(Effect.mapError((error) => new InvalidMiddlewareAttachmentError({
143
+ specPath: leaf.relativePath,
144
+ message: MiddlewareAttachment.formatValidationError(error)
145
+ })));
146
+ return LoadedLeafModule({
147
+ ...leaf,
148
+ spec: groupSpec
149
+ });
150
+ });
151
+ /**
152
+ * Walk the built `Context` for a `Finalized` `GroupImpl` service value. The
153
+ * lookup is value-shaped (via `GroupImpl.isFinalizedGroupImpl`) so we don't
154
+ * need to know the group's path up front to construct a typed tag for it.
155
+ */
156
+ const findFinalizedGroupImpl = (context) => Array.findFirst(context.mapUnsafe.values(), GroupImpl.isFinalizedGroupImpl);
157
+ /**
158
+ * Build the impl layer with a fresh `Registry` so each validation is isolated
159
+ * from prior validations' `FunctionImpl.make` writes. The CLI no longer reads
160
+ * the registry directly—`GroupImpl.finalize` snapshots the registered function
161
+ * names onto the produced `Finalized` `GroupImpl` service value—but a fresh
162
+ * `Ref` is still required because the default `Context.Reference` is cached
163
+ * globally and would otherwise accumulate items across impls.
164
+ */
165
+ const buildImplLayer = Effect.fnUntraced(function* (implLayer) {
166
+ const registry = Ref.makeUnsafe({});
167
+ return yield* Layer.build(implLayer).pipe(Effect.provideService(Registry.Registry, registry));
168
+ }, Effect.scoped);
169
+ /**
170
+ * Validate that the leaf's sibling impl file imports the spec, default-exports
171
+ * a finalized `GroupImpl` layer, and provides a `FunctionImpl` for every
172
+ * function declared by the spec.
173
+ */
174
+ const validateImpl = Effect.fn("LeafModule.validateImpl")(function* (leaf) {
175
+ const implRelativePath = yield* implPathForSpec(leaf.relativePath);
176
+ const implAbsolutePath = yield* absoluteModulePath(implRelativePath);
177
+ const specAbsolutePath = yield* absoluteModulePath(leaf.relativePath);
178
+ const bundled = yield* bundle(implAbsolutePath).pipe(Effect.mapError((error) => fromBundlerError(implRelativePath, error)));
179
+ if (!(yield* directlyImports(bundled, implAbsolutePath, specAbsolutePath))) return yield* new ImplMissingSpecImportError({
180
+ implPath: implRelativePath,
181
+ expectedSpecPath: leaf.relativePath
182
+ });
183
+ if (!Layer.isLayer(bundled.module.default)) return yield* new ImplMissingDefaultLayerError({ implPath: implRelativePath });
184
+ const groupSpec = leaf.spec;
185
+ const expectedFunctionNames = Object.keys(groupSpec.functions);
186
+ const context = yield* buildImplLayer(bundled.module.default);
187
+ const finalizedGroupImpl = yield* Option.match(findFinalizedGroupImpl(context), {
188
+ onNone: () => new ImplNotFinalizedError({ implPath: implRelativePath }),
189
+ onSome: Effect.succeed
190
+ });
191
+ const registeredSet = HashSet.fromIterable(finalizedGroupImpl.registeredFunctionNames);
192
+ const missing = expectedFunctionNames.filter((name) => !HashSet.has(registeredSet, name));
193
+ if (missing.length > 0) return yield* new ImplMissingFunctionsError({
194
+ implPath: implRelativePath,
195
+ groupPath: leaf.groupPathDot,
196
+ missingFunctionNames: missing
197
+ });
198
+ const expectedMiddlewareKeys = Array.dedupe([...(groupSpec.middlewareSpecs ?? []).map((middlewareSpec) => middlewareSpec.key), ...Object.values(groupSpec.functions).flatMap((function_) => (function_.middlewareSpecs ?? []).map((middlewareSpec) => middlewareSpec.key))]);
199
+ const registeredMiddlewareKeys = HashSet.fromIterable(finalizedGroupImpl.registeredMiddlewareKeys ?? []);
200
+ const missingMiddleware = Array.filter(expectedMiddlewareKeys, (key) => !HashSet.has(registeredMiddlewareKeys, key));
201
+ if (missingMiddleware.length > 0) return yield* new ImplMissingMiddlewareError({
202
+ implPath: implRelativePath,
203
+ groupPath: leaf.groupPathDot,
204
+ missingMiddlewareKeys: missingMiddleware
205
+ });
206
+ });
207
+ const withImplFiles = Effect.fnUntraced(function* (relativePaths, use) {
208
+ const fs = yield* FileSystem.FileSystem;
209
+ const path = yield* Path.Path;
210
+ const confectDirectory = yield* ConfectDirectory.get;
211
+ const pairs = yield* Effect.forEach(relativePaths, (implRelativePath) => specPathForImpl(implRelativePath).pipe(Effect.map((specRelativePath) => ({
212
+ implRelativePath,
213
+ specRelativePath
214
+ }))));
215
+ return yield* withBatch(pairs.flatMap(({ implRelativePath, specRelativePath }) => [path.resolve(confectDirectory, implRelativePath), path.resolve(confectDirectory, specRelativePath)]), Effect.gen(function* () {
216
+ return yield* use(yield* Effect.forEach(pairs, Effect.fnUntraced(function* ({ implRelativePath, specRelativePath }) {
217
+ if (!(yield* fs.exists(path.join(confectDirectory, specRelativePath)))) return yield* new MissingSpecFileError({
218
+ implPath: implRelativePath,
219
+ expectedSpecPath: specRelativePath
220
+ });
221
+ if (!(yield* fs.exists(path.join(confectDirectory, implRelativePath)))) return yield* new MissingImplFileError({
222
+ specPath: specRelativePath,
223
+ expectedImplPath: implRelativePath
224
+ });
225
+ return yield* loadSpec(yield* toLeafModule(specRelativePath));
226
+ }), { concurrency: VALIDATION_CONCURRENCY }));
227
+ }));
228
+ });
229
+ const validateImplFiles = Effect.fn("LeafModule.validateImplFiles")((relativePaths) => withImplFiles(relativePaths, validateAllImpls));
230
+ const validateAllImpls = Effect.fnUntraced(function* (leaves) {
231
+ const path = yield* Path.Path;
232
+ const confectDirectory = yield* ConfectDirectory.get;
233
+ const implementationPaths = yield* Effect.forEach(leaves, ({ relativePath }) => implPathForSpec(relativePath).pipe(Effect.map((implPath) => path.resolve(confectDirectory, implPath))));
234
+ yield* withBatch(implementationPaths, Effect.forEach(leaves, validateImpl, {
235
+ concurrency: VALIDATION_CONCURRENCY,
236
+ discard: true
237
+ }));
238
+ });
239
+ const loadAll = Effect.gen(function* () {
240
+ const fs = yield* FileSystem.FileSystem;
241
+ const path = yield* Path.Path;
242
+ const confectDirectory = yield* ConfectDirectory.get;
243
+ const specFiles = yield* discoverLeafSpecFiles;
244
+ const results = yield* withBatch(specFiles.map((relativePath) => path.resolve(confectDirectory, relativePath)), Effect.forEach(specFiles, Effect.fnUntraced(function* (specRelativePath) {
245
+ const discovered = yield* toLeafModule(specRelativePath);
246
+ const leaf = yield* loadSpec(discovered);
247
+ const implRelativePath = yield* implPathForSpec(specRelativePath);
248
+ const implAbsolutePath = path.join(confectDirectory, implRelativePath);
249
+ if (!(yield* fs.exists(implAbsolutePath))) return yield* new MissingImplFileError({
250
+ specPath: specRelativePath,
251
+ expectedImplPath: implRelativePath
252
+ });
253
+ return leaf;
254
+ }), { concurrency: VALIDATION_CONCURRENCY }));
255
+ yield* validateOrphanImpls(specFiles);
256
+ return results;
257
+ });
258
+ /**
259
+ * Walk leaf ancestors and fail with a {@link ParentChildNameCollisionError} when
260
+ * a parent leaf declares a function or subgroup whose name matches a sibling
261
+ * subdirectory spec's segment. Without this check the colliding descendant
262
+ * would overwrite the parent's entry in the assembled `GroupSpec.groups` map at
263
+ * runtime, surfacing as a confusing `Refs.make` error rather than a
264
+ * codegen-time diagnostic.
265
+ */
266
+ const validateOrphanImpls = Effect.fnUntraced(function* (specFiles) {
267
+ const fs = yield* FileSystem.FileSystem;
268
+ const path = yield* Path.Path;
269
+ const confectDirectory = yield* ConfectDirectory.get;
270
+ const implFiles = yield* discoverLeafImplFiles;
271
+ const specPaths = HashSet.fromIterable(specFiles);
272
+ yield* Effect.forEach(implFiles, Effect.fnUntraced(function* (implRelativePath) {
273
+ const specRelativePath = yield* specPathForImpl(implRelativePath);
274
+ if (HashSet.has(specPaths, specRelativePath)) return;
275
+ const specAbsolutePath = path.join(confectDirectory, specRelativePath);
276
+ if (!(yield* fs.exists(specAbsolutePath))) return yield* new MissingSpecFileError({
277
+ implPath: implRelativePath,
278
+ expectedSpecPath: specRelativePath
279
+ });
280
+ }));
281
+ });
282
+ //#endregion
283
+ export { IMPL_SUFFIX, SPEC_SUFFIX, discoverLeafImplFiles, discoverLeafSpecFiles, exportNameFromModulePath, groupPathFromRelativeModulePath, implPathForSpec, isLeafSpecPath, loadAll, loadSpec, registeredFunctionsRelativePath, specImportPathFromGenerated, specPathForImpl, toLeafModule, validateAllImpls, validateImpl, validateImplFiles, withImplFiles };
284
+
285
+ //# sourceMappingURL=LeafModule.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"LeafModule.mjs","names":["Bundler.importersOfPackage","Bundler.bundle","Bundler.directlyImports","Bundler.withBatch"],"sources":["../src/LeafModule.ts"],"sourcesContent":["import { GroupSpec, MiddlewareAttachment } from \"@confect/core\";\nimport { Registry, type RegistryItems } from \"@confect/server\";\nimport * as GroupImpl from \"@confect/server/GroupImpl\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Path from \"effect/Path\";\nimport type { Context } from \"effect\";\nimport * as Array from \"effect/Array\";\nimport * as Brand from \"effect/Brand\";\nimport * as Effect from \"effect/Effect\";\nimport * as HashSet from \"effect/HashSet\";\nimport * as Layer from \"effect/Layer\";\nimport * as Option from \"effect/Option\";\nimport * as Ref from \"effect/Ref\";\nimport * as Schema from \"effect/Schema\";\nimport * as String from \"effect/String\";\nimport { fromBundlerError } from \"./BuildError.js\";\nimport * as Bundler from \"./Bundler.js\";\nimport {\n MissingImplFileError,\n MissingSpecFileError,\n ImplMissingDefaultLayerError,\n ImplMissingFunctionsError,\n ImplMissingMiddlewareError,\n ImplMissingSpecImportError,\n ImplNotFinalizedError,\n InvalidMiddlewareAttachmentError,\n SpecImportsServerError,\n SpecMissingDefaultGroupSpecError,\n} from \"./CodegenError.js\";\nimport { ConfectDirectory } from \"./ConfectDirectory.js\";\nimport { ImplModulePath } from \"./ImplModulePath.js\";\nimport { removePathExtension, toPosixPath } from \"./utils.js\";\n\nexport interface LeafModule {\n readonly relativePath: string;\n readonly pathSegments: readonly [string, ...string[]];\n readonly groupPathDot: string;\n readonly exportName: string;\n readonly specImportPath: string;\n}\n\nexport type LoadedLeafModule = LeafModule & {\n readonly spec: GroupSpec.AnyWithProps;\n} & Brand.Brand<\"@confect/cli/LoadedLeafModule\">;\n\nconst LoadedLeafModule = Brand.nominal<LoadedLeafModule>();\n\nconst VALIDATION_CONCURRENCY = 4;\n\nexport const SPEC_SUFFIX = \".spec.ts\";\nexport const IMPL_SUFFIX = \".impl.ts\";\n\nconst swapModuleSuffix = Effect.fnUntraced(function* (\n relativePath: string,\n fromSuffix: string,\n toSuffix: string,\n) {\n const path = yield* Path.Path;\n const { dir, name, ext } = path.parse(relativePath);\n if (ext !== \".ts\" || !name.endsWith(fromSuffix.slice(0, -\".ts\".length))) {\n return relativePath;\n }\n\n const stem = name.slice(0, -fromSuffix.slice(0, -\".ts\".length).length);\n const nextName = `${stem}${toSuffix.slice(0, -\".ts\".length)}`;\n return dir.length > 0\n ? path.join(dir, `${nextName}${ext}`)\n : `${nextName}${ext}`;\n});\n\nexport const isLeafSpecPath = (relativePath: string) =>\n relativePath.endsWith(SPEC_SUFFIX);\n\nexport const exportNameFromModulePath = Effect.fnUntraced(function* (\n relativePath: string,\n) {\n const path = yield* Path.Path;\n const { name, ext } = path.parse(relativePath);\n if (ext !== \".ts\") {\n return name;\n }\n return name.endsWith(\".spec\") ? name.slice(0, -\".spec\".length) : name;\n});\n\nexport const groupPathFromRelativeModulePath = Effect.fnUntraced(function* (\n relativePath: string,\n) {\n const path = yield* Path.Path;\n const { dir, name, ext } = path.parse(relativePath);\n const stem =\n ext === \".ts\" && name.endsWith(\".spec\")\n ? name.slice(0, -\".spec\".length)\n : name;\n const dirSegments = Array.filter(\n String.split(dir, path.sep),\n String.isNonEmpty,\n );\n const pathSegments = Array.append(dirSegments, stem) as [string, ...string[]];\n return {\n pathSegments,\n groupPathDot: Array.join(pathSegments, \".\"),\n };\n});\n\nexport const specImportPathFromGenerated = Effect.fnUntraced(function* (\n specRelativePath: string,\n) {\n const path = yield* Path.Path;\n const withoutExt = toPosixPath(\n path,\n yield* removePathExtension(specRelativePath),\n );\n return `../${withoutExt}.js`;\n});\n\nexport const specPathForImpl = (implRelativePath: string) =>\n swapModuleSuffix(implRelativePath, IMPL_SUFFIX, SPEC_SUFFIX);\n\nexport const implPathForSpec = (specRelativePath: string) =>\n swapModuleSuffix(specRelativePath, SPEC_SUFFIX, IMPL_SUFFIX);\n\nexport const registeredFunctionsRelativePath = Effect.fnUntraced(function* (\n leaf: LeafModule,\n) {\n const path = yield* Path.Path;\n return path.join(\"registeredFunctions\", ...leaf.pathSegments) + \".ts\";\n});\n\nexport const discoverLeafSpecFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const excludedDirs = HashSet.make(\"_generated\", \"tables\", \"middleware\");\n const excludedFiles = HashSet.make(\"nodeSpec.ts\", \"spec.ts\");\n\n const allPaths = yield* fs.readDirectory(confectDirectory, {\n recursive: true,\n });\n\n return Array.filter(allPaths, (relativePath) => {\n if (!isLeafSpecPath(relativePath)) {\n return false;\n }\n\n if (HashSet.has(excludedFiles, relativePath)) {\n return false;\n }\n\n const segments = String.split(relativePath, path.sep);\n return !Array.some(segments, (segment) =>\n HashSet.has(excludedDirs, segment),\n );\n });\n});\n\nexport const discoverLeafImplFiles = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const excludedDirs = HashSet.make(\"_generated\", \"tables\", \"middleware\");\n\n const allPaths = yield* fs.readDirectory(confectDirectory, {\n recursive: true,\n });\n\n return Array.filter(\n Array.filterMap(allPaths, (relativePath) =>\n Schema.decodeResult(ImplModulePath)(relativePath),\n ),\n (relativePath) => {\n const segments = String.split(relativePath, path.sep);\n return !Array.some(segments, (segment) =>\n HashSet.has(excludedDirs, segment),\n );\n },\n );\n});\n\nexport const toLeafModule = Effect.fnUntraced(function* (\n specRelativePath: string,\n) {\n const exportName = yield* exportNameFromModulePath(specRelativePath);\n const { pathSegments, groupPathDot } =\n yield* groupPathFromRelativeModulePath(specRelativePath);\n const specImportPath = yield* specImportPathFromGenerated(specRelativePath);\n\n return {\n relativePath: specRelativePath,\n pathSegments,\n groupPathDot,\n exportName,\n specImportPath,\n } satisfies LeafModule;\n});\n\nconst absoluteModulePath = Effect.fnUntraced(function* (relativePath: string) {\n const confectDirectory = yield* ConfectDirectory.get;\n const path = yield* Path.Path;\n return path.resolve(confectDirectory, relativePath);\n});\n\n/**\n * Every `*.spec.ts` is reachable from `_generated/spec.ts`, which the client\n * imports through `_generated/refs.ts`—so a spec's whole import graph is\n * bundled into the browser whether or not the client calls those functions.\n * Server logic co-located with a declaration therefore ships to users,\n * silently.\n *\n * `@confect/server` is a sound proxy for \"server logic lives here\": an\n * implementation can't be written without `FunctionImpl`/`MiddlewareImpl`, both\n * of which live there. Table `Doc`/`Fields` schemas live in `@confect/core`, so\n * the documented `notes.Doc` pattern in a spec must not reach `@confect/server`\n * even through `tables/` or `_generated/`. The check runs over the spec\n * bundle's transitive inputs rather than the spec module alone, so it also\n * covers middleware declarations under `confect/middleware/` (which codegen\n * otherwise never visits) and any shared helper a spec pulls in—while only ever\n * flagging modules that genuinely reach the client.\n */\nconst validateClientSafety = Effect.fnUntraced(function* (\n leaf: LeafModule,\n bundled: Bundler.Bundled,\n) {\n const path = yield* Path.Path;\n const confectDirectory = path.resolve(yield* ConfectDirectory.get);\n\n const isCheckedUserModule = (absolutePath: string) => {\n const relative = path.relative(confectDirectory, absolutePath);\n return !relative.startsWith(\"..\") && !path.isAbsolute(relative);\n };\n\n const importers = Bundler.importersOfPackage(\n bundled,\n \"@confect/server\",\n isCheckedUserModule,\n );\n\n if (importers.length > 0) {\n return yield* new SpecImportsServerError({\n specPath: leaf.relativePath,\n importerPaths: Array.map(importers, (absolutePath) =>\n toPosixPath(path, path.relative(confectDirectory, absolutePath)),\n ),\n });\n }\n});\n\n/**\n * Validate that the leaf's spec file default-exports a `GroupSpec`, and that\n * nothing it reaches drags server code into the client (see\n * {@link validateClientSafety}). Returns the leaf paired with its validated\n * spec, so subsequent operations reuse that association without re-bundling.\n * The runtime is declared by the spec, not its file location.\n */\nexport const loadSpec = Effect.fn(\"LeafModule.loadSpec\")(function* (\n leaf: LeafModule,\n) {\n const absolutePath = yield* absoluteModulePath(leaf.relativePath);\n const bundled = yield* Bundler.bundle(absolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(leaf.relativePath, error)),\n );\n\n const groupSpec = bundled.module.default;\n\n if (!GroupSpec.isGroupSpec(groupSpec)) {\n return yield* new SpecMissingDefaultGroupSpecError({\n specPath: leaf.relativePath,\n });\n }\n\n yield* validateClientSafety(leaf, bundled);\n\n yield* Effect.fromResult(GroupSpec.validateMiddleware(groupSpec)).pipe(\n Effect.mapError(\n (error) =>\n new InvalidMiddlewareAttachmentError({\n specPath: leaf.relativePath,\n message: MiddlewareAttachment.formatValidationError(error),\n }),\n ),\n );\n\n return LoadedLeafModule({\n ...leaf,\n spec: groupSpec,\n });\n});\n\n/**\n * Walk the built `Context` for a `Finalized` `GroupImpl` service value. The\n * lookup is value-shaped (via `GroupImpl.isFinalizedGroupImpl`) so we don't\n * need to know the group's path up front to construct a typed tag for it.\n */\nconst findFinalizedGroupImpl = <S>(\n context: Context.Context<S>,\n): Option.Option<GroupImpl.AnyFinalized> =>\n Array.findFirst(context.mapUnsafe.values(), GroupImpl.isFinalizedGroupImpl);\n\n/**\n * Build the impl layer with a fresh `Registry` so each validation is isolated\n * from prior validations' `FunctionImpl.make` writes. The CLI no longer reads\n * the registry directly—`GroupImpl.finalize` snapshots the registered function\n * names onto the produced `Finalized` `GroupImpl` service value—but a fresh\n * `Ref` is still required because the default `Context.Reference` is cached\n * globally and would otherwise accumulate items across impls.\n */\nconst buildImplLayer = Effect.fnUntraced(function* (\n implLayer: Layer.Layer<unknown>,\n) {\n const registry = Ref.makeUnsafe<RegistryItems.RegistryItems>({});\n return yield* Layer.build(\n implLayer as Layer.Layer<unknown, never, never>,\n ).pipe(Effect.provideService(Registry.Registry, registry));\n}, Effect.scoped);\n\n/**\n * Validate that the leaf's sibling impl file imports the spec, default-exports\n * a finalized `GroupImpl` layer, and provides a `FunctionImpl` for every\n * function declared by the spec.\n */\nexport const validateImpl = Effect.fn(\"LeafModule.validateImpl\")(function* (\n leaf: LoadedLeafModule,\n) {\n const implRelativePath = yield* implPathForSpec(leaf.relativePath);\n const implAbsolutePath = yield* absoluteModulePath(implRelativePath);\n const specAbsolutePath = yield* absoluteModulePath(leaf.relativePath);\n\n const bundled = yield* Bundler.bundle(implAbsolutePath).pipe(\n Effect.mapError((error) => fromBundlerError(implRelativePath, error)),\n );\n\n if (\n !(yield* Bundler.directlyImports(\n bundled,\n implAbsolutePath,\n specAbsolutePath,\n ))\n ) {\n return yield* new ImplMissingSpecImportError({\n implPath: implRelativePath,\n expectedSpecPath: leaf.relativePath,\n });\n }\n\n if (!Layer.isLayer(bundled.module.default)) {\n return yield* new ImplMissingDefaultLayerError({\n implPath: implRelativePath,\n });\n }\n\n const groupSpec = leaf.spec;\n const expectedFunctionNames = Object.keys(groupSpec.functions);\n\n const context = yield* buildImplLayer(\n bundled.module.default as Layer.Layer<unknown>,\n );\n const finalizedGroupImpl = yield* Option.match(\n findFinalizedGroupImpl(context),\n {\n onNone: () => new ImplNotFinalizedError({ implPath: implRelativePath }),\n onSome: Effect.succeed,\n },\n );\n\n const registeredSet = HashSet.fromIterable(\n finalizedGroupImpl.registeredFunctionNames,\n );\n const missing = expectedFunctionNames.filter(\n (name) => !HashSet.has(registeredSet, name),\n );\n\n if (missing.length > 0) {\n return yield* new ImplMissingFunctionsError({\n implPath: implRelativePath,\n groupPath: leaf.groupPathDot,\n missingFunctionNames: missing,\n });\n }\n\n const expectedMiddlewareKeys = Array.dedupe([\n ...(groupSpec.middlewareSpecs ?? []).map(\n (middlewareSpec) => middlewareSpec.key,\n ),\n ...Object.values(groupSpec.functions).flatMap((function_) =>\n (function_.middlewareSpecs ?? []).map(\n (middlewareSpec) => middlewareSpec.key,\n ),\n ),\n ]);\n const registeredMiddlewareKeys = HashSet.fromIterable(\n finalizedGroupImpl.registeredMiddlewareKeys ?? [],\n );\n const missingMiddleware = Array.filter(\n expectedMiddlewareKeys,\n (key) => !HashSet.has(registeredMiddlewareKeys, key),\n );\n\n if (missingMiddleware.length > 0) {\n return yield* new ImplMissingMiddlewareError({\n implPath: implRelativePath,\n groupPath: leaf.groupPathDot,\n missingMiddlewareKeys: missingMiddleware,\n });\n }\n});\n\nexport const withImplFiles = Effect.fnUntraced(function* <A, E, R>(\n relativePaths: ReadonlyArray<ImplModulePath>,\n use: (leaves: ReadonlyArray<LoadedLeafModule>) => Effect.Effect<A, E, R>,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const pairs = yield* Effect.forEach(relativePaths, (implRelativePath) =>\n specPathForImpl(implRelativePath).pipe(\n Effect.map((specRelativePath) => ({\n implRelativePath,\n specRelativePath,\n })),\n ),\n );\n\n return yield* Bundler.withBatch(\n pairs.flatMap(({ implRelativePath, specRelativePath }) => [\n path.resolve(confectDirectory, implRelativePath),\n path.resolve(confectDirectory, specRelativePath),\n ]),\n Effect.gen(function* () {\n const leaves = yield* Effect.forEach(\n pairs,\n Effect.fnUntraced(function* ({ implRelativePath, specRelativePath }) {\n if (\n !(yield* fs.exists(path.join(confectDirectory, specRelativePath)))\n ) {\n return yield* new MissingSpecFileError({\n implPath: implRelativePath,\n expectedSpecPath: specRelativePath,\n });\n }\n if (\n !(yield* fs.exists(path.join(confectDirectory, implRelativePath)))\n ) {\n return yield* new MissingImplFileError({\n specPath: specRelativePath,\n expectedImplPath: implRelativePath,\n });\n }\n const leaf = yield* loadSpec(yield* toLeafModule(specRelativePath));\n return leaf;\n }),\n { concurrency: VALIDATION_CONCURRENCY },\n );\n return yield* use(leaves);\n }),\n );\n});\n\nexport const validateImplFiles = Effect.fn(\"LeafModule.validateImplFiles\")(\n (relativePaths: ReadonlyArray<ImplModulePath>) =>\n withImplFiles(relativePaths, validateAllImpls),\n);\n\nexport const validateAllImpls = Effect.fnUntraced(function* (\n leaves: ReadonlyArray<LoadedLeafModule>,\n) {\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const implementationPaths = yield* Effect.forEach(\n leaves,\n ({ relativePath }) =>\n implPathForSpec(relativePath).pipe(\n Effect.map((implPath) => path.resolve(confectDirectory, implPath)),\n ),\n );\n yield* Bundler.withBatch(\n implementationPaths,\n Effect.forEach(leaves, validateImpl, {\n concurrency: VALIDATION_CONCURRENCY,\n discard: true,\n }),\n );\n});\n\nexport const loadAll = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const specFiles = yield* discoverLeafSpecFiles;\n\n const results = yield* Bundler.withBatch(\n specFiles.map((relativePath) =>\n path.resolve(confectDirectory, relativePath),\n ),\n Effect.forEach(\n specFiles,\n Effect.fnUntraced(function* (specRelativePath: string) {\n const discovered = yield* toLeafModule(specRelativePath);\n const leaf = yield* loadSpec(discovered);\n\n const implRelativePath = yield* implPathForSpec(specRelativePath);\n const implAbsolutePath = path.join(confectDirectory, implRelativePath);\n if (!(yield* fs.exists(implAbsolutePath))) {\n return yield* new MissingImplFileError({\n specPath: specRelativePath,\n expectedImplPath: implRelativePath,\n });\n }\n\n return leaf;\n }),\n { concurrency: VALIDATION_CONCURRENCY },\n ),\n );\n\n yield* validateOrphanImpls(specFiles);\n\n return results;\n});\n\n/**\n * Walk leaf ancestors and fail with a {@link ParentChildNameCollisionError} when\n * a parent leaf declares a function or subgroup whose name matches a sibling\n * subdirectory spec's segment. Without this check the colliding descendant\n * would overwrite the parent's entry in the assembled `GroupSpec.groups` map at\n * runtime, surfacing as a confusing `Refs.make` error rather than a\n * codegen-time diagnostic.\n */\nconst validateOrphanImpls = Effect.fnUntraced(function* (\n specFiles: ReadonlyArray<string>,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n const implFiles = yield* discoverLeafImplFiles;\n const specPaths = HashSet.fromIterable(specFiles);\n\n yield* Effect.forEach(\n implFiles,\n Effect.fnUntraced(function* (implRelativePath: string) {\n const specRelativePath = yield* specPathForImpl(implRelativePath);\n if (HashSet.has(specPaths, specRelativePath)) {\n return;\n }\n\n const specAbsolutePath = path.join(confectDirectory, specRelativePath);\n if (!(yield* fs.exists(specAbsolutePath))) {\n return yield* new MissingSpecFileError({\n implPath: implRelativePath,\n expectedSpecPath: specRelativePath,\n });\n }\n }),\n );\n});\n"],"mappings":"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"}
@@ -0,0 +1,38 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as FileSystem from "effect/FileSystem";
3
+ import * as Path from "effect/Path";
4
+ import * as Array from "effect/Array";
5
+ import * as Option from "effect/Option";
6
+ import * as Context from "effect/Context";
7
+ import * as Ref from "effect/Ref";
8
+ import * as Schema from "effect/Schema";
9
+ import * as NodeFileSystem from "@effect/platform-node/NodeFileSystem";
10
+ import * as Layer from "effect/Layer";
11
+ //#region src/ProjectRoot.ts
12
+ var ProjectRoot = class ProjectRoot extends Context.Service()("@confect/cli/ProjectRoot") {
13
+ static get = ProjectRoot.use((service) => service.get);
14
+ };
15
+ const findProjectRootFrom = Effect.fnUntraced(function* (directory) {
16
+ const fs = yield* FileSystem.FileSystem;
17
+ const path = yield* Path.Path;
18
+ const startDir = path.resolve(directory);
19
+ const root = path.parse(startDir).root;
20
+ const directories = Array.unfold(startDir, (dir) => dir === root ? Option.none() : Option.some([dir, path.dirname(dir)]));
21
+ const projectRoot = yield* Effect.findFirst(directories, (dir) => fs.exists(path.join(dir, "package.json")));
22
+ return yield* Effect.fromOption(projectRoot, () => new ProjectRootNotFoundError());
23
+ });
24
+ const findProjectRoot = findProjectRootFrom(".");
25
+ const layer = Layer.effect(ProjectRoot, Effect.gen(function* () {
26
+ const projectRoot = yield* findProjectRoot;
27
+ const ref = yield* Ref.make(projectRoot);
28
+ return { get: Ref.get(ref) };
29
+ })).pipe(Layer.provide(NodeFileSystem.layer));
30
+ var ProjectRootNotFoundError = class extends Schema.TaggedError()("ProjectRootNotFoundError", {}) {
31
+ get message() {
32
+ return "Could not find project root (no 'package.json' found)";
33
+ }
34
+ };
35
+ //#endregion
36
+ export { ProjectRoot, ProjectRootNotFoundError, findProjectRoot, findProjectRootFrom, layer };
37
+
38
+ //# sourceMappingURL=ProjectRoot.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectRoot.mjs","names":[],"sources":["../src/ProjectRoot.ts"],"sourcesContent":["import * as NodeFileSystem from \"@effect/platform-node/NodeFileSystem\";\nimport * as Array from \"effect/Array\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Layer from \"effect/Layer\";\nimport * as Option from \"effect/Option\";\nimport * as Path from \"effect/Path\";\nimport * as Ref from \"effect/Ref\";\nimport * as Schema from \"effect/Schema\";\n\nexport class ProjectRoot extends Context.Service<\n ProjectRoot,\n { readonly get: Effect.Effect<string> }\n>()(\"@confect/cli/ProjectRoot\") {\n static readonly get = ProjectRoot.use((service) => service.get);\n}\n\nexport const findProjectRootFrom = Effect.fnUntraced(function* (\n directory: string,\n) {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n\n const startDir = path.resolve(directory);\n const root = path.parse(startDir).root;\n\n const directories = Array.unfold(startDir, (dir) =>\n dir === root\n ? Option.none()\n : Option.some([dir, path.dirname(dir)] as const),\n );\n\n const projectRoot = yield* Effect.findFirst(directories, (dir) =>\n fs.exists(path.join(dir, \"package.json\")),\n );\n\n return yield* Effect.fromOption(\n projectRoot,\n () => new ProjectRootNotFoundError(),\n );\n});\n\nexport const findProjectRoot = findProjectRootFrom(\".\");\n\nexport const layer = Layer.effect(\n ProjectRoot,\n Effect.gen(function* () {\n const projectRoot = yield* findProjectRoot;\n\n const ref = yield* Ref.make<string>(projectRoot);\n\n return { get: Ref.get(ref) } as const;\n }),\n).pipe(Layer.provide(NodeFileSystem.layer));\n\nexport class ProjectRootNotFoundError extends Schema.TaggedError<ProjectRootNotFoundError>()(\n \"ProjectRootNotFoundError\",\n {},\n) {\n override get message(): string {\n return \"Could not find project root (no 'package.json' found)\";\n }\n}\n"],"mappings":";;;;;;;;;;;AAWA,IAAa,cAAb,MAAa,oBAAoB,QAAQ,QAGvC,CAAC,CAAC,0BAA0B,CAAC,CAAC;CAC9B,OAAgB,MAAM,YAAY,KAAK,YAAY,QAAQ,GAAG;AAChE;AAEA,MAAa,sBAAsB,OAAO,WAAW,WACnD,WACA;CACA,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CAEzB,MAAM,WAAW,KAAK,QAAQ,SAAS;CACvC,MAAM,OAAO,KAAK,MAAM,QAAQ,CAAC,CAAC;CAElC,MAAM,cAAc,MAAM,OAAO,WAAW,QAC1C,QAAQ,OACJ,OAAO,KAAK,IACZ,OAAO,KAAK,CAAC,KAAK,KAAK,QAAQ,GAAG,CAAC,CAAU,CACnD;CAEA,MAAM,cAAc,OAAO,OAAO,UAAU,cAAc,QACxD,GAAG,OAAO,KAAK,KAAK,KAAK,cAAc,CAAC,CAC1C;CAEA,OAAO,OAAO,OAAO,WACnB,mBACM,IAAI,yBAAyB,CACrC;AACF,CAAC;AAED,MAAa,kBAAkB,oBAAoB,GAAG;AAEtD,MAAa,QAAQ,MAAM,OACzB,aACA,OAAO,IAAI,aAAa;CACtB,MAAM,cAAc,OAAO;CAE3B,MAAM,MAAM,OAAO,IAAI,KAAa,WAAW;CAE/C,OAAO,EAAE,KAAK,IAAI,IAAI,GAAG,EAAE;AAC7B,CAAC,CACH,CAAC,CAAC,KAAK,MAAM,QAAQ,eAAe,KAAK,CAAC;AAE1C,IAAa,2BAAb,cAA8C,OAAO,YAAsC,CAAC,CAC1F,4BACA,CAAC,CACH,CAAC,CAAC;CACA,IAAa,UAAkB;EAC7B,OAAO;CACT;AACF"}
@@ -0,0 +1,13 @@
1
+ import * as Array from "effect/Array";
2
+ import * as Schema from "effect/Schema";
3
+ import * as String from "effect/String";
4
+ //#region src/RelativeModulePath.ts
5
+ const RelativeModulePath = Schema.String.check(Schema.makeFilter((value) => !/^[A-Za-z]:/.test(value) && !String.includes("\0")(value) && Array.every(String.split(value, /[/\\]/), (segment) => String.isNonEmpty(segment) && segment !== "." && segment !== ".."))).pipe(Schema.brand("@confect/cli/RelativeModulePath"));
6
+ const withSuffix = (suffix) => RelativeModulePath.check(Schema.makeFilter((value) => {
7
+ const filename = Array.lastNonEmpty(String.split(value, /[/\\]/));
8
+ return String.endsWith(suffix)(filename) && filename.length > suffix.length;
9
+ }));
10
+ //#endregion
11
+ export { RelativeModulePath, withSuffix };
12
+
13
+ //# sourceMappingURL=RelativeModulePath.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RelativeModulePath.mjs","names":[],"sources":["../src/RelativeModulePath.ts"],"sourcesContent":["import * as Array from \"effect/Array\";\nimport * as Schema from \"effect/Schema\";\nimport * as String from \"effect/String\";\n\nexport const RelativeModulePath = Schema.String.check(\n Schema.makeFilter(\n (value) =>\n !/^[A-Za-z]:/.test(value) &&\n !String.includes(\"\\0\")(value) &&\n Array.every(\n String.split(value, /[/\\\\]/),\n (segment) =>\n String.isNonEmpty(segment) && segment !== \".\" && segment !== \"..\",\n ),\n ),\n).pipe(Schema.brand(\"@confect/cli/RelativeModulePath\"));\n\nexport type RelativeModulePath = typeof RelativeModulePath.Type;\n\nexport const withSuffix = (suffix: string) =>\n RelativeModulePath.check(\n Schema.makeFilter((value) => {\n const filename = Array.lastNonEmpty(String.split(value, /[/\\\\]/));\n return (\n String.endsWith(suffix)(filename) && filename.length > suffix.length\n );\n }),\n );\n"],"mappings":";;;;AAIA,MAAa,qBAAqB,OAAO,OAAO,MAC9C,OAAO,YACJ,UACC,CAAC,aAAa,KAAK,KAAK,KACxB,CAAC,OAAO,SAAS,IAAI,CAAC,CAAC,KAAK,KAC5B,MAAM,MACJ,OAAO,MAAM,OAAO,OAAO,IAC1B,YACC,OAAO,WAAW,OAAO,KAAK,YAAY,OAAO,YAAY,IACjE,CACJ,CACF,CAAC,CAAC,KAAK,OAAO,MAAM,iCAAiC,CAAC;AAItD,MAAa,cAAc,WACzB,mBAAmB,MACjB,OAAO,YAAY,UAAU;CAC3B,MAAM,WAAW,MAAM,aAAa,OAAO,MAAM,OAAO,OAAO,CAAC;CAChE,OACE,OAAO,SAAS,MAAM,CAAC,CAAC,QAAQ,KAAK,SAAS,SAAS,OAAO;AAElE,CAAC,CACH"}
@@ -0,0 +1,24 @@
1
+ import { RemoveBusyError } from "./CodegenError.mjs";
2
+ import * as Effect from "effect/Effect";
3
+ import * as FileSystem from "effect/FileSystem";
4
+ import * as Schedule from "effect/Schedule";
5
+ //#region src/Remove.ts
6
+ const MAX_ATTEMPTS = 4;
7
+ const remove = Effect.fn("Remove.remove")(function* (path) {
8
+ yield* (yield* FileSystem.FileSystem).remove(path, {
9
+ recursive: true,
10
+ force: true
11
+ }).pipe(Effect.retry({
12
+ while: (error) => error.reason._tag === "Busy",
13
+ times: 3,
14
+ schedule: Schedule.exponential("25 millis")
15
+ }), Effect.mapError((error) => error.reason._tag === "Busy" ? new RemoveBusyError({
16
+ path,
17
+ attempts: MAX_ATTEMPTS,
18
+ cause: error
19
+ }) : error));
20
+ });
21
+ //#endregion
22
+ export { remove };
23
+
24
+ //# sourceMappingURL=Remove.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Remove.mjs","names":[],"sources":["../src/Remove.ts"],"sourcesContent":["import * as Effect from \"effect/Effect\";\nimport * as FileSystem from \"effect/FileSystem\";\nimport * as Schedule from \"effect/Schedule\";\nimport { RemoveBusyError } from \"./CodegenError.js\";\n\nconst MAX_ATTEMPTS = 4;\n\nexport const remove = Effect.fn(\"Remove.remove\")(function* (path: string) {\n const fs = yield* FileSystem.FileSystem;\n yield* fs.remove(path, { recursive: true, force: true }).pipe(\n Effect.retry({\n while: (error) => error.reason._tag === \"Busy\",\n times: MAX_ATTEMPTS - 1,\n schedule: Schedule.exponential(\"25 millis\"),\n }),\n Effect.mapError((error) =>\n error.reason._tag === \"Busy\"\n ? new RemoveBusyError({ path, attempts: MAX_ATTEMPTS, cause: error })\n : error,\n ),\n );\n});\n"],"mappings":";;;;;AAKA,MAAM,eAAe;AAErB,MAAa,SAAS,OAAO,GAAG,eAAe,CAAC,CAAC,WAAW,MAAc;CAExE,QAAO,OADW,WAAW,WAAA,CACnB,OAAO,MAAM;EAAE,WAAW;EAAM,OAAO;CAAK,CAAC,CAAC,CAAC,KACvD,OAAO,MAAM;EACX,QAAQ,UAAU,MAAM,OAAO,SAAS;EACxC,OAAO;EACP,UAAU,SAAS,YAAY,WAAW;CAC5C,CAAC,GACD,OAAO,UAAU,UACf,MAAM,OAAO,SAAS,SAClB,IAAI,gBAAgB;EAAE;EAAM,UAAU;EAAc,OAAO;CAAM,CAAC,IAClE,KACN,CACF;AACF,CAAC"}
@@ -0,0 +1,31 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Cause from "effect/Cause";
3
+ import * as Scope from "effect/Scope";
4
+ import * as Deferred from "effect/Deferred";
5
+ import * as Exit from "effect/Exit";
6
+ //#region src/RestoreWatcher.ts
7
+ /**
8
+ * Run a directory watch that a restore can pause: Windows refuses to remove a
9
+ * directory while a recursive watch holds it open.
10
+ */
11
+ const make = Effect.fnUntraced(function* (watch) {
12
+ const parent = yield* Effect.scope;
13
+ const context = yield* Effect.context();
14
+ const failed = yield* Deferred.make();
15
+ const start = Effect.gen(function* () {
16
+ const scope = yield* Scope.fork(parent, "sequential");
17
+ yield* watch.pipe(Effect.provide(context), Effect.onExit((exit) => Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause) ? Deferred.failCause(failed, exit.cause) : Effect.void), Effect.forkIn(scope, { startImmediately: true }));
18
+ return scope;
19
+ });
20
+ let scope = yield* start;
21
+ return {
22
+ failure: Deferred.await(failed),
23
+ duringRestore: Effect.acquireRelease(Effect.suspend(() => Scope.close(scope, Exit.void)), () => start.pipe(Effect.tap((next) => Effect.sync(() => {
24
+ scope = next;
25
+ }))))
26
+ };
27
+ });
28
+ //#endregion
29
+ export { make };
30
+
31
+ //# sourceMappingURL=RestoreWatcher.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RestoreWatcher.mjs","names":[],"sources":["../src/RestoreWatcher.ts"],"sourcesContent":["import * as Cause from \"effect/Cause\";\nimport * as Deferred from \"effect/Deferred\";\nimport * as Effect from \"effect/Effect\";\nimport * as Exit from \"effect/Exit\";\nimport * as Scope from \"effect/Scope\";\n\n/**\n * Run a directory watch that a restore can pause: Windows refuses to remove a\n * directory while a recursive watch holds it open.\n */\n// Untraced, because restarts run in the captured context and would carry this\n// constructor's stack frame into their failures.\nexport const make = Effect.fnUntraced(function* <E, R>(\n watch: Effect.Effect<void, E, R>,\n) {\n const parent = yield* Effect.scope;\n const context = yield* Effect.context<R>();\n const failed = yield* Deferred.make<never, E>();\n const start = Effect.gen(function* () {\n const scope = yield* Scope.fork(parent, \"sequential\");\n yield* watch.pipe(\n Effect.provide(context),\n Effect.onExit((exit) =>\n Exit.isFailure(exit) && !Cause.hasInterruptsOnly(exit.cause)\n ? Deferred.failCause(failed, exit.cause)\n : Effect.void,\n ),\n Effect.forkIn(scope, { startImmediately: true }),\n );\n return scope;\n });\n let scope = yield* start;\n return {\n failure: Deferred.await(failed),\n duringRestore: Effect.acquireRelease(\n Effect.suspend(() => Scope.close(scope, Exit.void)),\n () =>\n start.pipe(\n Effect.tap((next) =>\n Effect.sync(() => {\n scope = next;\n }),\n ),\n ),\n ),\n };\n});\n"],"mappings":";;;;;;;;;;AAYA,MAAa,OAAO,OAAO,WAAW,WACpC,OACA;CACA,MAAM,SAAS,OAAO,OAAO;CAC7B,MAAM,UAAU,OAAO,OAAO,QAAW;CACzC,MAAM,SAAS,OAAO,SAAS,KAAe;CAC9C,MAAM,QAAQ,OAAO,IAAI,aAAa;EACpC,MAAM,QAAQ,OAAO,MAAM,KAAK,QAAQ,YAAY;EACpD,OAAO,MAAM,KACX,OAAO,QAAQ,OAAO,GACtB,OAAO,QAAQ,SACb,KAAK,UAAU,IAAI,KAAK,CAAC,MAAM,kBAAkB,KAAK,KAAK,IACvD,SAAS,UAAU,QAAQ,KAAK,KAAK,IACrC,OAAO,IACb,GACA,OAAO,OAAO,OAAO,EAAE,kBAAkB,KAAK,CAAC,CACjD;EACA,OAAO;CACT,CAAC;CACD,IAAI,QAAQ,OAAO;CACnB,OAAO;EACL,SAAS,SAAS,MAAM,MAAM;EAC9B,eAAe,OAAO,eACpB,OAAO,cAAc,MAAM,MAAM,OAAO,KAAK,IAAI,CAAC,SAEhD,MAAM,KACJ,OAAO,KAAK,SACV,OAAO,WAAW;GAChB,QAAQ;EACV,CAAC,CACH,CACF,CACJ;CACF;AACF,CAAC"}