@confect/cli 9.0.0-next.2 → 9.0.0-next.4
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 +51 -0
- package/dist/Bundler.mjs +34 -15
- package/dist/Bundler.mjs.map +1 -1
- package/dist/SpecAssemblyNode.mjs +19 -1
- package/dist/SpecAssemblyNode.mjs.map +1 -1
- package/dist/confect/dev.mjs +1 -2
- package/dist/confect/dev.mjs.map +1 -1
- package/dist/package.mjs +1 -1
- package/dist/templates.mjs +6 -1
- package/dist/templates.mjs.map +1 -1
- package/package.json +3 -3
- package/src/Bundler.ts +47 -26
- package/src/SpecAssemblyNode.ts +36 -0
- package/src/confect/dev.ts +1 -2
- package/src/templates.ts +8 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,56 @@
|
|
|
1
1
|
# @confect/cli
|
|
2
2
|
|
|
3
|
+
## 9.0.0-next.4
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 46e17f7: Externalize every bare-specifier dependency during codegen bundling instead of inlining third-party packages.
|
|
8
|
+
|
|
9
|
+
### Why
|
|
10
|
+
|
|
11
|
+
Since `9.0.0-next.0`, codegen bundles each `*.impl.ts` with esbuild so it can load the default-exported `Layer` and read the snapshotted function names from a `Finalized` `GroupImpl`. The bundler only marked `@confect/core`, `@confect/server`, `effect`, and `@effect/*` as external — every other dependency, including all of `node_modules` and every `node:*` built-in reached through inlined CJS source, was bundled into the output. With `format: "esm"`, esbuild rewrites any CJS `require(...)` in that inlined source to a runtime shim that throws `Dynamic require of "<id>" is not supported`, so any impl importing a third-party package (`sharp`, `luxon`, `@clerk/backend`, `jsonwebtoken`, `openai`, etc.) failed during `validateImpl`.
|
|
12
|
+
|
|
13
|
+
### What changed
|
|
14
|
+
- `@confect/cli/Bundler`'s `absoluteExternalsPlugin` now externalizes every bare specifier (anything not starting with `./`, `../`, or `/`), resolving each to an absolute file URL via the user's `node_modules`. `node:*` built-ins pass through unchanged.
|
|
15
|
+
- The `EXTERNAL_PACKAGES` allow-list is removed; relative imports continue to be bundled so the user's own source is still transpiled together.
|
|
16
|
+
- `@confect/cli/confect/dev` drops the redundant `external: EXTERNAL_PACKAGES` esbuild option — the plugin handles externalization for both codegen and dev-mode watchers.
|
|
17
|
+
|
|
18
|
+
### Fixes
|
|
19
|
+
|
|
20
|
+
This restores `confect codegen` for impls that import any non-`@confect/*` / `effect` / `@effect/*` library, fixing the regression introduced in `9.0.0-next.0`.
|
|
21
|
+
- @confect/core@9.0.0-next.4
|
|
22
|
+
- @confect/server@9.0.0-next.4
|
|
23
|
+
|
|
24
|
+
## 9.0.0-next.3
|
|
25
|
+
|
|
26
|
+
### Patch Changes
|
|
27
|
+
|
|
28
|
+
- 6d85210: Resolve `FunctionImpl` / `GroupImpl` group paths via an immutable `paths` map on `Spec` instead of identity-walking the assembled tree.
|
|
29
|
+
|
|
30
|
+
### Why
|
|
31
|
+
|
|
32
|
+
Since `9.0.0-next.1`, codegen has wrapped every parent leaf that has sibling subdirectory specs in `<parent>.addGroupAt("child", <child>)`. Because `GroupSpec.addGroupAt` is immutable, that produced a fresh object in the assembled tree, while the parent's `*.impl.ts` continued to hold a reference to the original imported leaf. The runtime resolver compared by `===`, so every such impl failed `validateImpl` with "Could not resolve group path for the provided GroupSpec." Child impls happened to work only because `GroupSpec.withName` was secretly mutating its argument in place to keep the child's identity stable — an asymmetry that was load-bearing for one half of the API and broken for the other.
|
|
33
|
+
|
|
34
|
+
### What changed
|
|
35
|
+
- `@confect/core/Spec` carries a new `readonly paths: ReadonlyMap<GroupSpec.AnyWithProps, string>` field and exposes a chainable `Spec#addPath(group, path)` builder. `add` / `addAt` / `merge` propagate `paths` unchanged; `merge` re-prefixes a node spec's entries with `"node."` to match the merged tree.
|
|
36
|
+
- `@confect/core/GroupSpec.withName` is now pure: it returns a fresh copy when the name differs and no longer rewrites the input in place. No new identity-tracking machinery is introduced.
|
|
37
|
+
- `@confect/server/FunctionImpl.make` and `GroupImpl.make` resolve their group path via `api.spec.paths.get(group)` — an O(1) map lookup instead of a tree walk — and throw a clearer error pointing at `Spec.addPath` when the spec hasn't been registered.
|
|
38
|
+
- `@confect/server/GroupPath` (the old identity-based resolver) is deleted.
|
|
39
|
+
- `@confect/cli` codegen emits one `.addPath(<binding>, "<dot.path>")` call per leaf in `_generated/spec.ts` (and `_generated/nodeSpec.ts`) so the imported leaves carry their full paths into the assembled spec value.
|
|
40
|
+
|
|
41
|
+
### User-facing impact
|
|
42
|
+
- Spec authoring (`*.spec.ts`) and impl authoring (`*.impl.ts`) APIs are unchanged. `FunctionImpl.make(api, spec, name, handler)` and `GroupImpl.make(api, spec)` keep their exact signatures.
|
|
43
|
+
- Generated `_generated/spec.ts` (and `_generated/nodeSpec.ts`) pick up one `.addPath(...)` chain entry per leaf on the next `confect codegen` run. The shape is fully immutable — no module-load mutation, no hidden side effects.
|
|
44
|
+
- Hand-rolled tests that construct a `Spec` and pass it to `Api.make` must now also call `.addPath(spec, "dot.path")` for any group they intend to look up.
|
|
45
|
+
|
|
46
|
+
### Fixes
|
|
47
|
+
|
|
48
|
+
This eliminates the runtime regression introduced in `9.0.0-next.1` for any project layout where a `confect/{path}.spec.ts` declares functions alongside a sibling `confect/{path}/` subdirectory of further specs.
|
|
49
|
+
|
|
50
|
+
- Updated dependencies [6d85210]
|
|
51
|
+
- @confect/core@9.0.0-next.3
|
|
52
|
+
- @confect/server@9.0.0-next.3
|
|
53
|
+
|
|
3
54
|
## 9.0.0-next.2
|
|
4
55
|
|
|
5
56
|
### Patch Changes
|
package/dist/Bundler.mjs
CHANGED
|
@@ -1,30 +1,49 @@
|
|
|
1
1
|
import { BundlerError } from "./BuildError.mjs";
|
|
2
|
-
import { createRequire } from "node:module";
|
|
3
2
|
import { Array, Effect, Option, pipe } from "effect";
|
|
4
3
|
import { Path } from "@effect/platform";
|
|
5
4
|
import * as esbuild from "esbuild";
|
|
6
5
|
import { pathToFileURL } from "node:url";
|
|
7
6
|
|
|
8
7
|
//#region src/Bundler.ts
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
8
|
+
const isRelativeOrAbsolutePath = (importPath) => importPath.startsWith("./") || importPath.startsWith("../") || importPath.startsWith("/");
|
|
9
|
+
const PLUGIN_DATA_SKIP = Symbol("absolute-externals.skip");
|
|
10
|
+
/**
|
|
11
|
+
* Mark every bare-specifier import as external and rewrite it to an absolute
|
|
12
|
+
* `file://` URL. Resolution is delegated to esbuild's own resolver via
|
|
13
|
+
* `build.resolve(...)`, which honors each package's `exports` map (preferring
|
|
14
|
+
* the `import` condition under `format: "esm"`) and falls back to
|
|
15
|
+
* `module`/`main` exactly the way Node's ESM resolution algorithm does.
|
|
16
|
+
*
|
|
17
|
+
* Bundles produced with this plugin are loaded via a data URL `import(...)`
|
|
18
|
+
* (see {@link bundle}); rewriting bare externals to absolute file URLs is what
|
|
19
|
+
* makes them resolvable at runtime, since a data URL has no parent file from
|
|
20
|
+
* which a bare specifier could be resolved.
|
|
21
|
+
*
|
|
22
|
+
* Relative/absolute-path imports are left to esbuild to bundle as usual, and
|
|
23
|
+
* `node:*` built-ins are passed through unchanged.
|
|
24
|
+
*/
|
|
19
25
|
const absoluteExternalsPlugin = {
|
|
20
26
|
name: "absolute-externals",
|
|
21
27
|
setup(build) {
|
|
22
28
|
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
29
|
+
if (args.pluginData?.[PLUGIN_DATA_SKIP]) return;
|
|
23
30
|
if (args.kind !== "import-statement" && args.kind !== "dynamic-import") return;
|
|
24
|
-
if (
|
|
25
|
-
|
|
31
|
+
if (isRelativeOrAbsolutePath(args.path)) return;
|
|
32
|
+
if (args.path.startsWith("node:")) return {
|
|
33
|
+
path: args.path,
|
|
34
|
+
external: true
|
|
35
|
+
};
|
|
36
|
+
const resolved = await build.resolve(args.path, {
|
|
37
|
+
kind: args.kind,
|
|
38
|
+
resolveDir: args.resolveDir,
|
|
39
|
+
pluginData: { [PLUGIN_DATA_SKIP]: true }
|
|
40
|
+
});
|
|
41
|
+
if (resolved.errors.length > 0) return {
|
|
42
|
+
errors: resolved.errors,
|
|
43
|
+
warnings: resolved.warnings
|
|
44
|
+
};
|
|
26
45
|
return {
|
|
27
|
-
path: pathToFileURL(
|
|
46
|
+
path: pathToFileURL(resolved.path).href,
|
|
28
47
|
external: true
|
|
29
48
|
};
|
|
30
49
|
});
|
|
@@ -87,5 +106,5 @@ const directlyImports = (bundled, sourceAbsolutePath, targetAbsolutePath) => Eff
|
|
|
87
106
|
});
|
|
88
107
|
|
|
89
108
|
//#endregion
|
|
90
|
-
export {
|
|
109
|
+
export { absoluteExternalsPlugin, bundle, directlyImports };
|
|
91
110
|
//# sourceMappingURL=Bundler.mjs.map
|
package/dist/Bundler.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Bundler.mjs","names":[],"sources":["../src/Bundler.ts"],"sourcesContent":["import {
|
|
1
|
+
{"version":3,"file":"Bundler.mjs","names":[],"sources":["../src/Bundler.ts"],"sourcesContent":["import { pathToFileURL } from \"node:url\";\nimport { Path } from \"@effect/platform\";\nimport { Array, Effect, Option, pipe } from \"effect\";\nimport * as esbuild from \"esbuild\";\nimport { BundlerError } from \"./BuildError\";\n\nexport interface Bundled {\n readonly module: any;\n readonly metafile: esbuild.Metafile;\n}\n\nconst isRelativeOrAbsolutePath = (importPath: string) =>\n importPath.startsWith(\"./\") ||\n importPath.startsWith(\"../\") ||\n importPath.startsWith(\"/\");\n\n// Recursion guard for `absoluteExternalsPlugin`. When the plugin asks esbuild\n// to resolve a bare specifier via `build.resolve(...)`, esbuild invokes every\n// registered `onResolve` hook again for that same specifier — including this\n// one. The flag (carried through the recursive call via `pluginData`) tells\n// the recursive invocation to skip rewriting and fall through to esbuild's\n// built-in resolver, which is what we wanted from `build.resolve` in the\n// first place.\nconst PLUGIN_DATA_SKIP = Symbol(\"absolute-externals.skip\");\n\n/**\n * Mark every bare-specifier import as external and rewrite it to an absolute\n * `file://` URL. Resolution is delegated to esbuild's own resolver via\n * `build.resolve(...)`, which honors each package's `exports` map (preferring\n * the `import` condition under `format: \"esm\"`) and falls back to\n * `module`/`main` exactly the way Node's ESM resolution algorithm does.\n *\n * Bundles produced with this plugin are loaded via a data URL `import(...)`\n * (see {@link bundle}); rewriting bare externals to absolute file URLs is what\n * makes them resolvable at runtime, since a data URL has no parent file from\n * which a bare specifier could be resolved.\n *\n * Relative/absolute-path imports are left to esbuild to bundle as usual, and\n * `node:*` built-ins are passed through unchanged.\n */\nexport const absoluteExternalsPlugin: esbuild.Plugin = {\n name: \"absolute-externals\",\n setup(build) {\n build.onResolve({ filter: /.*/ }, async (args) => {\n if (args.pluginData?.[PLUGIN_DATA_SKIP]) return;\n if (args.kind !== \"import-statement\" && args.kind !== \"dynamic-import\")\n return;\n if (isRelativeOrAbsolutePath(args.path)) return;\n if (args.path.startsWith(\"node:\")) {\n return { path: args.path, external: true };\n }\n\n const resolved = await build.resolve(args.path, {\n kind: args.kind,\n resolveDir: args.resolveDir,\n pluginData: { [PLUGIN_DATA_SKIP]: true },\n });\n\n if (resolved.errors.length > 0) {\n return { errors: resolved.errors, warnings: resolved.warnings };\n }\n\n return {\n path: pathToFileURL(resolved.path).href,\n external: true,\n };\n });\n },\n};\n\nconst buildEntry = (entryPoint: string) =>\n Effect.tryPromise({\n try: () =>\n esbuild.build({\n entryPoints: [entryPoint],\n bundle: true,\n write: false,\n platform: \"node\",\n format: \"esm\",\n logLevel: \"silent\",\n metafile: true,\n plugins: [absoluteExternalsPlugin],\n }),\n catch: (cause) => new BundlerError({ cause }),\n });\n\nconst importBundledModule = (result: esbuild.BuildResult) => {\n const code = result.outputFiles![0]!.text;\n const dataUrl =\n \"data:text/javascript;base64,\" + Buffer.from(code).toString(\"base64\");\n return import(dataUrl);\n};\n\n/**\n * Bundle a TypeScript entry point with esbuild and import the result via a\n * data URL. This handles extensionless `.ts` imports regardless of whether\n * the user's project sets `\"type\": \"module\"` in package.json. The returned\n * pair carries both the imported module and the esbuild metafile so callers\n * can inspect the import graph (see {@link directlyImports}).\n */\nexport const bundle = (\n entryPoint: string,\n): Effect.Effect<Bundled, BundlerError> =>\n Effect.gen(function* () {\n const result = yield* buildEntry(entryPoint);\n const module = yield* Effect.tryPromise({\n try: () => importBundledModule(result),\n catch: (cause) => new BundlerError({ cause }),\n });\n if (!result.metafile) {\n return yield* Effect.dieMessage(\"esbuild metafile missing\");\n }\n return { module, metafile: result.metafile };\n });\n\nconst findMetafileInputKey = (\n metafile: esbuild.Metafile,\n absolutePath: string,\n) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const resolved = path.resolve(absolutePath);\n return Array.findFirst(\n Object.keys(metafile.inputs),\n (key) => path.resolve(key) === resolved,\n );\n });\n\n/**\n * Returns `true` when the module bundled from `sourceAbsolutePath` declares a\n * direct import of `targetAbsolutePath` (according to the bundle's esbuild\n * metafile). Returns `false` if either path is missing from the metafile.\n */\nexport const directlyImports = (\n bundled: Bundled,\n sourceAbsolutePath: string,\n targetAbsolutePath: string,\n) =>\n Effect.gen(function* () {\n const path = yield* Path.Path;\n const sourceKey = yield* findMetafileInputKey(\n bundled.metafile,\n sourceAbsolutePath,\n );\n const targetKey = yield* findMetafileInputKey(\n bundled.metafile,\n targetAbsolutePath,\n );\n\n return pipe(\n Option.all([sourceKey, targetKey]),\n Option.flatMap(([sourceKey_, targetKey_]) =>\n Option.fromNullable(bundled.metafile.inputs[sourceKey_]).pipe(\n Option.map((sourceInput) => {\n const targetResolved = path.resolve(targetKey_);\n return sourceInput.imports.some(\n (importedFile) =>\n path.resolve(importedFile.path) === targetResolved,\n );\n }),\n ),\n ),\n Option.getOrElse(() => false),\n );\n });\n"],"mappings":";;;;;;;AAWA,MAAM,4BAA4B,eAChC,WAAW,WAAW,KAAK,IAC3B,WAAW,WAAW,MAAM,IAC5B,WAAW,WAAW,IAAI;AAS5B,MAAM,mBAAmB,OAAO,0BAA0B;;;;;;;;;;;;;;;;AAiB1D,MAAa,0BAA0C;CACrD,MAAM;CACN,MAAM,OAAO;AACX,QAAM,UAAU,EAAE,QAAQ,MAAM,EAAE,OAAO,SAAS;AAChD,OAAI,KAAK,aAAa,kBAAmB;AACzC,OAAI,KAAK,SAAS,sBAAsB,KAAK,SAAS,iBACpD;AACF,OAAI,yBAAyB,KAAK,KAAK,CAAE;AACzC,OAAI,KAAK,KAAK,WAAW,QAAQ,CAC/B,QAAO;IAAE,MAAM,KAAK;IAAM,UAAU;IAAM;GAG5C,MAAM,WAAW,MAAM,MAAM,QAAQ,KAAK,MAAM;IAC9C,MAAM,KAAK;IACX,YAAY,KAAK;IACjB,YAAY,GAAG,mBAAmB,MAAM;IACzC,CAAC;AAEF,OAAI,SAAS,OAAO,SAAS,EAC3B,QAAO;IAAE,QAAQ,SAAS;IAAQ,UAAU,SAAS;IAAU;AAGjE,UAAO;IACL,MAAM,cAAc,SAAS,KAAK,CAAC;IACnC,UAAU;IACX;IACD;;CAEL;AAED,MAAM,cAAc,eAClB,OAAO,WAAW;CAChB,WACE,QAAQ,MAAM;EACZ,aAAa,CAAC,WAAW;EACzB,QAAQ;EACR,OAAO;EACP,UAAU;EACV,QAAQ;EACR,UAAU;EACV,UAAU;EACV,SAAS,CAAC,wBAAwB;EACnC,CAAC;CACJ,QAAQ,UAAU,IAAI,aAAa,EAAE,OAAO,CAAC;CAC9C,CAAC;AAEJ,MAAM,uBAAuB,WAAgC;CAC3D,MAAM,OAAO,OAAO,YAAa,GAAI;AAGrC,QAAO,OADL,iCAAiC,OAAO,KAAK,KAAK,CAAC,SAAS,SAAS;;;;;;;;;AAWzE,MAAa,UACX,eAEA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,WAAW,WAAW;CAC5C,MAAM,SAAS,OAAO,OAAO,WAAW;EACtC,WAAW,oBAAoB,OAAO;EACtC,QAAQ,UAAU,IAAI,aAAa,EAAE,OAAO,CAAC;EAC9C,CAAC;AACF,KAAI,CAAC,OAAO,SACV,QAAO,OAAO,OAAO,WAAW,2BAA2B;AAE7D,QAAO;EAAE;EAAQ,UAAU,OAAO;EAAU;EAC5C;AAEJ,MAAM,wBACJ,UACA,iBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,WAAW,KAAK,QAAQ,aAAa;AAC3C,QAAO,MAAM,UACX,OAAO,KAAK,SAAS,OAAO,GAC3B,QAAQ,KAAK,QAAQ,IAAI,KAAK,SAChC;EACD;;;;;;AAOJ,MAAa,mBACX,SACA,oBACA,uBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,YAAY,OAAO,qBACvB,QAAQ,UACR,mBACD;CACD,MAAM,YAAY,OAAO,qBACvB,QAAQ,UACR,mBACD;AAED,QAAO,KACL,OAAO,IAAI,CAAC,WAAW,UAAU,CAAC,EAClC,OAAO,SAAS,CAAC,YAAY,gBAC3B,OAAO,aAAa,QAAQ,SAAS,OAAO,YAAY,CAAC,KACvD,OAAO,KAAK,gBAAgB;EAC1B,MAAM,iBAAiB,KAAK,QAAQ,WAAW;AAC/C,SAAO,YAAY,QAAQ,MACxB,iBACC,KAAK,QAAQ,aAAa,KAAK,KAAK,eACvC;GACD,CACH,CACF,EACD,OAAO,gBAAgB,MAAM,CAC9B;EACD"}
|
|
@@ -28,7 +28,25 @@ const importBindingsForNode = (node) => pipe(node.children, Array.flatMap(import
|
|
|
28
28
|
onSome: (binding) => Array.prepend(childBindings, binding)
|
|
29
29
|
}));
|
|
30
30
|
const collectImportBindings = (nodes) => pipe(Array.flatMap(nodes, importBindingsForNode), (bindings) => Record.fromIterableBy(bindings, (binding) => binding.importPath), Record.toEntries, Array.map(([, binding]) => binding), Array.sortBy(Order.mapInput(Order.string, (binding) => binding.localName)));
|
|
31
|
+
const leafPathsForNode = (node, ancestorSegments) => {
|
|
32
|
+
const segments = [...ancestorSegments, node.segment];
|
|
33
|
+
const childPaths = Array.flatMap(node.children, (child) => leafPathsForNode(child, segments));
|
|
34
|
+
return Option.match(node.importBinding, {
|
|
35
|
+
onNone: () => childPaths,
|
|
36
|
+
onSome: (binding) => Array.prepend(childPaths, {
|
|
37
|
+
binding,
|
|
38
|
+
dotPath: segments.join(".")
|
|
39
|
+
})
|
|
40
|
+
});
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* Walk the assembly tree and produce one entry per leaf spec, pairing its
|
|
44
|
+
* import binding with the full dot-path codegen will register via
|
|
45
|
+
* `Spec.addPath`. Ordering matches `collectImportBindings` (sorted by the
|
|
46
|
+
* binding's local name) so the generated file is stable across runs.
|
|
47
|
+
*/
|
|
48
|
+
const collectLeafPaths = (nodes) => pipe(Array.flatMap(nodes, (node) => leafPathsForNode(node, [])), Array.sortBy(Order.mapInput(Order.string, (entry) => entry.binding.localName)));
|
|
31
49
|
|
|
32
50
|
//#endregion
|
|
33
|
-
export { assemblyNodesFromLeaves, collectImportBindings, partitionByRuntime };
|
|
51
|
+
export { assemblyNodesFromLeaves, collectImportBindings, collectLeafPaths, partitionByRuntime };
|
|
34
52
|
//# sourceMappingURL=SpecAssemblyNode.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SpecAssemblyNode.mjs","names":[],"sources":["../src/SpecAssemblyNode.ts"],"sourcesContent":["import type { LeafModule } from \"./LeafModule\";\nimport { Array, Option, Order, pipe, Record } from \"effect\";\n\nexport interface SpecImportBinding {\n readonly importPath: string;\n readonly exportName: string;\n readonly localName: string;\n}\n\nexport interface SpecAssemblyNode {\n readonly segment: string;\n readonly importBinding: Option.Option<SpecImportBinding>;\n readonly children: ReadonlyArray<SpecAssemblyNode>;\n}\n\nconst importBindingFromLeaf = (leaf: LeafModule): SpecImportBinding => ({\n importPath: leaf.specImportPath,\n exportName: leaf.exportName,\n localName: leaf.pathSegments.join(\"_\"),\n});\n\nconst assemblyNodesAtDepth = (\n leaves: ReadonlyArray<LeafModule>,\n depth: number,\n): ReadonlyArray<SpecAssemblyNode> =>\n pipe(\n Array.groupBy(leaves, (leaf) => leaf.pathSegments[depth]!),\n Record.toEntries,\n Array.sortBy(Order.mapInput(Order.string, ([segment]) => segment)),\n Array.map(([segment, groupLeaves]) => {\n const terminal = Array.findFirst(\n groupLeaves,\n (leaf) => leaf.pathSegments.length === depth + 1,\n );\n const descendants = Array.filter(\n groupLeaves,\n (leaf) => leaf.pathSegments.length > depth + 1,\n );\n return {\n segment,\n importBinding: Option.map(terminal, importBindingFromLeaf),\n children: assemblyNodesAtDepth(descendants, depth + 1),\n };\n }),\n );\n\nexport const assemblyNodesFromLeaves = (\n leaves: ReadonlyArray<LeafModule>,\n): ReadonlyArray<SpecAssemblyNode> => assemblyNodesAtDepth(leaves, 0);\n\nexport const partitionByRuntime = (\n leaves: ReadonlyArray<LeafModule>,\n): {\n readonly convex: ReadonlyArray<LeafModule>;\n readonly node: ReadonlyArray<LeafModule>;\n} => {\n const [node, convex] = Array.partition(\n leaves,\n (leaf) => leaf.runtime === \"Convex\",\n );\n return { convex, node };\n};\n\nconst importBindingsForNode = (\n node: SpecAssemblyNode,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(node.children, Array.flatMap(importBindingsForNode), (childBindings) =>\n Option.match(node.importBinding, {\n onNone: () => childBindings,\n onSome: (binding) => Array.prepend(childBindings, binding),\n }),\n );\n\nexport const collectImportBindings = (\n nodes: ReadonlyArray<SpecAssemblyNode>,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(\n Array.flatMap(nodes, importBindingsForNode),\n (bindings) =>\n Record.fromIterableBy(bindings, (binding) => binding.importPath),\n Record.toEntries,\n Array.map(([, binding]) => binding),\n Array.sortBy(Order.mapInput(Order.string, (binding) => binding.localName)),\n );\n"],"mappings":";;;AAeA,MAAM,yBAAyB,UAAyC;CACtE,YAAY,KAAK;CACjB,YAAY,KAAK;CACjB,WAAW,KAAK,aAAa,KAAK,IAAI;CACvC;AAED,MAAM,wBACJ,QACA,UAEA,KACE,MAAM,QAAQ,SAAS,SAAS,KAAK,aAAa,OAAQ,EAC1D,OAAO,WACP,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,CAAC,aAAa,QAAQ,CAAC,EAClE,MAAM,KAAK,CAAC,SAAS,iBAAiB;CACpC,MAAM,WAAW,MAAM,UACrB,cACC,SAAS,KAAK,aAAa,WAAW,QAAQ,EAChD;CACD,MAAM,cAAc,MAAM,OACxB,cACC,SAAS,KAAK,aAAa,SAAS,QAAQ,EAC9C;AACD,QAAO;EACL;EACA,eAAe,OAAO,IAAI,UAAU,sBAAsB;EAC1D,UAAU,qBAAqB,aAAa,QAAQ,EAAE;EACvD;EACD,CACH;AAEH,MAAa,2BACX,WACoC,qBAAqB,QAAQ,EAAE;AAErE,MAAa,sBACX,WAIG;CACH,MAAM,CAAC,MAAM,UAAU,MAAM,UAC3B,SACC,SAAS,KAAK,YAAY,SAC5B;AACD,QAAO;EAAE;EAAQ;EAAM;;AAGzB,MAAM,yBACJ,SAEA,KAAK,KAAK,UAAU,MAAM,QAAQ,sBAAsB,GAAG,kBACzD,OAAO,MAAM,KAAK,eAAe;CAC/B,cAAc;CACd,SAAS,YAAY,MAAM,QAAQ,eAAe,QAAQ;CAC3D,CAAC,CACH;AAEH,MAAa,yBACX,UAEA,KACE,MAAM,QAAQ,OAAO,sBAAsB,GAC1C,aACC,OAAO,eAAe,WAAW,YAAY,QAAQ,WAAW,EAClE,OAAO,WACP,MAAM,KAAK,GAAG,aAAa,QAAQ,EACnC,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,YAAY,QAAQ,UAAU,CAAC,CAC3E"}
|
|
1
|
+
{"version":3,"file":"SpecAssemblyNode.mjs","names":[],"sources":["../src/SpecAssemblyNode.ts"],"sourcesContent":["import type { LeafModule } from \"./LeafModule\";\nimport { Array, Option, Order, pipe, Record } from \"effect\";\n\nexport interface SpecImportBinding {\n readonly importPath: string;\n readonly exportName: string;\n readonly localName: string;\n}\n\nexport interface SpecAssemblyNode {\n readonly segment: string;\n readonly importBinding: Option.Option<SpecImportBinding>;\n readonly children: ReadonlyArray<SpecAssemblyNode>;\n}\n\nconst importBindingFromLeaf = (leaf: LeafModule): SpecImportBinding => ({\n importPath: leaf.specImportPath,\n exportName: leaf.exportName,\n localName: leaf.pathSegments.join(\"_\"),\n});\n\nconst assemblyNodesAtDepth = (\n leaves: ReadonlyArray<LeafModule>,\n depth: number,\n): ReadonlyArray<SpecAssemblyNode> =>\n pipe(\n Array.groupBy(leaves, (leaf) => leaf.pathSegments[depth]!),\n Record.toEntries,\n Array.sortBy(Order.mapInput(Order.string, ([segment]) => segment)),\n Array.map(([segment, groupLeaves]) => {\n const terminal = Array.findFirst(\n groupLeaves,\n (leaf) => leaf.pathSegments.length === depth + 1,\n );\n const descendants = Array.filter(\n groupLeaves,\n (leaf) => leaf.pathSegments.length > depth + 1,\n );\n return {\n segment,\n importBinding: Option.map(terminal, importBindingFromLeaf),\n children: assemblyNodesAtDepth(descendants, depth + 1),\n };\n }),\n );\n\nexport const assemblyNodesFromLeaves = (\n leaves: ReadonlyArray<LeafModule>,\n): ReadonlyArray<SpecAssemblyNode> => assemblyNodesAtDepth(leaves, 0);\n\nexport const partitionByRuntime = (\n leaves: ReadonlyArray<LeafModule>,\n): {\n readonly convex: ReadonlyArray<LeafModule>;\n readonly node: ReadonlyArray<LeafModule>;\n} => {\n const [node, convex] = Array.partition(\n leaves,\n (leaf) => leaf.runtime === \"Convex\",\n );\n return { convex, node };\n};\n\nconst importBindingsForNode = (\n node: SpecAssemblyNode,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(node.children, Array.flatMap(importBindingsForNode), (childBindings) =>\n Option.match(node.importBinding, {\n onNone: () => childBindings,\n onSome: (binding) => Array.prepend(childBindings, binding),\n }),\n );\n\nexport const collectImportBindings = (\n nodes: ReadonlyArray<SpecAssemblyNode>,\n): ReadonlyArray<SpecImportBinding> =>\n pipe(\n Array.flatMap(nodes, importBindingsForNode),\n (bindings) =>\n Record.fromIterableBy(bindings, (binding) => binding.importPath),\n Record.toEntries,\n Array.map(([, binding]) => binding),\n Array.sortBy(Order.mapInput(Order.string, (binding) => binding.localName)),\n );\n\nexport interface SpecLeafPath {\n readonly binding: SpecImportBinding;\n readonly dotPath: string;\n}\n\nconst leafPathsForNode = (\n node: SpecAssemblyNode,\n ancestorSegments: ReadonlyArray<string>,\n): ReadonlyArray<SpecLeafPath> => {\n const segments = [...ancestorSegments, node.segment];\n const childPaths = Array.flatMap(node.children, (child) =>\n leafPathsForNode(child, segments),\n );\n return Option.match(node.importBinding, {\n onNone: () => childPaths,\n onSome: (binding) =>\n Array.prepend(childPaths, { binding, dotPath: segments.join(\".\") }),\n });\n};\n\n/**\n * Walk the assembly tree and produce one entry per leaf spec, pairing its\n * import binding with the full dot-path codegen will register via\n * `Spec.addPath`. Ordering matches `collectImportBindings` (sorted by the\n * binding's local name) so the generated file is stable across runs.\n */\nexport const collectLeafPaths = (\n nodes: ReadonlyArray<SpecAssemblyNode>,\n): ReadonlyArray<SpecLeafPath> =>\n pipe(\n Array.flatMap(nodes, (node) => leafPathsForNode(node, [])),\n Array.sortBy(\n Order.mapInput(Order.string, (entry) => entry.binding.localName),\n ),\n );\n"],"mappings":";;;AAeA,MAAM,yBAAyB,UAAyC;CACtE,YAAY,KAAK;CACjB,YAAY,KAAK;CACjB,WAAW,KAAK,aAAa,KAAK,IAAI;CACvC;AAED,MAAM,wBACJ,QACA,UAEA,KACE,MAAM,QAAQ,SAAS,SAAS,KAAK,aAAa,OAAQ,EAC1D,OAAO,WACP,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,CAAC,aAAa,QAAQ,CAAC,EAClE,MAAM,KAAK,CAAC,SAAS,iBAAiB;CACpC,MAAM,WAAW,MAAM,UACrB,cACC,SAAS,KAAK,aAAa,WAAW,QAAQ,EAChD;CACD,MAAM,cAAc,MAAM,OACxB,cACC,SAAS,KAAK,aAAa,SAAS,QAAQ,EAC9C;AACD,QAAO;EACL;EACA,eAAe,OAAO,IAAI,UAAU,sBAAsB;EAC1D,UAAU,qBAAqB,aAAa,QAAQ,EAAE;EACvD;EACD,CACH;AAEH,MAAa,2BACX,WACoC,qBAAqB,QAAQ,EAAE;AAErE,MAAa,sBACX,WAIG;CACH,MAAM,CAAC,MAAM,UAAU,MAAM,UAC3B,SACC,SAAS,KAAK,YAAY,SAC5B;AACD,QAAO;EAAE;EAAQ;EAAM;;AAGzB,MAAM,yBACJ,SAEA,KAAK,KAAK,UAAU,MAAM,QAAQ,sBAAsB,GAAG,kBACzD,OAAO,MAAM,KAAK,eAAe;CAC/B,cAAc;CACd,SAAS,YAAY,MAAM,QAAQ,eAAe,QAAQ;CAC3D,CAAC,CACH;AAEH,MAAa,yBACX,UAEA,KACE,MAAM,QAAQ,OAAO,sBAAsB,GAC1C,aACC,OAAO,eAAe,WAAW,YAAY,QAAQ,WAAW,EAClE,OAAO,WACP,MAAM,KAAK,GAAG,aAAa,QAAQ,EACnC,MAAM,OAAO,MAAM,SAAS,MAAM,SAAS,YAAY,QAAQ,UAAU,CAAC,CAC3E;AAOH,MAAM,oBACJ,MACA,qBACgC;CAChC,MAAM,WAAW,CAAC,GAAG,kBAAkB,KAAK,QAAQ;CACpD,MAAM,aAAa,MAAM,QAAQ,KAAK,WAAW,UAC/C,iBAAiB,OAAO,SAAS,CAClC;AACD,QAAO,OAAO,MAAM,KAAK,eAAe;EACtC,cAAc;EACd,SAAS,YACP,MAAM,QAAQ,YAAY;GAAE;GAAS,SAAS,SAAS,KAAK,IAAI;GAAE,CAAC;EACtE,CAAC;;;;;;;;AASJ,MAAa,oBACX,UAEA,KACE,MAAM,QAAQ,QAAQ,SAAS,iBAAiB,MAAM,EAAE,CAAC,CAAC,EAC1D,MAAM,OACJ,MAAM,SAAS,MAAM,SAAS,UAAU,MAAM,QAAQ,UAAU,CACjE,CACF"}
|
package/dist/confect/dev.mjs
CHANGED
|
@@ -5,7 +5,7 @@ import { catchAndLog } from "../CodegenError.mjs";
|
|
|
5
5
|
import { ConvexDirectory } from "../ConvexDirectory.mjs";
|
|
6
6
|
import { ConfectDirectory } from "../ConfectDirectory.mjs";
|
|
7
7
|
import { FunctionPaths, diff } from "../FunctionPaths.mjs";
|
|
8
|
-
import {
|
|
8
|
+
import { absoluteExternalsPlugin } from "../Bundler.mjs";
|
|
9
9
|
import { generateAuthConfig, generateCrons, generateHttp } from "../utils.mjs";
|
|
10
10
|
import { discoverLeafImplFiles, isLeafImplPath, isLeafSpecPath } from "../LeafModule.mjs";
|
|
11
11
|
import { codegenHandler, loadPreviousFunctionPaths } from "./codegen.mjs";
|
|
@@ -184,7 +184,6 @@ const esbuildOptions = (entry, signal, pendingRef, watcherErrorsRef) => {
|
|
|
184
184
|
platform: "node",
|
|
185
185
|
format: "esm",
|
|
186
186
|
logLevel: "silent",
|
|
187
|
-
external: EXTERNAL_PACKAGES,
|
|
188
187
|
plugins: [absoluteExternalsPlugin, {
|
|
189
188
|
name: "notify-rebuild",
|
|
190
189
|
setup(build) {
|
package/dist/confect/dev.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dev.mjs","names":["FunctionPaths.diff","CodegenError.catchAndLog"],"sources":["../../src/confect/dev.ts"],"sourcesContent":["import { Command } from \"@effect/cli\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Ansi, AnsiDoc } from \"@effect/printer-ansi\";\nimport {\n Array,\n Chunk,\n Clock,\n Console,\n Duration,\n Effect,\n Equal,\n ExecutionStrategy,\n Exit,\n HashSet,\n Match,\n Option,\n Order,\n pipe,\n Queue,\n Ref,\n Scope,\n Stream,\n String,\n} from \"effect\";\nimport * as esbuild from \"esbuild\";\nimport { logCoalescedBuildErrors } from \"../BuildError\";\nimport { absoluteExternalsPlugin, EXTERNAL_PACKAGES } from \"../Bundler\";\nimport * as CodegenError from \"../CodegenError\";\nimport { ConfectDirectory } from \"../ConfectDirectory\";\nimport { ConvexDirectory } from \"../ConvexDirectory\";\nimport * as FunctionPaths from \"../FunctionPaths\";\nimport type * as GroupPaths from \"../GroupPaths\";\nimport {\n discoverLeafImplFiles,\n isLeafImplPath,\n isLeafSpecPath,\n} from \"../LeafModule\";\nimport {\n logFunctionAdded,\n logFunctionRemoved,\n logPending,\n logSuccess,\n} from \"../log\";\nimport { ProjectRoot } from \"../ProjectRoot\";\nimport { generateAuthConfig, generateCrons, generateHttp } from \"../utils\";\nimport { codegenHandler, loadPreviousFunctionPaths } from \"./codegen\";\n\nconst GENERATED_SPEC_PATH = \"_generated/spec.ts\";\nconst GENERATED_NODE_SPEC_PATH = \"_generated/nodeSpec.ts\";\n\n// Quiescence window: the sync loop waits this long for further signals\n// after each batch. One user edit fires `onEnd` on every esbuild\n// watcher that touches the file, and rebuild times vary across entry\n// points so the onEnds can be spread over hundreds of milliseconds.\n// The drain keeps extending its wait (bounded by `COALESCE_MAX_WAIT`)\n// until no new signals arrive within the window, collapsing the whole\n// burst into a single codegen cycle.\nconst COALESCE_QUIESCENCE = Duration.millis(300);\n\n// Upper bound on `drainUntilQuiescent` so a pathological infinite\n// signal stream can't pin the sync loop forever.\nconst COALESCE_MAX_WAIT = Duration.seconds(5);\n\n// How long to wait for esbuild watchers to react to codegen's own\n// writes (e.g. an updated `_generated/spec.ts`). Added on top of the\n// quiescence drain when codegen reported writes happened.\nconst ECHO_COOLDOWN = Duration.millis(500);\n\nconst emptyFunctionPaths = FunctionPaths.FunctionPaths.make(HashSet.empty());\n\ntype Pending = {\n readonly specDirty: boolean;\n readonly httpDirty: boolean;\n readonly cronsDirty: boolean;\n readonly authDirty: boolean;\n};\n\ntype PendingKey = keyof Pending;\n\nconst pendingInit: Pending = {\n specDirty: false,\n httpDirty: false,\n cronsDirty: false,\n authDirty: false,\n};\n\nconst isPendingDirty = (p: Pending): boolean =>\n p.specDirty || p.httpDirty || p.cronsDirty || p.authDirty;\n\ntype WatcherErrors = ReadonlyMap<string, readonly esbuild.Message[]>;\n\nconst emptyWatcherErrors: WatcherErrors = new Map();\n\nconst changeChar = (change: \"Added\" | \"Removed\" | \"Modified\") =>\n Match.value(change).pipe(\n Match.when(\"Added\", () => ({ char: \"+\", color: Ansi.green })),\n Match.when(\"Removed\", () => ({ char: \"-\", color: Ansi.red })),\n Match.when(\"Modified\", () => ({ char: \"~\", color: Ansi.yellow })),\n Match.exhaustive,\n );\n\nconst logFileChangeIndented = (\n change: \"Added\" | \"Removed\" | \"Modified\",\n fullPath: string,\n) =>\n Effect.gen(function* () {\n const projectRoot = yield* ProjectRoot.get;\n const path = yield* Path.Path;\n\n const prefix = projectRoot + path.sep;\n const suffix = pipe(fullPath, String.startsWith(prefix))\n ? pipe(fullPath, String.slice(prefix.length))\n : fullPath;\n\n const { char, color } = changeChar(change);\n\n yield* Console.log(\n pipe(\n AnsiDoc.char(char),\n AnsiDoc.annotate(color),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(AnsiDoc.text(prefix), AnsiDoc.annotate(Ansi.blackBright)),\n pipe(AnsiDoc.text(suffix), AnsiDoc.annotate(color)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n });\n\nconst logFunctionPathDiff = (\n previous: FunctionPaths.FunctionPaths,\n current: FunctionPaths.FunctionPaths,\n) =>\n Effect.gen(function* () {\n const {\n functionsAdded,\n functionsRemoved,\n groupsRemoved,\n groupsAdded,\n groupsChanged,\n } = FunctionPaths.diff(previous, current);\n\n const logForGroups = (\n groupPaths: GroupPaths.GroupPaths,\n fnPaths: FunctionPaths.FunctionPaths,\n logFn: typeof logFunctionAdded,\n ) =>\n Effect.forEach(groupPaths, (gp) =>\n Effect.forEach(\n Array.fromIterable(\n HashSet.filter(fnPaths, (fp) => Equal.equals(fp.groupPath, gp)),\n ),\n logFn,\n ),\n );\n\n yield* logForGroups(groupsRemoved, functionsRemoved, logFunctionRemoved);\n yield* logForGroups(groupsAdded, functionsAdded, logFunctionAdded);\n yield* Effect.forEach(groupsChanged, (gp) =>\n Effect.gen(function* () {\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsAdded, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionAdded,\n );\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsRemoved, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionRemoved,\n );\n }),\n );\n });\n\nexport const dev = Command.make(\"dev\", {}, () =>\n Effect.gen(function* () {\n yield* logPending(\"Performing initial sync…\");\n const previousFunctionPaths = yield* loadPreviousFunctionPaths;\n const initialResult = yield* codegenHandler.pipe(\n Effect.tap(({ functionPaths }) =>\n logFunctionPathDiff(previousFunctionPaths, functionPaths),\n ),\n Effect.tap(() => logSuccess(\"Generated files are up-to-date\")),\n CodegenError.catchAndLog,\n );\n const initialFunctionPaths = Option.match(initialResult, {\n onNone: () => emptyFunctionPaths,\n onSome: ({ functionPaths }) => functionPaths,\n });\n\n const pendingRef = yield* Ref.make<Pending>(pendingInit);\n const signal = yield* Queue.sliding<void>(1);\n const restartQueue = yield* Queue.sliding<void>(1);\n const watcherErrorsRef = yield* Ref.make<WatcherErrors>(emptyWatcherErrors);\n\n yield* Effect.all(\n [\n Effect.scoped(\n entryPointsWatcher(\n signal,\n pendingRef,\n restartQueue,\n watcherErrorsRef,\n ),\n ),\n confectStructureWatcher(signal, pendingRef, restartQueue),\n syncLoop(signal, pendingRef, initialFunctionPaths, watcherErrorsRef),\n ],\n { concurrency: \"unbounded\" },\n );\n }),\n).pipe(Command.withDescription(\"Start the Confect development server\"));\n\nconst esbuildMessageKey = (m: esbuild.Message): string =>\n `${m.location?.file ?? \"\"}:${m.location?.line ?? \"\"}:${m.location?.column ?? \"\"}:${m.text}`;\n\nconst allMessages = (errors: WatcherErrors): ReadonlyArray<esbuild.Message> =>\n pipe(Array.fromIterable(errors.values()), Array.flatten);\n\nconst dedupeWatcherErrors = (\n errors: WatcherErrors,\n): ReadonlyArray<esbuild.Message> =>\n pipe(\n allMessages(errors),\n Array.dedupeWith(\n (messageA, messageB) =>\n esbuildMessageKey(messageA) === esbuildMessageKey(messageB),\n ),\n );\n\nconst watcherErrorsSignature = (errors: WatcherErrors): string =>\n pipe(\n allMessages(errors),\n Array.map(esbuildMessageKey),\n Array.dedupe,\n Array.sort(Order.string),\n Array.join(\"\\n\"),\n );\n\n/**\n * Log any watcher errors that haven't already been logged at their\n * current signature. Suppresses the per-watcher fanout that happens\n * when one root cause (e.g. a missing import) breaks every entry\n * point's build at the same source location.\n */\nconst logChangedWatcherErrors = (\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n lastLoggedSignatureRef: Ref.Ref<string>,\n) =>\n Effect.gen(function* () {\n const errors = yield* Ref.get(watcherErrorsRef);\n const signature = watcherErrorsSignature(errors);\n const previous = yield* Ref.get(lastLoggedSignatureRef);\n if (signature === previous) return;\n yield* Ref.set(lastLoggedSignatureRef, signature);\n if (errors.size === 0) return;\n yield* logCoalescedBuildErrors(dedupeWatcherErrors(errors));\n });\n\n/**\n * Block until the signal queue has been quiet for `quiescence`. esbuild\n * watchers' `onEnd` events for a single user edit can be spread across\n * hundreds of milliseconds, so a fixed window misses late arrivals.\n * Bounded by `maxWait` so pathological signal floods can't pin the\n * loop forever.\n */\nconst drainUntilQuiescent = (\n signal: Queue.Queue<void>,\n quiescence: Duration.Duration,\n maxWait: Duration.Duration,\n) =>\n Effect.gen(function* () {\n const start = yield* Clock.currentTimeMillis;\n const maxMillis = Duration.toMillis(maxWait);\n yield* Effect.iterate(true as boolean, {\n while: (keepGoing) => keepGoing,\n body: () =>\n Effect.gen(function* () {\n yield* Effect.sleep(quiescence);\n const drained = yield* Queue.takeAll(signal);\n if (Chunk.isEmpty(drained)) return false;\n const now = yield* Clock.currentTimeMillis;\n return now - start < maxMillis;\n }),\n });\n });\n\nconst syncLoop = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n initialFunctionPaths: FunctionPaths.FunctionPaths,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) =>\n Effect.gen(function* () {\n const functionPathsRef = yield* Ref.make(initialFunctionPaths);\n const lastLoggedErrorsRef = yield* Ref.make<string>(\"\");\n\n return yield* Effect.forever(\n Effect.gen(function* () {\n yield* Effect.logDebug(\"Running sync loop…\");\n // Wait for the first signal of a burst, then keep absorbing\n // follow-up signals from other watchers' onEnds until the queue\n // stays quiet for `COALESCE_QUIESCENCE`.\n yield* Queue.take(signal);\n yield* drainUntilQuiescent(\n signal,\n COALESCE_QUIESCENCE,\n COALESCE_MAX_WAIT,\n );\n\n yield* logChangedWatcherErrors(watcherErrorsRef, lastLoggedErrorsRef);\n\n const pending = yield* Ref.getAndSet(pendingRef, pendingInit);\n\n if (!isPendingDirty(pending)) {\n // No-op signal (e.g. a late echo after a previous cycle\n // already drained). Stay silent.\n return;\n }\n\n yield* logPending(\"Dependencies may have changed, reloading…\");\n\n if (pending.specDirty) {\n const current = yield* codegenHandler.pipe(\n Effect.tap(({ functionPaths: nextFunctionPaths }) =>\n Effect.gen(function* () {\n const previous = yield* Ref.get(functionPathsRef);\n yield* logFunctionPathDiff(previous, nextFunctionPaths);\n yield* Ref.set(functionPathsRef, nextFunctionPaths);\n }),\n ),\n CodegenError.catchAndLog,\n );\n if (Option.isNone(current)) {\n return;\n }\n // Drain any stragglers from this cycle's burst (slow watchers\n // whose onEnd fired after the first quiescence) plus, when\n // codegen wrote, the echo signals esbuild emits in response\n // to our writes. Reset `pendingRef` so those drained signals\n // don't carry a dirty flag into the next cycle.\n if (current.value.anyWritesHappened) {\n yield* Effect.sleep(ECHO_COOLDOWN);\n }\n yield* drainUntilQuiescent(\n signal,\n COALESCE_QUIESCENCE,\n COALESCE_MAX_WAIT,\n );\n yield* Ref.set(pendingRef, pendingInit);\n }\n\n const dirtyOptionalFiles = [\n ...(pending.httpDirty\n ? [syncOptionalFile(generateHttp, \"http.ts\")]\n : []),\n ...(pending.cronsDirty\n ? [syncOptionalFile(generateCrons, \"crons.ts\")]\n : []),\n ...(pending.authDirty\n ? [syncOptionalFile(generateAuthConfig, \"auth.config.ts\")]\n : []),\n ];\n\n yield* Array.isNonEmptyReadonlyArray(dirtyOptionalFiles)\n ? Effect.all(dirtyOptionalFiles, { concurrency: \"unbounded\" })\n : Effect.void;\n\n yield* logSuccess(\"Generated files are up-to-date\");\n }),\n );\n });\n\ninterface EntryPoint {\n readonly absolutePath: string;\n readonly displayPath: string;\n readonly pendingKey: PendingKey;\n}\n\n/**\n * Every file whose import graph codegen should react to. Each one becomes\n * its own scoped esbuild watcher; the union of their watches gives us\n * dependency-aware tracking of anything reachable from `confect/`,\n * including files outside `confect/`.\n */\nconst discoverEntryPoints = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const projectRoot = yield* ProjectRoot.get;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const tryEntry = (relativePath: string, pendingKey: PendingKey) =>\n Effect.gen(function* () {\n const absolutePath = path.join(confectDirectory, relativePath);\n if (!(yield* fs.exists(absolutePath))) {\n return Option.none<EntryPoint>();\n }\n return Option.some<EntryPoint>({\n absolutePath,\n displayPath: path.relative(projectRoot, absolutePath),\n pendingKey,\n });\n });\n\n const fixedEntryOptions = yield* Effect.all([\n tryEntry(GENERATED_SPEC_PATH, \"specDirty\"),\n tryEntry(GENERATED_NODE_SPEC_PATH, \"specDirty\"),\n tryEntry(\"schema.ts\", \"specDirty\"),\n tryEntry(\"http.ts\", \"httpDirty\"),\n tryEntry(\"crons.ts\", \"cronsDirty\"),\n tryEntry(\"auth.ts\", \"authDirty\"),\n ]);\n\n const implRelativePaths = yield* discoverLeafImplFiles;\n const implEntryOptions = yield* Effect.forEach(\n implRelativePaths,\n (relativePath) => tryEntry(relativePath, \"specDirty\"),\n );\n\n return Array.getSomes([...fixedEntryOptions, ...implEntryOptions]);\n});\n\nconst esbuildOptions = (\n entry: EntryPoint,\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) => {\n // First `onEnd` fires when esbuild finishes the watcher's initial\n // build. At startup that's an echo of the just-completed initial\n // codegen pass; for a watcher spawned mid-session (e.g. a newly\n // added impl) it's an echo of the codegen run that triggered the\n // restart. Either way, the entry's contents were already accounted\n // for, so we record any errors but don't flip dirty or push a\n // signal — only genuine subsequent rebuilds should do that.\n const initialBuildSeenRef = Ref.unsafeMake(false);\n return {\n entryPoints: [entry.absolutePath],\n bundle: true,\n write: false,\n metafile: true,\n platform: \"node\" as const,\n format: \"esm\" as const,\n logLevel: \"silent\" as const,\n external: EXTERNAL_PACKAGES,\n plugins: [\n absoluteExternalsPlugin,\n {\n name: \"notify-rebuild\",\n setup(build: esbuild.PluginBuild) {\n build.onEnd((result) => {\n Effect.runPromise(\n Effect.gen(function* () {\n const wasInitial = yield* Ref.getAndSet(\n initialBuildSeenRef,\n true,\n );\n const isInitial = !wasInitial;\n yield* Ref.update(watcherErrorsRef, (current) => {\n const next = new Map(current);\n if (result.errors.length > 0) {\n next.set(entry.absolutePath, result.errors);\n } else {\n next.delete(entry.absolutePath);\n }\n return next;\n });\n if (isInitial && result.errors.length === 0) return;\n yield* Ref.update(pendingRef, (p) => ({\n ...p,\n [entry.pendingKey]: true,\n }));\n yield* Queue.offer(signal, undefined);\n }),\n );\n });\n },\n },\n ],\n };\n};\n\nconst createEntryPointWatcher = (\n entry: EntryPoint,\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) =>\n Effect.acquireRelease(\n Effect.promise(async () => {\n const ctx = await esbuild.context(\n esbuildOptions(entry, signal, pendingRef, watcherErrorsRef),\n );\n await ctx.watch();\n return ctx;\n }),\n (ctx) =>\n Effect.gen(function* () {\n yield* Effect.promise(() => ctx.dispose());\n // Clear any errors recorded by this watcher so a disposed\n // watcher can't leave stale errors visible to the sync loop.\n yield* Ref.update(watcherErrorsRef, (current) => {\n if (!current.has(entry.absolutePath)) return current;\n const next = new Map(current);\n next.delete(entry.absolutePath);\n return next;\n });\n yield* Effect.logDebug(\n `esbuild watcher disposed: ${entry.displayPath}`,\n );\n }),\n );\n\n/**\n * Holds one scoped esbuild watcher per entry point and reconciles the set\n * whenever something offers to `restartQueue`. Adding or removing an entry\n * point only spawns/disposes the affected watcher; unchanged entries keep\n * their existing context, so a structural change doesn't churn watchers\n * for unrelated files.\n */\nconst entryPointsWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restartQueue: Queue.Queue<void>,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) =>\n Effect.gen(function* () {\n const parentScope = yield* Effect.scope;\n const scopesRef = yield* Ref.make(new Map<string, Scope.CloseableScope>());\n\n const sync = Effect.gen(function* () {\n const desired = yield* discoverEntryPoints;\n const desiredByPath = new Map(\n desired.map((entryPoint) => [entryPoint.absolutePath, entryPoint]),\n );\n const current = yield* Ref.get(scopesRef);\n\n yield* Effect.forEach(\n Array.fromIterable(current),\n ([absolutePath, childScope]) =>\n desiredByPath.has(absolutePath)\n ? Effect.void\n : Scope.close(childScope, Exit.void).pipe(\n Effect.andThen(\n Ref.update(scopesRef, (scopes) => {\n const updated = new Map(scopes);\n updated.delete(absolutePath);\n return updated;\n }),\n ),\n ),\n );\n\n yield* Effect.forEach(desired, (entry) =>\n Effect.gen(function* () {\n const existing = yield* Ref.get(scopesRef);\n if (existing.has(entry.absolutePath)) return;\n\n const childScope = yield* Scope.fork(\n parentScope,\n ExecutionStrategy.sequential,\n );\n yield* createEntryPointWatcher(\n entry,\n signal,\n pendingRef,\n watcherErrorsRef,\n ).pipe(Scope.extend(childScope));\n yield* Ref.update(scopesRef, (scopes) => {\n const updated = new Map(scopes);\n updated.set(entry.absolutePath, childScope);\n return updated;\n });\n }),\n );\n });\n\n yield* sync;\n\n return yield* Effect.forever(\n Queue.take(restartQueue).pipe(Effect.andThen(sync)),\n );\n });\n\n/**\n * Single recursive `fs.watch` on `confect/`. Flips the matching dirty flag\n * for any change to an entry-point-shaped file (so codegen runs without\n * waiting on a newly spawned esbuild watcher), and offers to\n * `restartQueue` when an entry point is created or removed so the watcher\n * manager picks up the new set.\n */\nconst confectStructureWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restartQueue: Queue.Queue<void>,\n) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n yield* pipe(\n fs.watch(confectDirectory, { recursive: true }),\n Stream.debounce(Duration.millis(200)),\n Stream.runForEach((event) =>\n handleConfectChange({\n relativePath: path.relative(confectDirectory, event.path),\n eventTag: event._tag,\n signal,\n pendingRef,\n restartQueue,\n }),\n ),\n );\n });\n\nconst TOP_LEVEL_OPTIONAL_KEYS: ReadonlyMap<string, PendingKey> = new Map([\n [\"http.ts\", \"httpDirty\"],\n [\"crons.ts\", \"cronsDirty\"],\n [\"auth.ts\", \"authDirty\"],\n]);\n\nconst flipDirtyAndSignal = (\n pendingRef: Ref.Ref<Pending>,\n signal: Queue.Queue<void>,\n key: PendingKey,\n restartQueue: Queue.Queue<void>,\n restart: boolean,\n) =>\n pipe(\n Ref.update(pendingRef, (p) => ({ ...p, [key]: true })),\n Effect.andThen(Queue.offer(signal, undefined)),\n Effect.andThen(\n restart ? Queue.offer(restartQueue, undefined) : Effect.void,\n ),\n );\n\nconst handleConfectChange = ({\n relativePath,\n eventTag,\n signal,\n pendingRef,\n restartQueue,\n}: {\n relativePath: string;\n eventTag: \"Create\" | \"Update\" | \"Remove\";\n signal: Queue.Queue<void>;\n pendingRef: Ref.Ref<Pending>;\n restartQueue: Queue.Queue<void>;\n}) => {\n // _generated/ files are written by codegen itself; reacting to them here\n // would form a loop. The esbuild watchers track the generated specs as\n // entry points, so changes there flow back through `notify-rebuild`.\n if (relativePath.split(/[/\\\\]/).includes(\"_generated\")) {\n return Effect.void;\n }\n\n if (!relativePath.endsWith(\".ts\")) {\n return Effect.void;\n }\n\n const isLifecycleChange = eventTag !== \"Update\";\n\n const topLevelKey = TOP_LEVEL_OPTIONAL_KEYS.get(relativePath);\n if (topLevelKey !== undefined) {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n topLevelKey,\n restartQueue,\n isLifecycleChange,\n );\n }\n\n if (relativePath === \"schema.ts\") {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n \"specDirty\",\n restartQueue,\n isLifecycleChange,\n );\n }\n\n if (isLeafSpecPath(relativePath) || isLeafImplPath(relativePath)) {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n \"specDirty\",\n restartQueue,\n isLifecycleChange,\n );\n }\n\n // Any other `.ts` under `confect/` (helpers like `tables/Notes.ts`).\n // Updates to such files are handled by the esbuild watcher for whichever\n // entry point imports them — its onEnd flips the right dirty flag.\n // Creates are our safety net: when a previously-missing import is added,\n // esbuild may not have its parent directory on a poll path, so we\n // re-run codegen on Create here.\n if (eventTag === \"Create\") {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n \"specDirty\",\n restartQueue,\n false,\n );\n }\n\n return Effect.void;\n};\n\nconst syncOptionalFile = (generate: typeof generateHttp, convexFile: string) =>\n pipe(\n generate,\n Effect.andThen(\n Option.match({\n onSome: ({ change, convexFilePath }) =>\n Match.value(change).pipe(\n Match.when(\"Unchanged\", () => Effect.void),\n Match.whenOr(\"Added\", \"Modified\", (addedOrModified) =>\n logFileChangeIndented(addedOrModified, convexFilePath),\n ),\n Match.exhaustive,\n ),\n onNone: () =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const convexFilePath = path.join(convexDirectory, convexFile);\n\n if (yield* fs.exists(convexFilePath)) {\n yield* fs.remove(convexFilePath);\n yield* logFileChangeIndented(\"Removed\", convexFilePath);\n }\n }),\n }),\n ),\n );\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dev.mjs","names":["FunctionPaths.diff","CodegenError.catchAndLog"],"sources":["../../src/confect/dev.ts"],"sourcesContent":["import { Command } from \"@effect/cli\";\nimport { FileSystem, Path } from \"@effect/platform\";\nimport { Ansi, AnsiDoc } from \"@effect/printer-ansi\";\nimport {\n Array,\n Chunk,\n Clock,\n Console,\n Duration,\n Effect,\n Equal,\n ExecutionStrategy,\n Exit,\n HashSet,\n Match,\n Option,\n Order,\n pipe,\n Queue,\n Ref,\n Scope,\n Stream,\n String,\n} from \"effect\";\nimport * as esbuild from \"esbuild\";\nimport { logCoalescedBuildErrors } from \"../BuildError\";\nimport { absoluteExternalsPlugin } from \"../Bundler\";\nimport * as CodegenError from \"../CodegenError\";\nimport { ConfectDirectory } from \"../ConfectDirectory\";\nimport { ConvexDirectory } from \"../ConvexDirectory\";\nimport * as FunctionPaths from \"../FunctionPaths\";\nimport type * as GroupPaths from \"../GroupPaths\";\nimport {\n discoverLeafImplFiles,\n isLeafImplPath,\n isLeafSpecPath,\n} from \"../LeafModule\";\nimport {\n logFunctionAdded,\n logFunctionRemoved,\n logPending,\n logSuccess,\n} from \"../log\";\nimport { ProjectRoot } from \"../ProjectRoot\";\nimport { generateAuthConfig, generateCrons, generateHttp } from \"../utils\";\nimport { codegenHandler, loadPreviousFunctionPaths } from \"./codegen\";\n\nconst GENERATED_SPEC_PATH = \"_generated/spec.ts\";\nconst GENERATED_NODE_SPEC_PATH = \"_generated/nodeSpec.ts\";\n\n// Quiescence window: the sync loop waits this long for further signals\n// after each batch. One user edit fires `onEnd` on every esbuild\n// watcher that touches the file, and rebuild times vary across entry\n// points so the onEnds can be spread over hundreds of milliseconds.\n// The drain keeps extending its wait (bounded by `COALESCE_MAX_WAIT`)\n// until no new signals arrive within the window, collapsing the whole\n// burst into a single codegen cycle.\nconst COALESCE_QUIESCENCE = Duration.millis(300);\n\n// Upper bound on `drainUntilQuiescent` so a pathological infinite\n// signal stream can't pin the sync loop forever.\nconst COALESCE_MAX_WAIT = Duration.seconds(5);\n\n// How long to wait for esbuild watchers to react to codegen's own\n// writes (e.g. an updated `_generated/spec.ts`). Added on top of the\n// quiescence drain when codegen reported writes happened.\nconst ECHO_COOLDOWN = Duration.millis(500);\n\nconst emptyFunctionPaths = FunctionPaths.FunctionPaths.make(HashSet.empty());\n\ntype Pending = {\n readonly specDirty: boolean;\n readonly httpDirty: boolean;\n readonly cronsDirty: boolean;\n readonly authDirty: boolean;\n};\n\ntype PendingKey = keyof Pending;\n\nconst pendingInit: Pending = {\n specDirty: false,\n httpDirty: false,\n cronsDirty: false,\n authDirty: false,\n};\n\nconst isPendingDirty = (p: Pending): boolean =>\n p.specDirty || p.httpDirty || p.cronsDirty || p.authDirty;\n\ntype WatcherErrors = ReadonlyMap<string, readonly esbuild.Message[]>;\n\nconst emptyWatcherErrors: WatcherErrors = new Map();\n\nconst changeChar = (change: \"Added\" | \"Removed\" | \"Modified\") =>\n Match.value(change).pipe(\n Match.when(\"Added\", () => ({ char: \"+\", color: Ansi.green })),\n Match.when(\"Removed\", () => ({ char: \"-\", color: Ansi.red })),\n Match.when(\"Modified\", () => ({ char: \"~\", color: Ansi.yellow })),\n Match.exhaustive,\n );\n\nconst logFileChangeIndented = (\n change: \"Added\" | \"Removed\" | \"Modified\",\n fullPath: string,\n) =>\n Effect.gen(function* () {\n const projectRoot = yield* ProjectRoot.get;\n const path = yield* Path.Path;\n\n const prefix = projectRoot + path.sep;\n const suffix = pipe(fullPath, String.startsWith(prefix))\n ? pipe(fullPath, String.slice(prefix.length))\n : fullPath;\n\n const { char, color } = changeChar(change);\n\n yield* Console.log(\n pipe(\n AnsiDoc.char(char),\n AnsiDoc.annotate(color),\n AnsiDoc.catWithSpace(\n AnsiDoc.hcat([\n pipe(AnsiDoc.text(prefix), AnsiDoc.annotate(Ansi.blackBright)),\n pipe(AnsiDoc.text(suffix), AnsiDoc.annotate(color)),\n ]),\n ),\n AnsiDoc.render({ style: \"pretty\" }),\n ),\n );\n });\n\nconst logFunctionPathDiff = (\n previous: FunctionPaths.FunctionPaths,\n current: FunctionPaths.FunctionPaths,\n) =>\n Effect.gen(function* () {\n const {\n functionsAdded,\n functionsRemoved,\n groupsRemoved,\n groupsAdded,\n groupsChanged,\n } = FunctionPaths.diff(previous, current);\n\n const logForGroups = (\n groupPaths: GroupPaths.GroupPaths,\n fnPaths: FunctionPaths.FunctionPaths,\n logFn: typeof logFunctionAdded,\n ) =>\n Effect.forEach(groupPaths, (gp) =>\n Effect.forEach(\n Array.fromIterable(\n HashSet.filter(fnPaths, (fp) => Equal.equals(fp.groupPath, gp)),\n ),\n logFn,\n ),\n );\n\n yield* logForGroups(groupsRemoved, functionsRemoved, logFunctionRemoved);\n yield* logForGroups(groupsAdded, functionsAdded, logFunctionAdded);\n yield* Effect.forEach(groupsChanged, (gp) =>\n Effect.gen(function* () {\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsAdded, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionAdded,\n );\n yield* Effect.forEach(\n Array.fromIterable(\n HashSet.filter(functionsRemoved, (fp) =>\n Equal.equals(fp.groupPath, gp),\n ),\n ),\n logFunctionRemoved,\n );\n }),\n );\n });\n\nexport const dev = Command.make(\"dev\", {}, () =>\n Effect.gen(function* () {\n yield* logPending(\"Performing initial sync…\");\n const previousFunctionPaths = yield* loadPreviousFunctionPaths;\n const initialResult = yield* codegenHandler.pipe(\n Effect.tap(({ functionPaths }) =>\n logFunctionPathDiff(previousFunctionPaths, functionPaths),\n ),\n Effect.tap(() => logSuccess(\"Generated files are up-to-date\")),\n CodegenError.catchAndLog,\n );\n const initialFunctionPaths = Option.match(initialResult, {\n onNone: () => emptyFunctionPaths,\n onSome: ({ functionPaths }) => functionPaths,\n });\n\n const pendingRef = yield* Ref.make<Pending>(pendingInit);\n const signal = yield* Queue.sliding<void>(1);\n const restartQueue = yield* Queue.sliding<void>(1);\n const watcherErrorsRef = yield* Ref.make<WatcherErrors>(emptyWatcherErrors);\n\n yield* Effect.all(\n [\n Effect.scoped(\n entryPointsWatcher(\n signal,\n pendingRef,\n restartQueue,\n watcherErrorsRef,\n ),\n ),\n confectStructureWatcher(signal, pendingRef, restartQueue),\n syncLoop(signal, pendingRef, initialFunctionPaths, watcherErrorsRef),\n ],\n { concurrency: \"unbounded\" },\n );\n }),\n).pipe(Command.withDescription(\"Start the Confect development server\"));\n\nconst esbuildMessageKey = (m: esbuild.Message): string =>\n `${m.location?.file ?? \"\"}:${m.location?.line ?? \"\"}:${m.location?.column ?? \"\"}:${m.text}`;\n\nconst allMessages = (errors: WatcherErrors): ReadonlyArray<esbuild.Message> =>\n pipe(Array.fromIterable(errors.values()), Array.flatten);\n\nconst dedupeWatcherErrors = (\n errors: WatcherErrors,\n): ReadonlyArray<esbuild.Message> =>\n pipe(\n allMessages(errors),\n Array.dedupeWith(\n (messageA, messageB) =>\n esbuildMessageKey(messageA) === esbuildMessageKey(messageB),\n ),\n );\n\nconst watcherErrorsSignature = (errors: WatcherErrors): string =>\n pipe(\n allMessages(errors),\n Array.map(esbuildMessageKey),\n Array.dedupe,\n Array.sort(Order.string),\n Array.join(\"\\n\"),\n );\n\n/**\n * Log any watcher errors that haven't already been logged at their\n * current signature. Suppresses the per-watcher fanout that happens\n * when one root cause (e.g. a missing import) breaks every entry\n * point's build at the same source location.\n */\nconst logChangedWatcherErrors = (\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n lastLoggedSignatureRef: Ref.Ref<string>,\n) =>\n Effect.gen(function* () {\n const errors = yield* Ref.get(watcherErrorsRef);\n const signature = watcherErrorsSignature(errors);\n const previous = yield* Ref.get(lastLoggedSignatureRef);\n if (signature === previous) return;\n yield* Ref.set(lastLoggedSignatureRef, signature);\n if (errors.size === 0) return;\n yield* logCoalescedBuildErrors(dedupeWatcherErrors(errors));\n });\n\n/**\n * Block until the signal queue has been quiet for `quiescence`. esbuild\n * watchers' `onEnd` events for a single user edit can be spread across\n * hundreds of milliseconds, so a fixed window misses late arrivals.\n * Bounded by `maxWait` so pathological signal floods can't pin the\n * loop forever.\n */\nconst drainUntilQuiescent = (\n signal: Queue.Queue<void>,\n quiescence: Duration.Duration,\n maxWait: Duration.Duration,\n) =>\n Effect.gen(function* () {\n const start = yield* Clock.currentTimeMillis;\n const maxMillis = Duration.toMillis(maxWait);\n yield* Effect.iterate(true as boolean, {\n while: (keepGoing) => keepGoing,\n body: () =>\n Effect.gen(function* () {\n yield* Effect.sleep(quiescence);\n const drained = yield* Queue.takeAll(signal);\n if (Chunk.isEmpty(drained)) return false;\n const now = yield* Clock.currentTimeMillis;\n return now - start < maxMillis;\n }),\n });\n });\n\nconst syncLoop = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n initialFunctionPaths: FunctionPaths.FunctionPaths,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) =>\n Effect.gen(function* () {\n const functionPathsRef = yield* Ref.make(initialFunctionPaths);\n const lastLoggedErrorsRef = yield* Ref.make<string>(\"\");\n\n return yield* Effect.forever(\n Effect.gen(function* () {\n yield* Effect.logDebug(\"Running sync loop…\");\n // Wait for the first signal of a burst, then keep absorbing\n // follow-up signals from other watchers' onEnds until the queue\n // stays quiet for `COALESCE_QUIESCENCE`.\n yield* Queue.take(signal);\n yield* drainUntilQuiescent(\n signal,\n COALESCE_QUIESCENCE,\n COALESCE_MAX_WAIT,\n );\n\n yield* logChangedWatcherErrors(watcherErrorsRef, lastLoggedErrorsRef);\n\n const pending = yield* Ref.getAndSet(pendingRef, pendingInit);\n\n if (!isPendingDirty(pending)) {\n // No-op signal (e.g. a late echo after a previous cycle\n // already drained). Stay silent.\n return;\n }\n\n yield* logPending(\"Dependencies may have changed, reloading…\");\n\n if (pending.specDirty) {\n const current = yield* codegenHandler.pipe(\n Effect.tap(({ functionPaths: nextFunctionPaths }) =>\n Effect.gen(function* () {\n const previous = yield* Ref.get(functionPathsRef);\n yield* logFunctionPathDiff(previous, nextFunctionPaths);\n yield* Ref.set(functionPathsRef, nextFunctionPaths);\n }),\n ),\n CodegenError.catchAndLog,\n );\n if (Option.isNone(current)) {\n return;\n }\n // Drain any stragglers from this cycle's burst (slow watchers\n // whose onEnd fired after the first quiescence) plus, when\n // codegen wrote, the echo signals esbuild emits in response\n // to our writes. Reset `pendingRef` so those drained signals\n // don't carry a dirty flag into the next cycle.\n if (current.value.anyWritesHappened) {\n yield* Effect.sleep(ECHO_COOLDOWN);\n }\n yield* drainUntilQuiescent(\n signal,\n COALESCE_QUIESCENCE,\n COALESCE_MAX_WAIT,\n );\n yield* Ref.set(pendingRef, pendingInit);\n }\n\n const dirtyOptionalFiles = [\n ...(pending.httpDirty\n ? [syncOptionalFile(generateHttp, \"http.ts\")]\n : []),\n ...(pending.cronsDirty\n ? [syncOptionalFile(generateCrons, \"crons.ts\")]\n : []),\n ...(pending.authDirty\n ? [syncOptionalFile(generateAuthConfig, \"auth.config.ts\")]\n : []),\n ];\n\n yield* Array.isNonEmptyReadonlyArray(dirtyOptionalFiles)\n ? Effect.all(dirtyOptionalFiles, { concurrency: \"unbounded\" })\n : Effect.void;\n\n yield* logSuccess(\"Generated files are up-to-date\");\n }),\n );\n });\n\ninterface EntryPoint {\n readonly absolutePath: string;\n readonly displayPath: string;\n readonly pendingKey: PendingKey;\n}\n\n/**\n * Every file whose import graph codegen should react to. Each one becomes\n * its own scoped esbuild watcher; the union of their watches gives us\n * dependency-aware tracking of anything reachable from `confect/`,\n * including files outside `confect/`.\n */\nconst discoverEntryPoints = Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const projectRoot = yield* ProjectRoot.get;\n const confectDirectory = yield* ConfectDirectory.get;\n\n const tryEntry = (relativePath: string, pendingKey: PendingKey) =>\n Effect.gen(function* () {\n const absolutePath = path.join(confectDirectory, relativePath);\n if (!(yield* fs.exists(absolutePath))) {\n return Option.none<EntryPoint>();\n }\n return Option.some<EntryPoint>({\n absolutePath,\n displayPath: path.relative(projectRoot, absolutePath),\n pendingKey,\n });\n });\n\n const fixedEntryOptions = yield* Effect.all([\n tryEntry(GENERATED_SPEC_PATH, \"specDirty\"),\n tryEntry(GENERATED_NODE_SPEC_PATH, \"specDirty\"),\n tryEntry(\"schema.ts\", \"specDirty\"),\n tryEntry(\"http.ts\", \"httpDirty\"),\n tryEntry(\"crons.ts\", \"cronsDirty\"),\n tryEntry(\"auth.ts\", \"authDirty\"),\n ]);\n\n const implRelativePaths = yield* discoverLeafImplFiles;\n const implEntryOptions = yield* Effect.forEach(\n implRelativePaths,\n (relativePath) => tryEntry(relativePath, \"specDirty\"),\n );\n\n return Array.getSomes([...fixedEntryOptions, ...implEntryOptions]);\n});\n\nconst esbuildOptions = (\n entry: EntryPoint,\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) => {\n // First `onEnd` fires when esbuild finishes the watcher's initial\n // build. At startup that's an echo of the just-completed initial\n // codegen pass; for a watcher spawned mid-session (e.g. a newly\n // added impl) it's an echo of the codegen run that triggered the\n // restart. Either way, the entry's contents were already accounted\n // for, so we record any errors but don't flip dirty or push a\n // signal — only genuine subsequent rebuilds should do that.\n const initialBuildSeenRef = Ref.unsafeMake(false);\n return {\n entryPoints: [entry.absolutePath],\n bundle: true,\n write: false,\n metafile: true,\n platform: \"node\" as const,\n format: \"esm\" as const,\n logLevel: \"silent\" as const,\n plugins: [\n absoluteExternalsPlugin,\n {\n name: \"notify-rebuild\",\n setup(build: esbuild.PluginBuild) {\n build.onEnd((result) => {\n Effect.runPromise(\n Effect.gen(function* () {\n const wasInitial = yield* Ref.getAndSet(\n initialBuildSeenRef,\n true,\n );\n const isInitial = !wasInitial;\n yield* Ref.update(watcherErrorsRef, (current) => {\n const next = new Map(current);\n if (result.errors.length > 0) {\n next.set(entry.absolutePath, result.errors);\n } else {\n next.delete(entry.absolutePath);\n }\n return next;\n });\n if (isInitial && result.errors.length === 0) return;\n yield* Ref.update(pendingRef, (p) => ({\n ...p,\n [entry.pendingKey]: true,\n }));\n yield* Queue.offer(signal, undefined);\n }),\n );\n });\n },\n },\n ],\n };\n};\n\nconst createEntryPointWatcher = (\n entry: EntryPoint,\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) =>\n Effect.acquireRelease(\n Effect.promise(async () => {\n const ctx = await esbuild.context(\n esbuildOptions(entry, signal, pendingRef, watcherErrorsRef),\n );\n await ctx.watch();\n return ctx;\n }),\n (ctx) =>\n Effect.gen(function* () {\n yield* Effect.promise(() => ctx.dispose());\n // Clear any errors recorded by this watcher so a disposed\n // watcher can't leave stale errors visible to the sync loop.\n yield* Ref.update(watcherErrorsRef, (current) => {\n if (!current.has(entry.absolutePath)) return current;\n const next = new Map(current);\n next.delete(entry.absolutePath);\n return next;\n });\n yield* Effect.logDebug(\n `esbuild watcher disposed: ${entry.displayPath}`,\n );\n }),\n );\n\n/**\n * Holds one scoped esbuild watcher per entry point and reconciles the set\n * whenever something offers to `restartQueue`. Adding or removing an entry\n * point only spawns/disposes the affected watcher; unchanged entries keep\n * their existing context, so a structural change doesn't churn watchers\n * for unrelated files.\n */\nconst entryPointsWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restartQueue: Queue.Queue<void>,\n watcherErrorsRef: Ref.Ref<WatcherErrors>,\n) =>\n Effect.gen(function* () {\n const parentScope = yield* Effect.scope;\n const scopesRef = yield* Ref.make(new Map<string, Scope.CloseableScope>());\n\n const sync = Effect.gen(function* () {\n const desired = yield* discoverEntryPoints;\n const desiredByPath = new Map(\n desired.map((entryPoint) => [entryPoint.absolutePath, entryPoint]),\n );\n const current = yield* Ref.get(scopesRef);\n\n yield* Effect.forEach(\n Array.fromIterable(current),\n ([absolutePath, childScope]) =>\n desiredByPath.has(absolutePath)\n ? Effect.void\n : Scope.close(childScope, Exit.void).pipe(\n Effect.andThen(\n Ref.update(scopesRef, (scopes) => {\n const updated = new Map(scopes);\n updated.delete(absolutePath);\n return updated;\n }),\n ),\n ),\n );\n\n yield* Effect.forEach(desired, (entry) =>\n Effect.gen(function* () {\n const existing = yield* Ref.get(scopesRef);\n if (existing.has(entry.absolutePath)) return;\n\n const childScope = yield* Scope.fork(\n parentScope,\n ExecutionStrategy.sequential,\n );\n yield* createEntryPointWatcher(\n entry,\n signal,\n pendingRef,\n watcherErrorsRef,\n ).pipe(Scope.extend(childScope));\n yield* Ref.update(scopesRef, (scopes) => {\n const updated = new Map(scopes);\n updated.set(entry.absolutePath, childScope);\n return updated;\n });\n }),\n );\n });\n\n yield* sync;\n\n return yield* Effect.forever(\n Queue.take(restartQueue).pipe(Effect.andThen(sync)),\n );\n });\n\n/**\n * Single recursive `fs.watch` on `confect/`. Flips the matching dirty flag\n * for any change to an entry-point-shaped file (so codegen runs without\n * waiting on a newly spawned esbuild watcher), and offers to\n * `restartQueue` when an entry point is created or removed so the watcher\n * manager picks up the new set.\n */\nconst confectStructureWatcher = (\n signal: Queue.Queue<void>,\n pendingRef: Ref.Ref<Pending>,\n restartQueue: Queue.Queue<void>,\n) =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const confectDirectory = yield* ConfectDirectory.get;\n\n yield* pipe(\n fs.watch(confectDirectory, { recursive: true }),\n Stream.debounce(Duration.millis(200)),\n Stream.runForEach((event) =>\n handleConfectChange({\n relativePath: path.relative(confectDirectory, event.path),\n eventTag: event._tag,\n signal,\n pendingRef,\n restartQueue,\n }),\n ),\n );\n });\n\nconst TOP_LEVEL_OPTIONAL_KEYS: ReadonlyMap<string, PendingKey> = new Map([\n [\"http.ts\", \"httpDirty\"],\n [\"crons.ts\", \"cronsDirty\"],\n [\"auth.ts\", \"authDirty\"],\n]);\n\nconst flipDirtyAndSignal = (\n pendingRef: Ref.Ref<Pending>,\n signal: Queue.Queue<void>,\n key: PendingKey,\n restartQueue: Queue.Queue<void>,\n restart: boolean,\n) =>\n pipe(\n Ref.update(pendingRef, (p) => ({ ...p, [key]: true })),\n Effect.andThen(Queue.offer(signal, undefined)),\n Effect.andThen(\n restart ? Queue.offer(restartQueue, undefined) : Effect.void,\n ),\n );\n\nconst handleConfectChange = ({\n relativePath,\n eventTag,\n signal,\n pendingRef,\n restartQueue,\n}: {\n relativePath: string;\n eventTag: \"Create\" | \"Update\" | \"Remove\";\n signal: Queue.Queue<void>;\n pendingRef: Ref.Ref<Pending>;\n restartQueue: Queue.Queue<void>;\n}) => {\n // _generated/ files are written by codegen itself; reacting to them here\n // would form a loop. The esbuild watchers track the generated specs as\n // entry points, so changes there flow back through `notify-rebuild`.\n if (relativePath.split(/[/\\\\]/).includes(\"_generated\")) {\n return Effect.void;\n }\n\n if (!relativePath.endsWith(\".ts\")) {\n return Effect.void;\n }\n\n const isLifecycleChange = eventTag !== \"Update\";\n\n const topLevelKey = TOP_LEVEL_OPTIONAL_KEYS.get(relativePath);\n if (topLevelKey !== undefined) {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n topLevelKey,\n restartQueue,\n isLifecycleChange,\n );\n }\n\n if (relativePath === \"schema.ts\") {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n \"specDirty\",\n restartQueue,\n isLifecycleChange,\n );\n }\n\n if (isLeafSpecPath(relativePath) || isLeafImplPath(relativePath)) {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n \"specDirty\",\n restartQueue,\n isLifecycleChange,\n );\n }\n\n // Any other `.ts` under `confect/` (helpers like `tables/Notes.ts`).\n // Updates to such files are handled by the esbuild watcher for whichever\n // entry point imports them — its onEnd flips the right dirty flag.\n // Creates are our safety net: when a previously-missing import is added,\n // esbuild may not have its parent directory on a poll path, so we\n // re-run codegen on Create here.\n if (eventTag === \"Create\") {\n return flipDirtyAndSignal(\n pendingRef,\n signal,\n \"specDirty\",\n restartQueue,\n false,\n );\n }\n\n return Effect.void;\n};\n\nconst syncOptionalFile = (generate: typeof generateHttp, convexFile: string) =>\n pipe(\n generate,\n Effect.andThen(\n Option.match({\n onSome: ({ change, convexFilePath }) =>\n Match.value(change).pipe(\n Match.when(\"Unchanged\", () => Effect.void),\n Match.whenOr(\"Added\", \"Modified\", (addedOrModified) =>\n logFileChangeIndented(addedOrModified, convexFilePath),\n ),\n Match.exhaustive,\n ),\n onNone: () =>\n Effect.gen(function* () {\n const fs = yield* FileSystem.FileSystem;\n const path = yield* Path.Path;\n const convexDirectory = yield* ConvexDirectory.get;\n const convexFilePath = path.join(convexDirectory, convexFile);\n\n if (yield* fs.exists(convexFilePath)) {\n yield* fs.remove(convexFilePath);\n yield* logFileChangeIndented(\"Removed\", convexFilePath);\n }\n }),\n }),\n ),\n );\n"],"mappings":"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"}
|
package/dist/package.mjs
CHANGED
package/dist/templates.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { CodeBlockWriter } from "./CodeBlockWriter.mjs";
|
|
2
|
-
import { collectImportBindings } from "./SpecAssemblyNode.mjs";
|
|
2
|
+
import { collectImportBindings, collectLeafPaths } from "./SpecAssemblyNode.mjs";
|
|
3
3
|
import { Array, Effect, Option } from "effect";
|
|
4
4
|
|
|
5
5
|
//#region src/templates.ts
|
|
@@ -211,6 +211,11 @@ const assembledSpec = ({ nodes, runtime }) => Effect.gen(function* () {
|
|
|
211
211
|
const specFactory = runtime === "Convex" ? "Spec.make()" : "Spec.makeNode()";
|
|
212
212
|
const groupFactory = runtime === "Convex" ? "GroupSpec.makeAt" : "GroupSpec.makeNodeAt";
|
|
213
213
|
yield* cbw.write(`export default ${specFactory}`);
|
|
214
|
+
yield* Effect.forEach(collectLeafPaths(nodes), (leaf) => Effect.gen(function* () {
|
|
215
|
+
yield* cbw.write(`.addPath(${leaf.binding.localName}, `);
|
|
216
|
+
yield* cbw.quote(leaf.dotPath);
|
|
217
|
+
yield* cbw.write(")");
|
|
218
|
+
}));
|
|
214
219
|
yield* Effect.forEach(nodes, (node) => writeRootAddAt(cbw, node, groupFactory));
|
|
215
220
|
yield* cbw.write(";");
|
|
216
221
|
yield* cbw.newLine();
|
package/dist/templates.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"templates.mjs","names":[],"sources":["../src/templates.ts"],"sourcesContent":["import { Array, Effect, Option } from \"effect\";\nimport { CodeBlockWriter } from \"./CodeBlockWriter\";\nimport {\n collectImportBindings,\n type SpecAssemblyNode,\n} from \"./SpecAssemblyNode\";\n\nexport const functions = ({\n functionNames,\n registeredFunctionsImportPath,\n useNode = false,\n}: {\n functionNames: string[];\n registeredFunctionsImportPath: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(`\"use node\";`);\n yield* cbw.blankLine();\n }\n\n yield* cbw.writeLine(\n `import registeredFunctions from \"${registeredFunctionsImportPath}\";`,\n );\n yield* cbw.newLine();\n yield* Effect.forEach(functionNames, (functionName) =>\n cbw.writeLine(\n `export const ${functionName} = registeredFunctions.${functionName};`,\n ),\n );\n\n return yield* cbw.toString();\n });\n\nexport const schema = ({ schemaImportPath }: { schemaImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import schemaDefinition from \"${schemaImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(\n `export default schemaDefinition.convexSchemaDefinition;`,\n );\n\n return yield* cbw.toString();\n });\n\nexport const http = ({ httpImportPath }: { httpImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import http from \"${httpImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default http;`);\n\n return yield* cbw.toString();\n });\n\nexport const crons = ({ cronsImportPath }: { cronsImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import crons from \"${cronsImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default crons.convexCronJobs;`);\n\n return yield* cbw.toString();\n });\n\nexport const authConfig = ({ authImportPath }: { authImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import auth from \"${authImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default auth;`);\n\n return yield* cbw.toString();\n });\n\nexport const refs = ({\n specImportPath,\n nodeSpecImportPath,\n}: {\n specImportPath: string;\n nodeSpecImportPath: Option.Option<string>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Refs } from \"@confect/core\";`);\n yield* cbw.writeLine(`import spec from \"${specImportPath}\";`);\n yield* Option.match(nodeSpecImportPath, {\n onNone: () => Effect.void,\n onSome: (nodeSpecImportPath_) =>\n cbw.writeLine(`import nodeSpec from \"${nodeSpecImportPath_}\";`),\n });\n yield* cbw.blankLine();\n yield* cbw.writeLine(\n Option.match(nodeSpecImportPath, {\n onSome: () => `export default Refs.make(spec, nodeSpec);`,\n onNone: () => `export default Refs.make(spec);`,\n }),\n );\n\n return yield* cbw.toString();\n });\n\nexport const api = ({\n schemaImportPath,\n specImportPath,\n}: {\n schemaImportPath: string;\n specImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Api } from \"@confect/server\";`);\n yield* cbw.writeLine(`import schema from \"${schemaImportPath}\";`);\n yield* cbw.writeLine(`import spec from \"${specImportPath}\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`export default Api.make(schema, spec);`);\n\n return yield* cbw.toString();\n });\n\nexport const nodeApi = ({\n schemaImportPath,\n nodeSpecImportPath,\n}: {\n schemaImportPath: string;\n nodeSpecImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Api } from \"@confect/server\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`import schema from \"${schemaImportPath}\";`);\n yield* cbw.writeLine(`import nodeSpec from \"${nodeSpecImportPath}\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`export default Api.make(schema, nodeSpec);`);\n\n return yield* cbw.toString();\n });\n\nexport const registeredFunctionsForGroup = ({\n apiImportPath,\n groupPathDot,\n implImportPath,\n layerExportName,\n useNode = false,\n}: {\n apiImportPath: string;\n groupPathDot: string;\n implImportPath: string;\n layerExportName: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(\n `import { RegisteredFunctions } from \"@confect/server\";`,\n );\n yield* cbw.writeLine(\n `import { RegisteredNodeFunction } from \"@confect/server/node\";`,\n );\n } else {\n yield* cbw.writeLine(\n `import { RegisteredConvexFunction, RegisteredFunctions } from \"@confect/server\";`,\n );\n }\n\n yield* cbw.writeLine(`import api from \"${apiImportPath}\";`);\n yield* cbw.writeLine(`import ${layerExportName} from \"${implImportPath}\";`);\n yield* cbw.blankLine();\n const quotedGroupPath = `\"${groupPathDot.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n yield* cbw.writeLine(\n useNode\n ? `export default RegisteredFunctions.buildForGroup(api, ${quotedGroupPath}, ${layerExportName}, RegisteredNodeFunction.make);`\n : `export default RegisteredFunctions.buildForGroup(api, ${quotedGroupPath}, ${layerExportName}, RegisteredConvexFunction.make);`,\n );\n\n return yield* cbw.toString();\n });\n\nexport const services = ({ schemaImportPath }: { schemaImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n // Imports\n yield* cbw.writeLine(\"import {\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx as ActionCtx_,\");\n yield* cbw.writeLine(\"ActionRunner as ActionRunner_,\");\n yield* cbw.writeLine(\"Auth as Auth_,\");\n yield* cbw.writeLine(\"type DataModel,\");\n yield* cbw.writeLine(\"DatabaseReader as DatabaseReader_,\");\n yield* cbw.writeLine(\"DatabaseWriter as DatabaseWriter_,\");\n yield* cbw.writeLine(\"MutationCtx as MutationCtx_,\");\n yield* cbw.writeLine(\"MutationRunner as MutationRunner_,\");\n yield* cbw.writeLine(\"QueryCtx as QueryCtx_,\");\n yield* cbw.writeLine(\"QueryRunner as QueryRunner_,\");\n yield* cbw.writeLine(\"Scheduler as Scheduler_,\");\n yield* cbw.writeLine(\"StorageActionWriter as StorageActionWriter_,\");\n yield* cbw.writeLine(\"StorageReader as StorageReader_,\");\n yield* cbw.writeLine(\"StorageWriter as StorageWriter_,\");\n yield* cbw.writeLine(\"VectorSearch as VectorSearch_,\");\n }),\n );\n yield* cbw.writeLine(`} from \"@confect/server\";`);\n yield* cbw.writeLine(\n `import type schemaDefinition from \"${schemaImportPath}\";`,\n );\n yield* cbw.blankLine();\n\n // Auth\n yield* cbw.writeLine(\"export const Auth = Auth_.Auth;\");\n yield* cbw.writeLine(\"export type Auth = typeof Auth.Identifier;\");\n yield* cbw.blankLine();\n\n // Scheduler\n yield* cbw.writeLine(\"export const Scheduler = Scheduler_.Scheduler;\");\n yield* cbw.writeLine(\n \"export type Scheduler = typeof Scheduler.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageReader\n yield* cbw.writeLine(\n \"export const StorageReader = StorageReader_.StorageReader;\",\n );\n yield* cbw.writeLine(\n \"export type StorageReader = typeof StorageReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageWriter\n yield* cbw.writeLine(\n \"export const StorageWriter = StorageWriter_.StorageWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageWriter = typeof StorageWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageActionWriter\n yield* cbw.writeLine(\n \"export const StorageActionWriter = StorageActionWriter_.StorageActionWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageActionWriter = typeof StorageActionWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // VectorSearch\n yield* cbw.writeLine(\"export const VectorSearch =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"VectorSearch_.VectorSearch<DataModel.FromSchema<typeof schemaDefinition>>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type VectorSearch = typeof VectorSearch.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseReader\n yield* cbw.writeLine(\"export const DatabaseReader =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseReader_.DatabaseReader<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseReader = typeof DatabaseReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseWriter\n yield* cbw.writeLine(\"export const DatabaseWriter =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseWriter_.DatabaseWriter<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseWriter = typeof DatabaseWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryRunner\n yield* cbw.writeLine(\n \"export const QueryRunner = QueryRunner_.QueryRunner;\",\n );\n yield* cbw.writeLine(\n \"export type QueryRunner = typeof QueryRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // MutationRunner\n yield* cbw.writeLine(\n \"export const MutationRunner = MutationRunner_.MutationRunner;\",\n );\n yield* cbw.writeLine(\n \"export type MutationRunner = typeof MutationRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionRunner\n yield* cbw.writeLine(\n \"export const ActionRunner = ActionRunner_.ActionRunner;\",\n );\n yield* cbw.writeLine(\n \"export type ActionRunner = typeof ActionRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryCtx\n yield* cbw.writeLine(\"export const QueryCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"QueryCtx_.QueryCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\"export type QueryCtx = typeof QueryCtx.Identifier;\");\n yield* cbw.blankLine();\n\n // MutationCtx\n yield* cbw.writeLine(\"export const MutationCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"MutationCtx_.MutationCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type MutationCtx = typeof MutationCtx.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionCtx\n yield* cbw.writeLine(\"export const ActionCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx_.ActionCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type ActionCtx = typeof ActionCtx.Identifier;\",\n );\n\n return yield* cbw.toString();\n });\n\nconst writeChildAddGroupAt = (\n cbw: CodeBlockWriter,\n child: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addGroupAt(\");\n yield* cbw.quote(child.segment);\n yield* cbw.write(\", \");\n yield* writeGroupAssembly(cbw, child, groupFactory);\n yield* cbw.write(\")\");\n });\n\nconst writeGroupFactoryCall = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(groupFactory);\n yield* cbw.write(\"(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\")\");\n\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n });\n\nconst writeGroupAssembly: (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n) => Effect.Effect<void> = (cbw, node, groupFactory) =>\n Option.match(node.importBinding, {\n onNone: () => writeGroupFactoryCall(cbw, node, groupFactory),\n onSome: (binding) =>\n Effect.gen(function* () {\n yield* cbw.write(binding.localName);\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n }),\n });\n\nconst writeRootAddAt = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addAt(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\", \");\n\n yield* writeGroupAssembly(cbw, node, groupFactory);\n\n yield* cbw.write(\")\");\n });\n\nexport const assembledSpec = ({\n nodes,\n runtime,\n}: {\n nodes: ReadonlyArray<SpecAssemblyNode>;\n runtime: \"Convex\" | \"Node\";\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n const nodeRequiresGroupFactory = (node: SpecAssemblyNode): boolean =>\n Option.isNone(node.importBinding) ||\n Array.some(node.children, nodeRequiresGroupFactory);\n\n const needsGroupSpec = Array.some(nodes, nodeRequiresGroupFactory);\n yield* cbw.writeLine(\n needsGroupSpec\n ? `import { GroupSpec, Spec } from \"@confect/core\";`\n : `import { Spec } from \"@confect/core\";`,\n );\n\n yield* Effect.forEach(collectImportBindings(nodes), (binding) =>\n cbw.writeLine(\n `import ${binding.localName} from \"${binding.importPath}\";`,\n ),\n );\n\n yield* cbw.blankLine();\n\n const specFactory =\n runtime === \"Convex\" ? \"Spec.make()\" : \"Spec.makeNode()\";\n const groupFactory =\n runtime === \"Convex\" ? \"GroupSpec.makeAt\" : \"GroupSpec.makeNodeAt\";\n\n yield* cbw.write(`export default ${specFactory}`);\n yield* Effect.forEach(nodes, (node) =>\n writeRootAddAt(cbw, node, groupFactory),\n );\n yield* cbw.write(\";\");\n yield* cbw.newLine();\n\n return yield* cbw.toString();\n });\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"templates.mjs","names":[],"sources":["../src/templates.ts"],"sourcesContent":["import { Array, Effect, Option } from \"effect\";\nimport { CodeBlockWriter } from \"./CodeBlockWriter\";\nimport {\n collectImportBindings,\n collectLeafPaths,\n type SpecAssemblyNode,\n} from \"./SpecAssemblyNode\";\n\nexport const functions = ({\n functionNames,\n registeredFunctionsImportPath,\n useNode = false,\n}: {\n functionNames: string[];\n registeredFunctionsImportPath: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(`\"use node\";`);\n yield* cbw.blankLine();\n }\n\n yield* cbw.writeLine(\n `import registeredFunctions from \"${registeredFunctionsImportPath}\";`,\n );\n yield* cbw.newLine();\n yield* Effect.forEach(functionNames, (functionName) =>\n cbw.writeLine(\n `export const ${functionName} = registeredFunctions.${functionName};`,\n ),\n );\n\n return yield* cbw.toString();\n });\n\nexport const schema = ({ schemaImportPath }: { schemaImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import schemaDefinition from \"${schemaImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(\n `export default schemaDefinition.convexSchemaDefinition;`,\n );\n\n return yield* cbw.toString();\n });\n\nexport const http = ({ httpImportPath }: { httpImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import http from \"${httpImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default http;`);\n\n return yield* cbw.toString();\n });\n\nexport const crons = ({ cronsImportPath }: { cronsImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import crons from \"${cronsImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default crons.convexCronJobs;`);\n\n return yield* cbw.toString();\n });\n\nexport const authConfig = ({ authImportPath }: { authImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import auth from \"${authImportPath}\";`);\n yield* cbw.newLine();\n yield* cbw.writeLine(`export default auth;`);\n\n return yield* cbw.toString();\n });\n\nexport const refs = ({\n specImportPath,\n nodeSpecImportPath,\n}: {\n specImportPath: string;\n nodeSpecImportPath: Option.Option<string>;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Refs } from \"@confect/core\";`);\n yield* cbw.writeLine(`import spec from \"${specImportPath}\";`);\n yield* Option.match(nodeSpecImportPath, {\n onNone: () => Effect.void,\n onSome: (nodeSpecImportPath_) =>\n cbw.writeLine(`import nodeSpec from \"${nodeSpecImportPath_}\";`),\n });\n yield* cbw.blankLine();\n yield* cbw.writeLine(\n Option.match(nodeSpecImportPath, {\n onSome: () => `export default Refs.make(spec, nodeSpec);`,\n onNone: () => `export default Refs.make(spec);`,\n }),\n );\n\n return yield* cbw.toString();\n });\n\nexport const api = ({\n schemaImportPath,\n specImportPath,\n}: {\n schemaImportPath: string;\n specImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Api } from \"@confect/server\";`);\n yield* cbw.writeLine(`import schema from \"${schemaImportPath}\";`);\n yield* cbw.writeLine(`import spec from \"${specImportPath}\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`export default Api.make(schema, spec);`);\n\n return yield* cbw.toString();\n });\n\nexport const nodeApi = ({\n schemaImportPath,\n nodeSpecImportPath,\n}: {\n schemaImportPath: string;\n nodeSpecImportPath: string;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n yield* cbw.writeLine(`import { Api } from \"@confect/server\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`import schema from \"${schemaImportPath}\";`);\n yield* cbw.writeLine(`import nodeSpec from \"${nodeSpecImportPath}\";`);\n yield* cbw.blankLine();\n yield* cbw.writeLine(`export default Api.make(schema, nodeSpec);`);\n\n return yield* cbw.toString();\n });\n\nexport const registeredFunctionsForGroup = ({\n apiImportPath,\n groupPathDot,\n implImportPath,\n layerExportName,\n useNode = false,\n}: {\n apiImportPath: string;\n groupPathDot: string;\n implImportPath: string;\n layerExportName: string;\n useNode?: boolean;\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n if (useNode) {\n yield* cbw.writeLine(\n `import { RegisteredFunctions } from \"@confect/server\";`,\n );\n yield* cbw.writeLine(\n `import { RegisteredNodeFunction } from \"@confect/server/node\";`,\n );\n } else {\n yield* cbw.writeLine(\n `import { RegisteredConvexFunction, RegisteredFunctions } from \"@confect/server\";`,\n );\n }\n\n yield* cbw.writeLine(`import api from \"${apiImportPath}\";`);\n yield* cbw.writeLine(`import ${layerExportName} from \"${implImportPath}\";`);\n yield* cbw.blankLine();\n const quotedGroupPath = `\"${groupPathDot.replace(/\\\\/g, \"\\\\\\\\\").replace(/\"/g, '\\\\\"')}\"`;\n yield* cbw.writeLine(\n useNode\n ? `export default RegisteredFunctions.buildForGroup(api, ${quotedGroupPath}, ${layerExportName}, RegisteredNodeFunction.make);`\n : `export default RegisteredFunctions.buildForGroup(api, ${quotedGroupPath}, ${layerExportName}, RegisteredConvexFunction.make);`,\n );\n\n return yield* cbw.toString();\n });\n\nexport const services = ({ schemaImportPath }: { schemaImportPath: string }) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n // Imports\n yield* cbw.writeLine(\"import {\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx as ActionCtx_,\");\n yield* cbw.writeLine(\"ActionRunner as ActionRunner_,\");\n yield* cbw.writeLine(\"Auth as Auth_,\");\n yield* cbw.writeLine(\"type DataModel,\");\n yield* cbw.writeLine(\"DatabaseReader as DatabaseReader_,\");\n yield* cbw.writeLine(\"DatabaseWriter as DatabaseWriter_,\");\n yield* cbw.writeLine(\"MutationCtx as MutationCtx_,\");\n yield* cbw.writeLine(\"MutationRunner as MutationRunner_,\");\n yield* cbw.writeLine(\"QueryCtx as QueryCtx_,\");\n yield* cbw.writeLine(\"QueryRunner as QueryRunner_,\");\n yield* cbw.writeLine(\"Scheduler as Scheduler_,\");\n yield* cbw.writeLine(\"StorageActionWriter as StorageActionWriter_,\");\n yield* cbw.writeLine(\"StorageReader as StorageReader_,\");\n yield* cbw.writeLine(\"StorageWriter as StorageWriter_,\");\n yield* cbw.writeLine(\"VectorSearch as VectorSearch_,\");\n }),\n );\n yield* cbw.writeLine(`} from \"@confect/server\";`);\n yield* cbw.writeLine(\n `import type schemaDefinition from \"${schemaImportPath}\";`,\n );\n yield* cbw.blankLine();\n\n // Auth\n yield* cbw.writeLine(\"export const Auth = Auth_.Auth;\");\n yield* cbw.writeLine(\"export type Auth = typeof Auth.Identifier;\");\n yield* cbw.blankLine();\n\n // Scheduler\n yield* cbw.writeLine(\"export const Scheduler = Scheduler_.Scheduler;\");\n yield* cbw.writeLine(\n \"export type Scheduler = typeof Scheduler.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageReader\n yield* cbw.writeLine(\n \"export const StorageReader = StorageReader_.StorageReader;\",\n );\n yield* cbw.writeLine(\n \"export type StorageReader = typeof StorageReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageWriter\n yield* cbw.writeLine(\n \"export const StorageWriter = StorageWriter_.StorageWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageWriter = typeof StorageWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // StorageActionWriter\n yield* cbw.writeLine(\n \"export const StorageActionWriter = StorageActionWriter_.StorageActionWriter;\",\n );\n yield* cbw.writeLine(\n \"export type StorageActionWriter = typeof StorageActionWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // VectorSearch\n yield* cbw.writeLine(\"export const VectorSearch =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"VectorSearch_.VectorSearch<DataModel.FromSchema<typeof schemaDefinition>>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type VectorSearch = typeof VectorSearch.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseReader\n yield* cbw.writeLine(\"export const DatabaseReader =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseReader_.DatabaseReader<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseReader = typeof DatabaseReader.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // DatabaseWriter\n yield* cbw.writeLine(\"export const DatabaseWriter =\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DatabaseWriter_.DatabaseWriter<typeof schemaDefinition>();\",\n ),\n );\n yield* cbw.writeLine(\n \"export type DatabaseWriter = typeof DatabaseWriter.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryRunner\n yield* cbw.writeLine(\n \"export const QueryRunner = QueryRunner_.QueryRunner;\",\n );\n yield* cbw.writeLine(\n \"export type QueryRunner = typeof QueryRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // MutationRunner\n yield* cbw.writeLine(\n \"export const MutationRunner = MutationRunner_.MutationRunner;\",\n );\n yield* cbw.writeLine(\n \"export type MutationRunner = typeof MutationRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionRunner\n yield* cbw.writeLine(\n \"export const ActionRunner = ActionRunner_.ActionRunner;\",\n );\n yield* cbw.writeLine(\n \"export type ActionRunner = typeof ActionRunner.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // QueryCtx\n yield* cbw.writeLine(\"export const QueryCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"QueryCtx_.QueryCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\"export type QueryCtx = typeof QueryCtx.Identifier;\");\n yield* cbw.blankLine();\n\n // MutationCtx\n yield* cbw.writeLine(\"export const MutationCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"MutationCtx_.MutationCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type MutationCtx = typeof MutationCtx.Identifier;\",\n );\n yield* cbw.blankLine();\n\n // ActionCtx\n yield* cbw.writeLine(\"export const ActionCtx =\");\n yield* cbw.indent(\n Effect.gen(function* () {\n yield* cbw.writeLine(\"ActionCtx_.ActionCtx<\");\n yield* cbw.indent(\n cbw.writeLine(\n \"DataModel.ToConvex<DataModel.FromSchema<typeof schemaDefinition>>\",\n ),\n );\n yield* cbw.writeLine(\">();\");\n }),\n );\n yield* cbw.writeLine(\n \"export type ActionCtx = typeof ActionCtx.Identifier;\",\n );\n\n return yield* cbw.toString();\n });\n\nconst writeChildAddGroupAt = (\n cbw: CodeBlockWriter,\n child: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addGroupAt(\");\n yield* cbw.quote(child.segment);\n yield* cbw.write(\", \");\n yield* writeGroupAssembly(cbw, child, groupFactory);\n yield* cbw.write(\")\");\n });\n\nconst writeGroupFactoryCall = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(groupFactory);\n yield* cbw.write(\"(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\")\");\n\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n });\n\nconst writeGroupAssembly: (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n) => Effect.Effect<void> = (cbw, node, groupFactory) =>\n Option.match(node.importBinding, {\n onNone: () => writeGroupFactoryCall(cbw, node, groupFactory),\n onSome: (binding) =>\n Effect.gen(function* () {\n yield* cbw.write(binding.localName);\n yield* Effect.forEach(node.children, (child) =>\n writeChildAddGroupAt(cbw, child, groupFactory),\n );\n }),\n });\n\nconst writeRootAddAt = (\n cbw: CodeBlockWriter,\n node: SpecAssemblyNode,\n groupFactory: string,\n): Effect.Effect<void> =>\n Effect.gen(function* () {\n yield* cbw.write(\".addAt(\");\n yield* cbw.quote(node.segment);\n yield* cbw.write(\", \");\n\n yield* writeGroupAssembly(cbw, node, groupFactory);\n\n yield* cbw.write(\")\");\n });\n\nexport const assembledSpec = ({\n nodes,\n runtime,\n}: {\n nodes: ReadonlyArray<SpecAssemblyNode>;\n runtime: \"Convex\" | \"Node\";\n}) =>\n Effect.gen(function* () {\n const cbw = new CodeBlockWriter({ indentNumberOfSpaces: 2 });\n\n const nodeRequiresGroupFactory = (node: SpecAssemblyNode): boolean =>\n Option.isNone(node.importBinding) ||\n Array.some(node.children, nodeRequiresGroupFactory);\n\n const needsGroupSpec = Array.some(nodes, nodeRequiresGroupFactory);\n yield* cbw.writeLine(\n needsGroupSpec\n ? `import { GroupSpec, Spec } from \"@confect/core\";`\n : `import { Spec } from \"@confect/core\";`,\n );\n\n yield* Effect.forEach(collectImportBindings(nodes), (binding) =>\n cbw.writeLine(\n `import ${binding.localName} from \"${binding.importPath}\";`,\n ),\n );\n\n yield* cbw.blankLine();\n\n const specFactory =\n runtime === \"Convex\" ? \"Spec.make()\" : \"Spec.makeNode()\";\n const groupFactory =\n runtime === \"Convex\" ? \"GroupSpec.makeAt\" : \"GroupSpec.makeNodeAt\";\n\n yield* cbw.write(`export default ${specFactory}`);\n yield* Effect.forEach(collectLeafPaths(nodes), (leaf) =>\n Effect.gen(function* () {\n yield* cbw.write(`.addPath(${leaf.binding.localName}, `);\n yield* cbw.quote(leaf.dotPath);\n yield* cbw.write(\")\");\n }),\n );\n yield* Effect.forEach(nodes, (node) =>\n writeRootAddAt(cbw, node, groupFactory),\n );\n yield* cbw.write(\";\");\n yield* cbw.newLine();\n\n return yield* cbw.toString();\n });\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@confect/cli",
|
|
3
|
-
"version": "9.0.0-next.
|
|
3
|
+
"version": "9.0.0-next.4",
|
|
4
4
|
"description": "Developer tooling for codegen and sync",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -65,8 +65,8 @@
|
|
|
65
65
|
},
|
|
66
66
|
"peerDependencies": {
|
|
67
67
|
"effect": "^3.21.2",
|
|
68
|
-
"@confect/core": "^9.0.0-next.
|
|
69
|
-
"@confect/server": "^9.0.0-next.
|
|
68
|
+
"@confect/core": "^9.0.0-next.4",
|
|
69
|
+
"@confect/server": "^9.0.0-next.4"
|
|
70
70
|
},
|
|
71
71
|
"engines": {
|
|
72
72
|
"node": ">=22",
|
package/src/Bundler.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createRequire } from "node:module";
|
|
2
1
|
import { pathToFileURL } from "node:url";
|
|
3
2
|
import { Path } from "@effect/platform";
|
|
4
3
|
import { Array, Effect, Option, pipe } from "effect";
|
|
@@ -10,39 +9,61 @@ export interface Bundled {
|
|
|
10
9
|
readonly metafile: esbuild.Metafile;
|
|
11
10
|
}
|
|
12
11
|
|
|
13
|
-
|
|
14
|
-
"
|
|
15
|
-
"
|
|
16
|
-
"
|
|
17
|
-
"@effect/*",
|
|
18
|
-
];
|
|
12
|
+
const isRelativeOrAbsolutePath = (importPath: string) =>
|
|
13
|
+
importPath.startsWith("./") ||
|
|
14
|
+
importPath.startsWith("../") ||
|
|
15
|
+
importPath.startsWith("/");
|
|
19
16
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
// Recursion guard for `absoluteExternalsPlugin`. When the plugin asks esbuild
|
|
18
|
+
// to resolve a bare specifier via `build.resolve(...)`, esbuild invokes every
|
|
19
|
+
// registered `onResolve` hook again for that same specifier — including this
|
|
20
|
+
// one. The flag (carried through the recursive call via `pluginData`) tells
|
|
21
|
+
// the recursive invocation to skip rewriting and fall through to esbuild's
|
|
22
|
+
// built-in resolver, which is what we wanted from `build.resolve` in the
|
|
23
|
+
// first place.
|
|
24
|
+
const PLUGIN_DATA_SKIP = Symbol("absolute-externals.skip");
|
|
27
25
|
|
|
26
|
+
/**
|
|
27
|
+
* Mark every bare-specifier import as external and rewrite it to an absolute
|
|
28
|
+
* `file://` URL. Resolution is delegated to esbuild's own resolver via
|
|
29
|
+
* `build.resolve(...)`, which honors each package's `exports` map (preferring
|
|
30
|
+
* the `import` condition under `format: "esm"`) and falls back to
|
|
31
|
+
* `module`/`main` exactly the way Node's ESM resolution algorithm does.
|
|
32
|
+
*
|
|
33
|
+
* Bundles produced with this plugin are loaded via a data URL `import(...)`
|
|
34
|
+
* (see {@link bundle}); rewriting bare externals to absolute file URLs is what
|
|
35
|
+
* makes them resolvable at runtime, since a data URL has no parent file from
|
|
36
|
+
* which a bare specifier could be resolved.
|
|
37
|
+
*
|
|
38
|
+
* Relative/absolute-path imports are left to esbuild to bundle as usual, and
|
|
39
|
+
* `node:*` built-ins are passed through unchanged.
|
|
40
|
+
*/
|
|
28
41
|
export const absoluteExternalsPlugin: esbuild.Plugin = {
|
|
29
42
|
name: "absolute-externals",
|
|
30
43
|
setup(build) {
|
|
31
44
|
build.onResolve({ filter: /.*/ }, async (args) => {
|
|
45
|
+
if (args.pluginData?.[PLUGIN_DATA_SKIP]) return;
|
|
32
46
|
if (args.kind !== "import-statement" && args.kind !== "dynamic-import")
|
|
33
47
|
return;
|
|
34
|
-
if (
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
48
|
+
if (isRelativeOrAbsolutePath(args.path)) return;
|
|
49
|
+
if (args.path.startsWith("node:")) {
|
|
50
|
+
return { path: args.path, external: true };
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const resolved = await build.resolve(args.path, {
|
|
54
|
+
kind: args.kind,
|
|
55
|
+
resolveDir: args.resolveDir,
|
|
56
|
+
pluginData: { [PLUGIN_DATA_SKIP]: true },
|
|
57
|
+
});
|
|
58
|
+
|
|
59
|
+
if (resolved.errors.length > 0) {
|
|
60
|
+
return { errors: resolved.errors, warnings: resolved.warnings };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
path: pathToFileURL(resolved.path).href,
|
|
65
|
+
external: true,
|
|
66
|
+
};
|
|
46
67
|
});
|
|
47
68
|
},
|
|
48
69
|
};
|
package/src/SpecAssemblyNode.ts
CHANGED
|
@@ -82,3 +82,39 @@ export const collectImportBindings = (
|
|
|
82
82
|
Array.map(([, binding]) => binding),
|
|
83
83
|
Array.sortBy(Order.mapInput(Order.string, (binding) => binding.localName)),
|
|
84
84
|
);
|
|
85
|
+
|
|
86
|
+
export interface SpecLeafPath {
|
|
87
|
+
readonly binding: SpecImportBinding;
|
|
88
|
+
readonly dotPath: string;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const leafPathsForNode = (
|
|
92
|
+
node: SpecAssemblyNode,
|
|
93
|
+
ancestorSegments: ReadonlyArray<string>,
|
|
94
|
+
): ReadonlyArray<SpecLeafPath> => {
|
|
95
|
+
const segments = [...ancestorSegments, node.segment];
|
|
96
|
+
const childPaths = Array.flatMap(node.children, (child) =>
|
|
97
|
+
leafPathsForNode(child, segments),
|
|
98
|
+
);
|
|
99
|
+
return Option.match(node.importBinding, {
|
|
100
|
+
onNone: () => childPaths,
|
|
101
|
+
onSome: (binding) =>
|
|
102
|
+
Array.prepend(childPaths, { binding, dotPath: segments.join(".") }),
|
|
103
|
+
});
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Walk the assembly tree and produce one entry per leaf spec, pairing its
|
|
108
|
+
* import binding with the full dot-path codegen will register via
|
|
109
|
+
* `Spec.addPath`. Ordering matches `collectImportBindings` (sorted by the
|
|
110
|
+
* binding's local name) so the generated file is stable across runs.
|
|
111
|
+
*/
|
|
112
|
+
export const collectLeafPaths = (
|
|
113
|
+
nodes: ReadonlyArray<SpecAssemblyNode>,
|
|
114
|
+
): ReadonlyArray<SpecLeafPath> =>
|
|
115
|
+
pipe(
|
|
116
|
+
Array.flatMap(nodes, (node) => leafPathsForNode(node, [])),
|
|
117
|
+
Array.sortBy(
|
|
118
|
+
Order.mapInput(Order.string, (entry) => entry.binding.localName),
|
|
119
|
+
),
|
|
120
|
+
);
|
package/src/confect/dev.ts
CHANGED
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
} from "effect";
|
|
25
25
|
import * as esbuild from "esbuild";
|
|
26
26
|
import { logCoalescedBuildErrors } from "../BuildError";
|
|
27
|
-
import { absoluteExternalsPlugin
|
|
27
|
+
import { absoluteExternalsPlugin } from "../Bundler";
|
|
28
28
|
import * as CodegenError from "../CodegenError";
|
|
29
29
|
import { ConfectDirectory } from "../ConfectDirectory";
|
|
30
30
|
import { ConvexDirectory } from "../ConvexDirectory";
|
|
@@ -450,7 +450,6 @@ const esbuildOptions = (
|
|
|
450
450
|
platform: "node" as const,
|
|
451
451
|
format: "esm" as const,
|
|
452
452
|
logLevel: "silent" as const,
|
|
453
|
-
external: EXTERNAL_PACKAGES,
|
|
454
453
|
plugins: [
|
|
455
454
|
absoluteExternalsPlugin,
|
|
456
455
|
{
|
package/src/templates.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { Array, Effect, Option } from "effect";
|
|
|
2
2
|
import { CodeBlockWriter } from "./CodeBlockWriter";
|
|
3
3
|
import {
|
|
4
4
|
collectImportBindings,
|
|
5
|
+
collectLeafPaths,
|
|
5
6
|
type SpecAssemblyNode,
|
|
6
7
|
} from "./SpecAssemblyNode";
|
|
7
8
|
|
|
@@ -472,6 +473,13 @@ export const assembledSpec = ({
|
|
|
472
473
|
runtime === "Convex" ? "GroupSpec.makeAt" : "GroupSpec.makeNodeAt";
|
|
473
474
|
|
|
474
475
|
yield* cbw.write(`export default ${specFactory}`);
|
|
476
|
+
yield* Effect.forEach(collectLeafPaths(nodes), (leaf) =>
|
|
477
|
+
Effect.gen(function* () {
|
|
478
|
+
yield* cbw.write(`.addPath(${leaf.binding.localName}, `);
|
|
479
|
+
yield* cbw.quote(leaf.dotPath);
|
|
480
|
+
yield* cbw.write(")");
|
|
481
|
+
}),
|
|
482
|
+
);
|
|
475
483
|
yield* Effect.forEach(nodes, (node) =>
|
|
476
484
|
writeRootAddAt(cbw, node, groupFactory),
|
|
477
485
|
);
|