@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 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 EXTERNAL_PACKAGES = [
10
- "@confect/core",
11
- "@confect/server",
12
- "effect",
13
- "@effect/*"
14
- ];
15
- const isExternalImport = (path) => EXTERNAL_PACKAGES.some((p) => {
16
- if (p.endsWith("/*")) return path.startsWith(p.slice(0, -1));
17
- return path === p || path.startsWith(p + "/");
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 (!isExternalImport(args.path)) return;
25
- const parentFile = pathToFileURL(args.resolveDir + "/_").href;
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(createRequire(parentFile).resolve(args.path)).href,
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 { EXTERNAL_PACKAGES, absoluteExternalsPlugin, bundle, directlyImports };
109
+ export { absoluteExternalsPlugin, bundle, directlyImports };
91
110
  //# sourceMappingURL=Bundler.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"Bundler.mjs","names":[],"sources":["../src/Bundler.ts"],"sourcesContent":["import { createRequire } from \"node:module\";\nimport { 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\nexport const EXTERNAL_PACKAGES = [\n \"@confect/core\",\n \"@confect/server\",\n \"effect\",\n \"@effect/*\",\n];\n\nconst isExternalImport = (path: string) =>\n EXTERNAL_PACKAGES.some((p) => {\n if (p.endsWith(\"/*\")) {\n return path.startsWith(p.slice(0, -1));\n }\n return path === p || path.startsWith(p + \"/\");\n });\n\nexport const absoluteExternalsPlugin: esbuild.Plugin = {\n name: \"absolute-externals\",\n setup(build) {\n build.onResolve({ filter: /.*/ }, async (args) => {\n if (args.kind !== \"import-statement\" && args.kind !== \"dynamic-import\")\n return;\n if (!isExternalImport(args.path)) return;\n // `import.meta.resolve`'s second argument is silently ignored in modern\n // Node, so resolution would always walk up from the CLI's bundled file\n // (`packages/cli/dist/utils.mjs`) instead of from the user's project.\n // Use `createRequire` keyed on the importing file's directory so we\n // resolve out of *their* `node_modules`. The synthetic filename is just\n // a CommonJS resolution anchor; the file does not need to exist.\n const parentFile = pathToFileURL(args.resolveDir + \"/_\").href;\n const require_ = createRequire(parentFile);\n const resolvedPath = require_.resolve(args.path);\n const resolved = pathToFileURL(resolvedPath).href;\n return { path: resolved, external: true };\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":";;;;;;;;AAYA,MAAa,oBAAoB;CAC/B;CACA;CACA;CACA;CACD;AAED,MAAM,oBAAoB,SACxB,kBAAkB,MAAM,MAAM;AAC5B,KAAI,EAAE,SAAS,KAAK,CAClB,QAAO,KAAK,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC;AAExC,QAAO,SAAS,KAAK,KAAK,WAAW,IAAI,IAAI;EAC7C;AAEJ,MAAa,0BAA0C;CACrD,MAAM;CACN,MAAM,OAAO;AACX,QAAM,UAAU,EAAE,QAAQ,MAAM,EAAE,OAAO,SAAS;AAChD,OAAI,KAAK,SAAS,sBAAsB,KAAK,SAAS,iBACpD;AACF,OAAI,CAAC,iBAAiB,KAAK,KAAK,CAAE;GAOlC,MAAM,aAAa,cAAc,KAAK,aAAa,KAAK,CAAC;AAIzD,UAAO;IAAE,MADQ,cAFA,cAAc,WAAW,CACZ,QAAQ,KAAK,KAAK,CACJ,CAAC;IACpB,UAAU;IAAM;IACzC;;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"}
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"}
@@ -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 { EXTERNAL_PACKAGES, absoluteExternalsPlugin } from "../Bundler.mjs";
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) {
@@ -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":";;;;;;;;;;;;;;;;;;AA+CA,MAAM,sBAAsB;AAC5B,MAAM,2BAA2B;AASjC,MAAM,sBAAsB,SAAS,OAAO,IAAI;AAIhD,MAAM,oBAAoB,SAAS,QAAQ,EAAE;AAK7C,MAAM,gBAAgB,SAAS,OAAO,IAAI;AAE1C,MAAM,mCAAiD,KAAK,QAAQ,OAAO,CAAC;AAW5E,MAAM,cAAuB;CAC3B,WAAW;CACX,WAAW;CACX,YAAY;CACZ,WAAW;CACZ;AAED,MAAM,kBAAkB,MACtB,EAAE,aAAa,EAAE,aAAa,EAAE,cAAc,EAAE;AAIlD,MAAM,qCAAoC,IAAI,KAAK;AAEnD,MAAM,cAAc,WAClB,MAAM,MAAM,OAAO,CAAC,KAClB,MAAM,KAAK,gBAAgB;CAAE,MAAM;CAAK,OAAO,KAAK;CAAO,EAAE,EAC7D,MAAM,KAAK,kBAAkB;CAAE,MAAM;CAAK,OAAO,KAAK;CAAK,EAAE,EAC7D,MAAM,KAAK,mBAAmB;CAAE,MAAM;CAAK,OAAO,KAAK;CAAQ,EAAE,EACjE,MAAM,WACP;AAEH,MAAM,yBACJ,QACA,aAEA,OAAO,IAAI,aAAa;CAItB,MAAM,UAHc,OAAO,YAAY,QAC1B,OAAO,KAAK,MAES;CAClC,MAAM,SAAS,KAAK,UAAU,OAAO,WAAW,OAAO,CAAC,GACpD,KAAK,UAAU,OAAO,MAAM,OAAO,OAAO,CAAC,GAC3C;CAEJ,MAAM,EAAE,MAAM,UAAU,WAAW,OAAO;AAE1C,QAAO,QAAQ,IACb,KACE,QAAQ,KAAK,KAAK,EAClB,QAAQ,SAAS,MAAM,EACvB,QAAQ,aACN,QAAQ,KAAK,CACX,KAAK,QAAQ,KAAK,OAAO,EAAE,QAAQ,SAAS,KAAK,YAAY,CAAC,EAC9D,KAAK,QAAQ,KAAK,OAAO,EAAE,QAAQ,SAAS,MAAM,CAAC,CACpD,CAAC,CACH,EACD,QAAQ,OAAO,EAAE,OAAO,UAAU,CAAC,CACpC,CACF;EACD;AAEJ,MAAM,uBACJ,UACA,YAEA,OAAO,IAAI,aAAa;CACtB,MAAM,EACJ,gBACA,kBACA,eACA,aACA,kBACEA,KAAmB,UAAU,QAAQ;CAEzC,MAAM,gBACJ,YACA,SACA,UAEA,OAAO,QAAQ,aAAa,OAC1B,OAAO,QACL,MAAM,aACJ,QAAQ,OAAO,UAAU,OAAO,MAAM,OAAO,GAAG,WAAW,GAAG,CAAC,CAChE,EACD,MACD,CACF;AAEH,QAAO,aAAa,eAAe,kBAAkB,mBAAmB;AACxE,QAAO,aAAa,aAAa,gBAAgB,iBAAiB;AAClE,QAAO,OAAO,QAAQ,gBAAgB,OACpC,OAAO,IAAI,aAAa;AACtB,SAAO,OAAO,QACZ,MAAM,aACJ,QAAQ,OAAO,iBAAiB,OAC9B,MAAM,OAAO,GAAG,WAAW,GAAG,CAC/B,CACF,EACD,iBACD;AACD,SAAO,OAAO,QACZ,MAAM,aACJ,QAAQ,OAAO,mBAAmB,OAChC,MAAM,OAAO,GAAG,WAAW,GAAG,CAC/B,CACF,EACD,mBACD;GACD,CACH;EACD;AAEJ,MAAa,MAAM,QAAQ,KAAK,OAAO,EAAE,QACvC,OAAO,IAAI,aAAa;AACtB,QAAO,WAAW,2BAA2B;CAC7C,MAAM,wBAAwB,OAAO;CACrC,MAAM,gBAAgB,OAAO,eAAe,KAC1C,OAAO,KAAK,EAAE,oBACZ,oBAAoB,uBAAuB,cAAc,CAC1D,EACD,OAAO,UAAU,WAAW,iCAAiC,CAAC,EAC9DC,YACD;CACD,MAAM,uBAAuB,OAAO,MAAM,eAAe;EACvD,cAAc;EACd,SAAS,EAAE,oBAAoB;EAChC,CAAC;CAEF,MAAM,aAAa,OAAO,IAAI,KAAc,YAAY;CACxD,MAAM,SAAS,OAAO,MAAM,QAAc,EAAE;CAC5C,MAAM,eAAe,OAAO,MAAM,QAAc,EAAE;CAClD,MAAM,mBAAmB,OAAO,IAAI,KAAoB,mBAAmB;AAE3E,QAAO,OAAO,IACZ;EACE,OAAO,OACL,mBACE,QACA,YACA,cACA,iBACD,CACF;EACD,wBAAwB,QAAQ,YAAY,aAAa;EACzD,SAAS,QAAQ,YAAY,sBAAsB,iBAAiB;EACrE,EACD,EAAE,aAAa,aAAa,CAC7B;EACD,CACH,CAAC,KAAK,QAAQ,gBAAgB,uCAAuC,CAAC;AAEvE,MAAM,qBAAqB,MACzB,GAAG,EAAE,UAAU,QAAQ,GAAG,GAAG,EAAE,UAAU,QAAQ,GAAG,GAAG,EAAE,UAAU,UAAU,GAAG,GAAG,EAAE;AAEvF,MAAM,eAAe,WACnB,KAAK,MAAM,aAAa,OAAO,QAAQ,CAAC,EAAE,MAAM,QAAQ;AAE1D,MAAM,uBACJ,WAEA,KACE,YAAY,OAAO,EACnB,MAAM,YACH,UAAU,aACT,kBAAkB,SAAS,KAAK,kBAAkB,SAAS,CAC9D,CACF;AAEH,MAAM,0BAA0B,WAC9B,KACE,YAAY,OAAO,EACnB,MAAM,IAAI,kBAAkB,EAC5B,MAAM,QACN,MAAM,KAAK,MAAM,OAAO,EACxB,MAAM,KAAK,KAAK,CACjB;;;;;;;AAQH,MAAM,2BACJ,kBACA,2BAEA,OAAO,IAAI,aAAa;CACtB,MAAM,SAAS,OAAO,IAAI,IAAI,iBAAiB;CAC/C,MAAM,YAAY,uBAAuB,OAAO;AAEhD,KAAI,eADa,OAAO,IAAI,IAAI,uBAAuB,EAC3B;AAC5B,QAAO,IAAI,IAAI,wBAAwB,UAAU;AACjD,KAAI,OAAO,SAAS,EAAG;AACvB,QAAO,wBAAwB,oBAAoB,OAAO,CAAC;EAC3D;;;;;;;;AASJ,MAAM,uBACJ,QACA,YACA,YAEA,OAAO,IAAI,aAAa;CACtB,MAAM,QAAQ,OAAO,MAAM;CAC3B,MAAM,YAAY,SAAS,SAAS,QAAQ;AAC5C,QAAO,OAAO,QAAQ,MAAiB;EACrC,QAAQ,cAAc;EACtB,YACE,OAAO,IAAI,aAAa;AACtB,UAAO,OAAO,MAAM,WAAW;GAC/B,MAAM,UAAU,OAAO,MAAM,QAAQ,OAAO;AAC5C,OAAI,MAAM,QAAQ,QAAQ,CAAE,QAAO;AAEnC,WADY,OAAO,MAAM,qBACZ,QAAQ;IACrB;EACL,CAAC;EACF;AAEJ,MAAM,YACJ,QACA,YACA,sBACA,qBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,mBAAmB,OAAO,IAAI,KAAK,qBAAqB;CAC9D,MAAM,sBAAsB,OAAO,IAAI,KAAa,GAAG;AAEvD,QAAO,OAAO,OAAO,QACnB,OAAO,IAAI,aAAa;AACtB,SAAO,OAAO,SAAS,qBAAqB;AAI5C,SAAO,MAAM,KAAK,OAAO;AACzB,SAAO,oBACL,QACA,qBACA,kBACD;AAED,SAAO,wBAAwB,kBAAkB,oBAAoB;EAErE,MAAM,UAAU,OAAO,IAAI,UAAU,YAAY,YAAY;AAE7D,MAAI,CAAC,eAAe,QAAQ,CAG1B;AAGF,SAAO,WAAW,4CAA4C;AAE9D,MAAI,QAAQ,WAAW;GACrB,MAAM,UAAU,OAAO,eAAe,KACpC,OAAO,KAAK,EAAE,eAAe,wBAC3B,OAAO,IAAI,aAAa;AAEtB,WAAO,oBADU,OAAO,IAAI,IAAI,iBAAiB,EACZ,kBAAkB;AACvD,WAAO,IAAI,IAAI,kBAAkB,kBAAkB;KACnD,CACH,EACDA,YACD;AACD,OAAI,OAAO,OAAO,QAAQ,CACxB;AAOF,OAAI,QAAQ,MAAM,kBAChB,QAAO,OAAO,MAAM,cAAc;AAEpC,UAAO,oBACL,QACA,qBACA,kBACD;AACD,UAAO,IAAI,IAAI,YAAY,YAAY;;EAGzC,MAAM,qBAAqB;GACzB,GAAI,QAAQ,YACR,CAAC,iBAAiB,cAAc,UAAU,CAAC,GAC3C,EAAE;GACN,GAAI,QAAQ,aACR,CAAC,iBAAiB,eAAe,WAAW,CAAC,GAC7C,EAAE;GACN,GAAI,QAAQ,YACR,CAAC,iBAAiB,oBAAoB,iBAAiB,CAAC,GACxD,EAAE;GACP;AAED,SAAO,MAAM,wBAAwB,mBAAmB,GACpD,OAAO,IAAI,oBAAoB,EAAE,aAAa,aAAa,CAAC,GAC5D,OAAO;AAEX,SAAO,WAAW,iCAAiC;GACnD,CACH;EACD;;;;;;;AAcJ,MAAM,sBAAsB,OAAO,IAAI,aAAa;CAClD,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,cAAc,OAAO,YAAY;CACvC,MAAM,mBAAmB,OAAO,iBAAiB;CAEjD,MAAM,YAAY,cAAsB,eACtC,OAAO,IAAI,aAAa;EACtB,MAAM,eAAe,KAAK,KAAK,kBAAkB,aAAa;AAC9D,MAAI,EAAE,OAAO,GAAG,OAAO,aAAa,EAClC,QAAO,OAAO,MAAkB;AAElC,SAAO,OAAO,KAAiB;GAC7B;GACA,aAAa,KAAK,SAAS,aAAa,aAAa;GACrD;GACD,CAAC;GACF;CAEJ,MAAM,oBAAoB,OAAO,OAAO,IAAI;EAC1C,SAAS,qBAAqB,YAAY;EAC1C,SAAS,0BAA0B,YAAY;EAC/C,SAAS,aAAa,YAAY;EAClC,SAAS,WAAW,YAAY;EAChC,SAAS,YAAY,aAAa;EAClC,SAAS,WAAW,YAAY;EACjC,CAAC;CAEF,MAAM,oBAAoB,OAAO;CACjC,MAAM,mBAAmB,OAAO,OAAO,QACrC,oBACC,iBAAiB,SAAS,cAAc,YAAY,CACtD;AAED,QAAO,MAAM,SAAS,CAAC,GAAG,mBAAmB,GAAG,iBAAiB,CAAC;EAClE;AAEF,MAAM,kBACJ,OACA,QACA,YACA,qBACG;CAQH,MAAM,sBAAsB,IAAI,WAAW,MAAM;AACjD,QAAO;EACL,aAAa,CAAC,MAAM,aAAa;EACjC,QAAQ;EACR,OAAO;EACP,UAAU;EACV,UAAU;EACV,QAAQ;EACR,UAAU;EACV,UAAU;EACV,SAAS,CACP,yBACA;GACE,MAAM;GACN,MAAM,OAA4B;AAChC,UAAM,OAAO,WAAW;AACtB,YAAO,WACL,OAAO,IAAI,aAAa;MAKtB,MAAM,YAAY,EAJC,OAAO,IAAI,UAC5B,qBACA,KACD;AAED,aAAO,IAAI,OAAO,mBAAmB,YAAY;OAC/C,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,WAAI,OAAO,OAAO,SAAS,EACzB,MAAK,IAAI,MAAM,cAAc,OAAO,OAAO;WAE3C,MAAK,OAAO,MAAM,aAAa;AAEjC,cAAO;QACP;AACF,UAAI,aAAa,OAAO,OAAO,WAAW,EAAG;AAC7C,aAAO,IAAI,OAAO,aAAa,OAAO;OACpC,GAAG;QACF,MAAM,aAAa;OACrB,EAAE;AACH,aAAO,MAAM,MAAM,QAAQ,OAAU;OACrC,CACH;MACD;;GAEL,CACF;EACF;;AAGH,MAAM,2BACJ,OACA,QACA,YACA,qBAEA,OAAO,eACL,OAAO,QAAQ,YAAY;CACzB,MAAM,MAAM,MAAM,QAAQ,QACxB,eAAe,OAAO,QAAQ,YAAY,iBAAiB,CAC5D;AACD,OAAM,IAAI,OAAO;AACjB,QAAO;EACP,GACD,QACC,OAAO,IAAI,aAAa;AACtB,QAAO,OAAO,cAAc,IAAI,SAAS,CAAC;AAG1C,QAAO,IAAI,OAAO,mBAAmB,YAAY;AAC/C,MAAI,CAAC,QAAQ,IAAI,MAAM,aAAa,CAAE,QAAO;EAC7C,MAAM,OAAO,IAAI,IAAI,QAAQ;AAC7B,OAAK,OAAO,MAAM,aAAa;AAC/B,SAAO;GACP;AACF,QAAO,OAAO,SACZ,6BAA6B,MAAM,cACpC;EACD,CACL;;;;;;;;AASH,MAAM,sBACJ,QACA,YACA,cACA,qBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,cAAc,OAAO,OAAO;CAClC,MAAM,YAAY,OAAO,IAAI,qBAAK,IAAI,KAAmC,CAAC;CAE1E,MAAM,OAAO,OAAO,IAAI,aAAa;EACnC,MAAM,UAAU,OAAO;EACvB,MAAM,gBAAgB,IAAI,IACxB,QAAQ,KAAK,eAAe,CAAC,WAAW,cAAc,WAAW,CAAC,CACnE;EACD,MAAM,UAAU,OAAO,IAAI,IAAI,UAAU;AAEzC,SAAO,OAAO,QACZ,MAAM,aAAa,QAAQ,GAC1B,CAAC,cAAc,gBACd,cAAc,IAAI,aAAa,GAC3B,OAAO,OACP,MAAM,MAAM,YAAY,KAAK,KAAK,CAAC,KACjC,OAAO,QACL,IAAI,OAAO,YAAY,WAAW;GAChC,MAAM,UAAU,IAAI,IAAI,OAAO;AAC/B,WAAQ,OAAO,aAAa;AAC5B,UAAO;IACP,CACH,CACF,CACR;AAED,SAAO,OAAO,QAAQ,UAAU,UAC9B,OAAO,IAAI,aAAa;AAEtB,QADiB,OAAO,IAAI,IAAI,UAAU,EAC7B,IAAI,MAAM,aAAa,CAAE;GAEtC,MAAM,aAAa,OAAO,MAAM,KAC9B,aACA,kBAAkB,WACnB;AACD,UAAO,wBACL,OACA,QACA,YACA,iBACD,CAAC,KAAK,MAAM,OAAO,WAAW,CAAC;AAChC,UAAO,IAAI,OAAO,YAAY,WAAW;IACvC,MAAM,UAAU,IAAI,IAAI,OAAO;AAC/B,YAAQ,IAAI,MAAM,cAAc,WAAW;AAC3C,WAAO;KACP;IACF,CACH;GACD;AAEF,QAAO;AAEP,QAAO,OAAO,OAAO,QACnB,MAAM,KAAK,aAAa,CAAC,KAAK,OAAO,QAAQ,KAAK,CAAC,CACpD;EACD;;;;;;;;AASJ,MAAM,2BACJ,QACA,YACA,iBAEA,OAAO,IAAI,aAAa;CACtB,MAAM,KAAK,OAAO,WAAW;CAC7B,MAAM,OAAO,OAAO,KAAK;CACzB,MAAM,mBAAmB,OAAO,iBAAiB;AAEjD,QAAO,KACL,GAAG,MAAM,kBAAkB,EAAE,WAAW,MAAM,CAAC,EAC/C,OAAO,SAAS,SAAS,OAAO,IAAI,CAAC,EACrC,OAAO,YAAY,UACjB,oBAAoB;EAClB,cAAc,KAAK,SAAS,kBAAkB,MAAM,KAAK;EACzD,UAAU,MAAM;EAChB;EACA;EACA;EACD,CAAC,CACH,CACF;EACD;AAEJ,MAAM,0BAA2D,IAAI,IAAI;CACvE,CAAC,WAAW,YAAY;CACxB,CAAC,YAAY,aAAa;CAC1B,CAAC,WAAW,YAAY;CACzB,CAAC;AAEF,MAAM,sBACJ,YACA,QACA,KACA,cACA,YAEA,KACE,IAAI,OAAO,aAAa,OAAO;CAAE,GAAG;EAAI,MAAM;CAAM,EAAE,EACtD,OAAO,QAAQ,MAAM,MAAM,QAAQ,OAAU,CAAC,EAC9C,OAAO,QACL,UAAU,MAAM,MAAM,cAAc,OAAU,GAAG,OAAO,KACzD,CACF;AAEH,MAAM,uBAAuB,EAC3B,cACA,UACA,QACA,YACA,mBAOI;AAIJ,KAAI,aAAa,MAAM,QAAQ,CAAC,SAAS,aAAa,CACpD,QAAO,OAAO;AAGhB,KAAI,CAAC,aAAa,SAAS,MAAM,CAC/B,QAAO,OAAO;CAGhB,MAAM,oBAAoB,aAAa;CAEvC,MAAM,cAAc,wBAAwB,IAAI,aAAa;AAC7D,KAAI,gBAAgB,OAClB,QAAO,mBACL,YACA,QACA,aACA,cACA,kBACD;AAGH,KAAI,iBAAiB,YACnB,QAAO,mBACL,YACA,QACA,aACA,cACA,kBACD;AAGH,KAAI,eAAe,aAAa,IAAI,eAAe,aAAa,CAC9D,QAAO,mBACL,YACA,QACA,aACA,cACA,kBACD;AASH,KAAI,aAAa,SACf,QAAO,mBACL,YACA,QACA,aACA,cACA,MACD;AAGH,QAAO,OAAO;;AAGhB,MAAM,oBAAoB,UAA+B,eACvD,KACE,UACA,OAAO,QACL,OAAO,MAAM;CACX,SAAS,EAAE,QAAQ,qBACjB,MAAM,MAAM,OAAO,CAAC,KAClB,MAAM,KAAK,mBAAmB,OAAO,KAAK,EAC1C,MAAM,OAAO,SAAS,aAAa,oBACjC,sBAAsB,iBAAiB,eAAe,CACvD,EACD,MAAM,WACP;CACH,cACE,OAAO,IAAI,aAAa;EACtB,MAAM,KAAK,OAAO,WAAW;EAC7B,MAAM,OAAO,OAAO,KAAK;EACzB,MAAM,kBAAkB,OAAO,gBAAgB;EAC/C,MAAM,iBAAiB,KAAK,KAAK,iBAAiB,WAAW;AAE7D,MAAI,OAAO,GAAG,OAAO,eAAe,EAAE;AACpC,UAAO,GAAG,OAAO,eAAe;AAChC,UAAO,sBAAsB,WAAW,eAAe;;GAEzD;CACL,CAAC,CACH,CACF"}
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
@@ -1,5 +1,5 @@
1
1
  //#region package.json
2
- var version = "9.0.0-next.2";
2
+ var version = "9.0.0-next.4";
3
3
 
4
4
  //#endregion
5
5
  export { version };
@@ -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();
@@ -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.2",
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.2",
69
- "@confect/server": "^9.0.0-next.2"
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
- export const EXTERNAL_PACKAGES = [
14
- "@confect/core",
15
- "@confect/server",
16
- "effect",
17
- "@effect/*",
18
- ];
12
+ const isRelativeOrAbsolutePath = (importPath: string) =>
13
+ importPath.startsWith("./") ||
14
+ importPath.startsWith("../") ||
15
+ importPath.startsWith("/");
19
16
 
20
- const isExternalImport = (path: string) =>
21
- EXTERNAL_PACKAGES.some((p) => {
22
- if (p.endsWith("/*")) {
23
- return path.startsWith(p.slice(0, -1));
24
- }
25
- return path === p || path.startsWith(p + "/");
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 (!isExternalImport(args.path)) return;
35
- // `import.meta.resolve`'s second argument is silently ignored in modern
36
- // Node, so resolution would always walk up from the CLI's bundled file
37
- // (`packages/cli/dist/utils.mjs`) instead of from the user's project.
38
- // Use `createRequire` keyed on the importing file's directory so we
39
- // resolve out of *their* `node_modules`. The synthetic filename is just
40
- // a CommonJS resolution anchor; the file does not need to exist.
41
- const parentFile = pathToFileURL(args.resolveDir + "/_").href;
42
- const require_ = createRequire(parentFile);
43
- const resolvedPath = require_.resolve(args.path);
44
- const resolved = pathToFileURL(resolvedPath).href;
45
- return { path: resolved, external: true };
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
  };
@@ -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
+ );
@@ -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, EXTERNAL_PACKAGES } from "../Bundler";
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
  );