@confect/server 8.0.0 → 9.0.0-next.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/dist/DatabaseSchema.d.ts +9 -4
  3. package/dist/DatabaseSchema.d.ts.map +1 -1
  4. package/dist/DatabaseSchema.js +4 -3
  5. package/dist/DatabaseSchema.js.map +1 -1
  6. package/dist/FunctionImpl.d.ts +10 -7
  7. package/dist/FunctionImpl.d.ts.map +1 -1
  8. package/dist/FunctionImpl.js +8 -8
  9. package/dist/FunctionImpl.js.map +1 -1
  10. package/dist/GroupImpl.d.ts +51 -12
  11. package/dist/GroupImpl.d.ts.map +1 -1
  12. package/dist/GroupImpl.js +72 -4
  13. package/dist/GroupImpl.js.map +1 -1
  14. package/dist/GroupPath.d.ts +8 -0
  15. package/dist/GroupPath.d.ts.map +1 -0
  16. package/dist/GroupPath.js +10 -0
  17. package/dist/GroupPath.js.map +1 -0
  18. package/dist/RegisteredConvexFunction.d.ts +6 -6
  19. package/dist/RegisteredConvexFunction.d.ts.map +1 -1
  20. package/dist/RegisteredFunction.d.ts +3 -3
  21. package/dist/RegisteredFunction.d.ts.map +1 -1
  22. package/dist/RegisteredFunctions.d.ts +15 -4
  23. package/dist/RegisteredFunctions.d.ts.map +1 -1
  24. package/dist/RegisteredFunctions.js +20 -11
  25. package/dist/RegisteredFunctions.js.map +1 -1
  26. package/dist/StorageActionWriter.d.ts +1 -1
  27. package/dist/index.d.ts +1 -3
  28. package/dist/index.js +1 -3
  29. package/package.json +4 -6
  30. package/src/DatabaseSchema.ts +10 -7
  31. package/src/FunctionImpl.ts +27 -36
  32. package/src/GroupImpl.ts +168 -32
  33. package/src/GroupPath.ts +43 -0
  34. package/src/RegisteredFunctions.ts +78 -28
  35. package/src/index.ts +0 -2
  36. package/dist/Impl.d.ts +0 -24
  37. package/dist/Impl.d.ts.map +0 -1
  38. package/dist/Impl.js +0 -28
  39. package/dist/Impl.js.map +0 -1
  40. package/dist/Registry.d.ts +0 -15
  41. package/dist/Registry.d.ts.map +0 -1
  42. package/dist/Registry.js +0 -10
  43. package/dist/Registry.js.map +0 -1
  44. package/src/Impl.ts +0 -59
  45. package/src/Registry.ts +0 -13
@@ -1,6 +1,6 @@
1
1
  import { AnyWithProps as AnyWithProps$1 } from "./Api.js";
2
2
  import { Any, RegisteredFunction } from "./RegisteredFunction.js";
3
- import { Impl } from "./Impl.js";
3
+ import { GroupImpl } from "./GroupImpl.js";
4
4
  import { AnyWithProps as AnyWithProps$2 } from "./RegistryItem.js";
5
5
  import { Layer, Types } from "effect";
6
6
  import * as Spec from "@confect/core/Spec";
@@ -9,14 +9,25 @@ import * as GroupSpec from "@confect/core/GroupSpec";
9
9
 
10
10
  //#region src/RegisteredFunctions.d.ts
11
11
  declare namespace RegisteredFunctions_d_exports {
12
- export { AnyWithProps, RegisteredFunctions, make };
12
+ export { AnyWithProps, ForGroupPath, RegisteredFunctions, buildForGroup };
13
13
  }
14
14
  type RegisteredFunctions<Spec_ extends Spec.AnyWithProps> = Types.Simplify<RegisteredFunctionsHelper<Spec.Groups<Spec_>>>;
15
15
  type RegisteredFunctionsHelper<Groups extends GroupSpec.AnyWithProps> = { [GroupName in GroupSpec.Name<Groups>]: GroupSpec.WithName<Groups, GroupName> extends infer Group extends GroupSpec.AnyWithProps ? GroupSpec.Groups<Group> extends infer SubGroups extends GroupSpec.AnyWithProps ? Types.Simplify<RegisteredFunctionsHelper<SubGroups> & { [FunctionName in FunctionSpec.Name<GroupSpec.Functions<Group>>]: FunctionSpec.WithName<GroupSpec.Functions<Group>, FunctionName> extends infer FunctionSpec_ extends FunctionSpec.AnyWithProps ? RegisteredFunction<FunctionSpec_> : never }> : { [FunctionName in FunctionSpec.Name<GroupSpec.Functions<Group>>]: FunctionSpec.WithName<GroupSpec.Functions<Group>, FunctionName> extends infer FunctionSpec_ extends FunctionSpec.AnyWithProps ? RegisteredFunction<FunctionSpec_> : never } : never };
16
16
  interface AnyWithProps {
17
17
  readonly [key: string]: Any | AnyWithProps;
18
18
  }
19
- declare const make: <Api_ extends AnyWithProps$1>(impl: Layer.Layer<Impl<Api_, "Finalized">>, makeRegisteredFunction: (api: Api_, registryItem: AnyWithProps$2) => Any) => Types.Simplify<RegisteredFunctionsHelper<Spec.Groups<Api_["spec"]>>>;
19
+ type RegisteredFunctionsAtPath<Tree, Path extends string> = Path extends `${infer Head}.${infer Tail}` ? Head extends keyof Tree ? Tree[Head] extends AnyWithProps ? RegisteredFunctionsAtPath<Tree[Head], Tail> : never : never : Path extends keyof Tree ? Tree[Path] : never;
20
+ type ForGroupPath<Spec_ extends Spec.AnyWithProps, Path extends string> = RegisteredFunctionsAtPath<RegisteredFunctions<Spec_>, Path>;
21
+ /**
22
+ * Build the registered Convex functions for a single group from its finalized
23
+ * `GroupImpl` layer.
24
+ *
25
+ * The `groupLayer` parameter requires `GroupImpl<string, "Finalized">`, so
26
+ * impls that were never piped through `GroupImpl.finalize` (and impls with
27
+ * unmet `FunctionImpl` requirements, which cannot be finalized) are rejected
28
+ * at the codegen boundary, not just deep inside Convex at runtime.
29
+ */
30
+ declare const buildForGroup: <Api_ extends AnyWithProps$1, const GroupPath_ extends string>(api: Api_, groupPath: GroupPath_, groupLayer: Layer.Layer<GroupImpl<string, "Finalized">>, makeRegisteredFunction: (api: Api_, registryItem: AnyWithProps$2) => Any) => ForGroupPath<Api_["spec"], GroupPath_>;
20
31
  //#endregion
21
- export { AnyWithProps, RegisteredFunctions, RegisteredFunctions_d_exports, make };
32
+ export { AnyWithProps, ForGroupPath, RegisteredFunctions, RegisteredFunctions_d_exports, buildForGroup };
22
33
  //# sourceMappingURL=RegisteredFunctions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"RegisteredFunctions.d.ts","names":[],"sources":["../src/RegisteredFunctions.ts"],"mappings":";;;;;;;;;;;;;KAYY,mBAAA,eAAkC,IAAA,CAAK,YAAA,IACjD,KAAA,CAAM,QAAA,CAAS,yBAAA,CAA0B,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,KAElD,yBAAA,gBAAyC,SAAA,CAAU,YAAA,oBACxC,SAAA,CAAU,IAAA,CAAK,MAAA,IAAU,SAAA,CAAU,QAAA,CAC/C,MAAA,EACA,SAAA,8BAC4B,SAAA,CAAU,YAAA,GACpC,SAAA,CAAU,MAAA,CAAO,KAAA,kCACf,SAAA,CAAU,YAAA,GACV,KAAA,CAAM,QAAA,CACJ,yBAAA,CAA0B,SAAA,uBACP,YAAA,CAAa,IAAA,CAC5B,SAAA,CAAU,SAAA,CAAU,KAAA,KAClB,YAAA,CAAa,QAAA,CACf,SAAA,CAAU,SAAA,CAAU,KAAA,GACpB,YAAA,sCACoC,YAAA,CAAa,YAAA,GAC/C,kBAAA,CAAsC,aAAA,kCAK3B,YAAA,CAAa,IAAA,CAC5B,SAAA,CAAU,SAAA,CAAU,KAAA,KAClB,YAAA,CAAa,QAAA,CACf,SAAA,CAAU,SAAA,CAAU,KAAA,GACpB,YAAA,sCACoC,YAAA,CAAa,YAAA,GAC/C,kBAAA,CAAsC,aAAA;AAAA,UAMnC,YAAA;EAAA,UACL,GAAA,WAAc,GAAA,GAAyB,YAAA;AAAA;AAAA,cAGtC,IAAA,gBAAqB,cAAA,EAChC,IAAA,EAAM,KAAA,CAAM,KAAA,CAAM,IAAA,CAAU,IAAA,iBAC5B,sBAAA,GACE,GAAA,EAAK,IAAA,EACL,YAAA,EAAc,cAAA,KACX,GAAA,KAAsB,KAAA,CAAA,QAAA,CAAA,yBAAA,CAAA,IAAA,CAAA,MAAA,CAAA,IAAA"}
1
+ {"version":3,"file":"RegisteredFunctions.d.ts","names":[],"sources":["../src/RegisteredFunctions.ts"],"mappings":";;;;;;;;;;;;;KAqBY,mBAAA,eAAkC,IAAA,CAAK,YAAA,IACjD,KAAA,CAAM,QAAA,CAAS,yBAAA,CAA0B,IAAA,CAAK,MAAA,CAAO,KAAA;AAAA,KAElD,yBAAA,gBAAyC,SAAA,CAAU,YAAA,oBACxC,SAAA,CAAU,IAAA,CAAK,MAAA,IAAU,SAAA,CAAU,QAAA,CAC/C,MAAA,EACA,SAAA,8BAC4B,SAAA,CAAU,YAAA,GACpC,SAAA,CAAU,MAAA,CAAO,KAAA,kCACf,SAAA,CAAU,YAAA,GACV,KAAA,CAAM,QAAA,CACJ,yBAAA,CAA0B,SAAA,uBACP,YAAA,CAAa,IAAA,CAC5B,SAAA,CAAU,SAAA,CAAU,KAAA,KAClB,YAAA,CAAa,QAAA,CACf,SAAA,CAAU,SAAA,CAAU,KAAA,GACpB,YAAA,sCACoC,YAAA,CAAa,YAAA,GAC/C,kBAAA,CAAsC,aAAA,kCAK3B,YAAA,CAAa,IAAA,CAC5B,SAAA,CAAU,SAAA,CAAU,KAAA,KAClB,YAAA,CAAa,QAAA,CACf,SAAA,CAAU,SAAA,CAAU,KAAA,GACpB,YAAA,sCACoC,YAAA,CAAa,YAAA,GAC/C,kBAAA,CAAsC,aAAA;AAAA,UAMnC,YAAA;EAAA,UACL,GAAA,WAAc,GAAA,GAAyB,YAAA;AAAA;AAAA,KAG9C,yBAAA,8BAGD,IAAA,yCACA,IAAA,eAAmB,IAAA,GACjB,IAAA,CAAK,IAAA,UAAc,YAAA,GACjB,yBAAA,CAA0B,IAAA,CAAK,IAAA,GAAO,IAAA,oBAG1C,IAAA,eAAmB,IAAA,GACjB,IAAA,CAAK,IAAA;AAAA,KAGC,YAAA,eACI,IAAA,CAAK,YAAA,yBAEjB,yBAAA,CAA0B,mBAAA,CAAoB,KAAA,GAAQ,IAAA;;;;;;;;;;cAW7C,aAAA,gBACE,cAAA,mCAGb,GAAA,EAAK,IAAA,EACL,SAAA,EAAW,UAAA,EACX,UAAA,EAAY,KAAA,CAAM,KAAA,CAAM,SAAA,wBACxB,sBAAA,GACE,GAAA,EAAK,IAAA,EACL,YAAA,EAAc,cAAA,KACX,GAAA,KACJ,YAAA,CAAa,IAAA,UAAc,UAAA"}
@@ -1,19 +1,28 @@
1
1
  import { __exportAll } from "./_virtual/_rolldown/runtime.js";
2
2
  import { mapLeaves } from "./internal/utils.js";
3
- import { Registry } from "./Registry.js";
4
3
  import { isRegistryItem } from "./RegistryItem.js";
5
- import { Impl } from "./Impl.js";
6
- import { Effect, Match, Ref } from "effect";
4
+ import { Array, Effect, Option, Predicate, Ref, String, pipe } from "effect";
5
+ import * as Registry from "@confect/core/Registry";
7
6
 
8
7
  //#region src/RegisteredFunctions.ts
9
- var RegisteredFunctions_exports = /* @__PURE__ */ __exportAll({ make: () => make });
10
- const make = (impl, makeRegisteredFunction) => Effect.gen(function* () {
11
- const registry = yield* Registry;
12
- const functionImplItems = yield* Ref.get(registry);
13
- const { api, finalizationStatus } = yield* Impl();
14
- return yield* Match.value(finalizationStatus).pipe(Match.withReturnType(), Match.when("Unfinalized", () => Effect.dieMessage("Impl is not finalized")), Match.when("Finalized", () => Effect.succeed(mapLeaves(functionImplItems, isRegistryItem, (registryItem) => makeRegisteredFunction(api, registryItem)))), Match.exhaustive);
15
- }).pipe(Effect.provide(impl), Effect.runSync);
8
+ var RegisteredFunctions_exports = /* @__PURE__ */ __exportAll({ buildForGroup: () => buildForGroup });
9
+ /**
10
+ * Build the registered Convex functions for a single group from its finalized
11
+ * `GroupImpl` layer.
12
+ *
13
+ * The `groupLayer` parameter requires `GroupImpl<string, "Finalized">`, so
14
+ * impls that were never piped through `GroupImpl.finalize` (and impls with
15
+ * unmet `FunctionImpl` requirements, which cannot be finalized) are rejected
16
+ * at the codegen boundary, not just deep inside Convex at runtime.
17
+ */
18
+ const buildForGroup = (api, groupPath, groupLayer, makeRegisteredFunction) => {
19
+ const registeredFunctions = mapLeaves(Effect.gen(function* () {
20
+ const registry = yield* Registry.Registry;
21
+ return yield* Ref.get(registry);
22
+ }).pipe(Effect.provide(groupLayer), Effect.runSync), isRegistryItem, (registryItem) => makeRegisteredFunction(api, registryItem));
23
+ return pipe(String.split(groupPath, "."), Array.reduce(Option.some(registeredFunctions), (currentNode, segment) => currentNode.pipe(Option.filter(Predicate.isRecord), Option.flatMap((nodeRecord) => segment in nodeRecord ? Option.some(nodeRecord[segment]) : Option.none()))), Option.getOrThrowWith(() => /* @__PURE__ */ new Error(`No functions registered for group path "${groupPath}"`)));
24
+ };
16
25
 
17
26
  //#endregion
18
- export { RegisteredFunctions_exports, make };
27
+ export { RegisteredFunctions_exports, buildForGroup };
19
28
  //# sourceMappingURL=RegisteredFunctions.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"RegisteredFunctions.js","names":["Registry.Registry","Impl.Impl","RegistryItem.isRegistryItem"],"sources":["../src/RegisteredFunctions.ts"],"sourcesContent":["import type * as FunctionSpec from \"@confect/core/FunctionSpec\";\nimport type * as GroupSpec from \"@confect/core/GroupSpec\";\nimport type * as Spec from \"@confect/core/Spec\";\nimport type { Layer } from \"effect\";\nimport { Effect, Match, Ref, type Types } from \"effect\";\nimport type * as Api from \"./Api\";\nimport * as Impl from \"./Impl\";\nimport { mapLeaves } from \"./internal/utils\";\nimport type * as RegisteredFunction from \"./RegisteredFunction\";\nimport * as Registry from \"./Registry\";\nimport * as RegistryItem from \"./RegistryItem\";\n\nexport type RegisteredFunctions<Spec_ extends Spec.AnyWithProps> =\n Types.Simplify<RegisteredFunctionsHelper<Spec.Groups<Spec_>>>;\n\ntype RegisteredFunctionsHelper<Groups extends GroupSpec.AnyWithProps> = {\n [GroupName in GroupSpec.Name<Groups>]: GroupSpec.WithName<\n Groups,\n GroupName\n > extends infer Group extends GroupSpec.AnyWithProps\n ? GroupSpec.Groups<Group> extends infer SubGroups extends\n GroupSpec.AnyWithProps\n ? Types.Simplify<\n RegisteredFunctionsHelper<SubGroups> & {\n [FunctionName in FunctionSpec.Name<\n GroupSpec.Functions<Group>\n >]: FunctionSpec.WithName<\n GroupSpec.Functions<Group>,\n FunctionName\n > extends infer FunctionSpec_ extends FunctionSpec.AnyWithProps\n ? RegisteredFunction.RegisteredFunction<FunctionSpec_>\n : never;\n }\n >\n : {\n [FunctionName in FunctionSpec.Name<\n GroupSpec.Functions<Group>\n >]: FunctionSpec.WithName<\n GroupSpec.Functions<Group>,\n FunctionName\n > extends infer FunctionSpec_ extends FunctionSpec.AnyWithProps\n ? RegisteredFunction.RegisteredFunction<FunctionSpec_>\n : never;\n }\n : never;\n};\n\nexport interface AnyWithProps {\n readonly [key: string]: RegisteredFunction.Any | AnyWithProps;\n}\n\nexport const make = <Api_ extends Api.AnyWithProps>(\n impl: Layer.Layer<Impl.Impl<Api_, \"Finalized\">>,\n makeRegisteredFunction: (\n api: Api_,\n registryItem: RegistryItem.AnyWithProps,\n ) => RegisteredFunction.Any,\n) =>\n Effect.gen(function* () {\n const registry = yield* Registry.Registry;\n const functionImplItems = yield* Ref.get(registry);\n const { api, finalizationStatus } = yield* Impl.Impl<Api_, \"Finalized\">();\n\n return yield* Match.value(\n finalizationStatus as Impl.FinalizationStatus,\n ).pipe(\n Match.withReturnType<Effect.Effect<RegisteredFunctions<Api_[\"spec\"]>>>(),\n Match.when(\"Unfinalized\", () =>\n Effect.dieMessage(\"Impl is not finalized\"),\n ),\n Match.when(\"Finalized\", () =>\n Effect.succeed(\n mapLeaves<RegistryItem.AnyWithProps, RegisteredFunction.Any>(\n functionImplItems,\n RegistryItem.isRegistryItem,\n (registryItem) => makeRegisteredFunction(api, registryItem),\n ) as RegisteredFunctions<Api_[\"spec\"]>,\n ),\n ),\n Match.exhaustive,\n );\n }).pipe(Effect.provide(impl), Effect.runSync);\n"],"mappings":";;;;;;;;;AAmDA,MAAa,QACX,MACA,2BAKA,OAAO,IAAI,aAAa;CACtB,MAAM,WAAW,OAAOA;CACxB,MAAM,oBAAoB,OAAO,IAAI,IAAI,SAAS;CAClD,MAAM,EAAE,KAAK,uBAAuB,OAAOC,MAA8B;AAEzE,QAAO,OAAO,MAAM,MAClB,mBACD,CAAC,KACA,MAAM,gBAAkE,EACxE,MAAM,KAAK,qBACT,OAAO,WAAW,wBAAwB,CAC3C,EACD,MAAM,KAAK,mBACT,OAAO,QACL,UACE,mBACAC,iBACC,iBAAiB,uBAAuB,KAAK,aAAa,CAC5D,CACF,CACF,EACD,MAAM,WACP;EACD,CAAC,KAAK,OAAO,QAAQ,KAAK,EAAE,OAAO,QAAQ"}
1
+ {"version":3,"file":"RegisteredFunctions.js","names":["RegistryItem.isRegistryItem"],"sources":["../src/RegisteredFunctions.ts"],"sourcesContent":["import type * as FunctionSpec from \"@confect/core/FunctionSpec\";\nimport type * as GroupSpec from \"@confect/core/GroupSpec\";\nimport * as Registry from \"@confect/core/Registry\";\nimport type * as Spec from \"@confect/core/Spec\";\nimport {\n Array,\n Effect,\n type Layer,\n Option,\n pipe,\n Predicate,\n Ref,\n String,\n type Types,\n} from \"effect\";\nimport type * as Api from \"./Api\";\nimport type * as GroupImpl from \"./GroupImpl\";\nimport { mapLeaves } from \"./internal/utils\";\nimport type * as RegisteredFunction from \"./RegisteredFunction\";\nimport * as RegistryItem from \"./RegistryItem\";\n\nexport type RegisteredFunctions<Spec_ extends Spec.AnyWithProps> =\n Types.Simplify<RegisteredFunctionsHelper<Spec.Groups<Spec_>>>;\n\ntype RegisteredFunctionsHelper<Groups extends GroupSpec.AnyWithProps> = {\n [GroupName in GroupSpec.Name<Groups>]: GroupSpec.WithName<\n Groups,\n GroupName\n > extends infer Group extends GroupSpec.AnyWithProps\n ? GroupSpec.Groups<Group> extends infer SubGroups extends\n GroupSpec.AnyWithProps\n ? Types.Simplify<\n RegisteredFunctionsHelper<SubGroups> & {\n [FunctionName in FunctionSpec.Name<\n GroupSpec.Functions<Group>\n >]: FunctionSpec.WithName<\n GroupSpec.Functions<Group>,\n FunctionName\n > extends infer FunctionSpec_ extends FunctionSpec.AnyWithProps\n ? RegisteredFunction.RegisteredFunction<FunctionSpec_>\n : never;\n }\n >\n : {\n [FunctionName in FunctionSpec.Name<\n GroupSpec.Functions<Group>\n >]: FunctionSpec.WithName<\n GroupSpec.Functions<Group>,\n FunctionName\n > extends infer FunctionSpec_ extends FunctionSpec.AnyWithProps\n ? RegisteredFunction.RegisteredFunction<FunctionSpec_>\n : never;\n }\n : never;\n};\n\nexport interface AnyWithProps {\n readonly [key: string]: RegisteredFunction.Any | AnyWithProps;\n}\n\ntype RegisteredFunctionsAtPath<\n Tree,\n Path extends string,\n> = Path extends `${infer Head}.${infer Tail}`\n ? Head extends keyof Tree\n ? Tree[Head] extends AnyWithProps\n ? RegisteredFunctionsAtPath<Tree[Head], Tail>\n : never\n : never\n : Path extends keyof Tree\n ? Tree[Path]\n : never;\n\nexport type ForGroupPath<\n Spec_ extends Spec.AnyWithProps,\n Path extends string,\n> = RegisteredFunctionsAtPath<RegisteredFunctions<Spec_>, Path>;\n\n/**\n * Build the registered Convex functions for a single group from its finalized\n * `GroupImpl` layer.\n *\n * The `groupLayer` parameter requires `GroupImpl<string, \"Finalized\">`, so\n * impls that were never piped through `GroupImpl.finalize` (and impls with\n * unmet `FunctionImpl` requirements, which cannot be finalized) are rejected\n * at the codegen boundary, not just deep inside Convex at runtime.\n */\nexport const buildForGroup = <\n Api_ extends Api.AnyWithProps,\n const GroupPath_ extends string,\n>(\n api: Api_,\n groupPath: GroupPath_,\n groupLayer: Layer.Layer<GroupImpl.GroupImpl<string, \"Finalized\">>,\n makeRegisteredFunction: (\n api: Api_,\n registryItem: RegistryItem.AnyWithProps,\n ) => RegisteredFunction.Any,\n): ForGroupPath<Api_[\"spec\"], GroupPath_> => {\n const registryItems = Effect.gen(function* () {\n const registry = yield* Registry.Registry;\n return yield* Ref.get(registry);\n }).pipe(Effect.provide(groupLayer), Effect.runSync);\n\n const registeredFunctions = mapLeaves<\n RegistryItem.AnyWithProps,\n RegisteredFunction.Any\n >(\n registryItems as { [key: string]: RegistryItem.AnyWithProps },\n RegistryItem.isRegistryItem,\n (registryItem) => makeRegisteredFunction(api, registryItem),\n );\n\n return pipe(\n String.split(groupPath, \".\"),\n Array.reduce(\n Option.some<unknown>(registeredFunctions),\n (currentNode, segment) =>\n currentNode.pipe(\n Option.filter(Predicate.isRecord),\n Option.flatMap((nodeRecord) =>\n segment in nodeRecord\n ? Option.some(nodeRecord[segment])\n : Option.none(),\n ),\n ),\n ),\n Option.getOrThrowWith(\n () => new Error(`No functions registered for group path \"${groupPath}\"`),\n ),\n ) as ForGroupPath<Api_[\"spec\"], GroupPath_>;\n};\n"],"mappings":";;;;;;;;;;;;;;;;;AAuFA,MAAa,iBAIX,KACA,WACA,YACA,2BAI2C;CAM3C,MAAM,sBAAsB,UALN,OAAO,IAAI,aAAa;EAC5C,MAAM,WAAW,OAAO,SAAS;AACjC,SAAO,OAAO,IAAI,IAAI,SAAS;GAC/B,CAAC,KAAK,OAAO,QAAQ,WAAW,EAAE,OAAO,QAAQ,EAOjDA,iBACC,iBAAiB,uBAAuB,KAAK,aAAa,CAC5D;AAED,QAAO,KACL,OAAO,MAAM,WAAW,IAAI,EAC5B,MAAM,OACJ,OAAO,KAAc,oBAAoB,GACxC,aAAa,YACZ,YAAY,KACV,OAAO,OAAO,UAAU,SAAS,EACjC,OAAO,SAAS,eACd,WAAW,aACP,OAAO,KAAK,WAAW,SAAS,GAChC,OAAO,MAAM,CAClB,CACF,CACJ,EACD,OAAO,qCACC,IAAI,MAAM,2CAA2C,UAAU,GAAG,CACzE,CACF"}
@@ -25,7 +25,7 @@ declare const StorageActionWriter_base: effect_Context0.TagClass<StorageActionWr
25
25
  store: (blob: Blob, options?: {
26
26
  sha256?: string;
27
27
  }) => Effect.Effect<GenericId<"_storage">, never, never>;
28
- }) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, StorageActionWriter | R> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, effect_Cause0.UnknownException, StorageActionWriter> : Effect.Effect<X, never, StorageActionWriter>;
28
+ }) => X) => [X] extends [Effect.Effect<infer A, infer E, infer R>] ? Effect.Effect<A, E, R | StorageActionWriter> : [X] extends [PromiseLike<infer A_1>] ? Effect.Effect<A_1, effect_Cause0.UnknownException, StorageActionWriter> : Effect.Effect<X, never, StorageActionWriter>;
29
29
  };
30
30
  declare class StorageActionWriter extends StorageActionWriter_base {
31
31
  static readonly layer: (storageActionWriter: StorageActionWriter$1) => Layer.Layer<StorageActionWriter, never, never>;
package/dist/index.d.ts CHANGED
@@ -30,9 +30,7 @@ import { Handler_d_exports } from "./Handler.js";
30
30
  import { FunctionImpl_d_exports } from "./FunctionImpl.js";
31
31
  import { GroupImpl_d_exports } from "./GroupImpl.js";
32
32
  import { HttpApi_d_exports } from "./HttpApi.js";
33
- import { Impl_d_exports } from "./Impl.js";
34
33
  import { RegistryItem_d_exports } from "./RegistryItem.js";
35
34
  import { RegisteredConvexFunction_d_exports } from "./RegisteredConvexFunction.js";
36
35
  import { RegisteredFunctions_d_exports } from "./RegisteredFunctions.js";
37
- import { Registry_d_exports } from "./Registry.js";
38
- export { ActionCtx_d_exports as ActionCtx, ActionRunner_d_exports as ActionRunner, Api_d_exports as Api, Auth_d_exports as Auth, BlobNotFoundError_d_exports as BlobNotFoundError, ConvexConfigProvider_d_exports as ConvexConfigProvider, CronJob_d_exports as CronJob, CronJobs_d_exports as CronJobs, DataModel_d_exports as DataModel, DatabaseReader_d_exports as DatabaseReader, DatabaseSchema_d_exports as DatabaseSchema, DatabaseWriter_d_exports as DatabaseWriter, Document_d_exports as Document, FunctionImpl_d_exports as FunctionImpl, GroupImpl_d_exports as GroupImpl, Handler_d_exports as Handler, HttpApi_d_exports as HttpApi, Impl_d_exports as Impl, MutationCtx_d_exports as MutationCtx, MutationRunner_d_exports as MutationRunner, OrderedQuery_d_exports as OrderedQuery, QueryCtx_d_exports as QueryCtx, QueryInitializer_d_exports as QueryInitializer, QueryRunner_d_exports as QueryRunner, RegisteredConvexFunction_d_exports as RegisteredConvexFunction, RegisteredFunction_d_exports as RegisteredFunction, RegisteredFunctions_d_exports as RegisteredFunctions, Registry_d_exports as Registry, RegistryItem_d_exports as RegistryItem, Scheduler_d_exports as Scheduler, SchemaToValidator_d_exports as SchemaToValidator, StorageActionWriter_d_exports as StorageActionWriter, StorageReader_d_exports as StorageReader, StorageWriter_d_exports as StorageWriter, Table_d_exports as Table, TableInfo_d_exports as TableInfo, VectorSearch_d_exports as VectorSearch };
36
+ export { ActionCtx_d_exports as ActionCtx, ActionRunner_d_exports as ActionRunner, Api_d_exports as Api, Auth_d_exports as Auth, BlobNotFoundError_d_exports as BlobNotFoundError, ConvexConfigProvider_d_exports as ConvexConfigProvider, CronJob_d_exports as CronJob, CronJobs_d_exports as CronJobs, DataModel_d_exports as DataModel, DatabaseReader_d_exports as DatabaseReader, DatabaseSchema_d_exports as DatabaseSchema, DatabaseWriter_d_exports as DatabaseWriter, Document_d_exports as Document, FunctionImpl_d_exports as FunctionImpl, GroupImpl_d_exports as GroupImpl, Handler_d_exports as Handler, HttpApi_d_exports as HttpApi, MutationCtx_d_exports as MutationCtx, MutationRunner_d_exports as MutationRunner, OrderedQuery_d_exports as OrderedQuery, QueryCtx_d_exports as QueryCtx, QueryInitializer_d_exports as QueryInitializer, QueryRunner_d_exports as QueryRunner, RegisteredConvexFunction_d_exports as RegisteredConvexFunction, RegisteredFunction_d_exports as RegisteredFunction, RegisteredFunctions_d_exports as RegisteredFunctions, RegistryItem_d_exports as RegistryItem, Scheduler_d_exports as Scheduler, SchemaToValidator_d_exports as SchemaToValidator, StorageActionWriter_d_exports as StorageActionWriter, StorageReader_d_exports as StorageReader, StorageWriter_d_exports as StorageWriter, Table_d_exports as Table, TableInfo_d_exports as TableInfo, VectorSearch_d_exports as VectorSearch };
package/dist/index.js CHANGED
@@ -15,7 +15,6 @@ import { OrderedQuery_exports } from "./OrderedQuery.js";
15
15
  import { QueryInitializer_exports } from "./QueryInitializer.js";
16
16
  import { DatabaseReader_exports } from "./DatabaseReader.js";
17
17
  import { DatabaseWriter_exports } from "./DatabaseWriter.js";
18
- import { Registry_exports } from "./Registry.js";
19
18
  import { RegistryItem_exports } from "./RegistryItem.js";
20
19
  import { FunctionImpl_exports } from "./FunctionImpl.js";
21
20
  import { GroupImpl_exports } from "./GroupImpl.js";
@@ -27,7 +26,6 @@ import { StorageActionWriter_exports } from "./StorageActionWriter.js";
27
26
  import { StorageReader_exports } from "./StorageReader.js";
28
27
  import { StorageWriter_exports } from "./StorageWriter.js";
29
28
  import { HttpApi_exports } from "./HttpApi.js";
30
- import { Impl_exports } from "./Impl.js";
31
29
  import { MutationCtx_exports } from "./MutationCtx.js";
32
30
  import { QueryCtx_exports } from "./QueryCtx.js";
33
31
  import { VectorSearch_exports } from "./VectorSearch.js";
@@ -36,4 +34,4 @@ import { RegisteredConvexFunction_exports } from "./RegisteredConvexFunction.js"
36
34
  import { RegisteredFunctions_exports } from "./RegisteredFunctions.js";
37
35
  import { TableInfo_exports } from "./TableInfo.js";
38
36
 
39
- export { ActionCtx_exports as ActionCtx, ActionRunner_exports as ActionRunner, Api_exports as Api, Auth_exports as Auth, BlobNotFoundError_exports as BlobNotFoundError, ConvexConfigProvider_exports as ConvexConfigProvider, CronJob_exports as CronJob, CronJobs_exports as CronJobs, DataModel_exports as DataModel, DatabaseReader_exports as DatabaseReader, DatabaseSchema_exports as DatabaseSchema, DatabaseWriter_exports as DatabaseWriter, Document_exports as Document, FunctionImpl_exports as FunctionImpl, GroupImpl_exports as GroupImpl, Handler_exports as Handler, HttpApi_exports as HttpApi, Impl_exports as Impl, MutationCtx_exports as MutationCtx, MutationRunner_exports as MutationRunner, OrderedQuery_exports as OrderedQuery, QueryCtx_exports as QueryCtx, QueryInitializer_exports as QueryInitializer, QueryRunner_exports as QueryRunner, RegisteredConvexFunction_exports as RegisteredConvexFunction, RegisteredFunction_exports as RegisteredFunction, RegisteredFunctions_exports as RegisteredFunctions, Registry_exports as Registry, RegistryItem_exports as RegistryItem, Scheduler_exports as Scheduler, SchemaToValidator_exports as SchemaToValidator, StorageActionWriter_exports as StorageActionWriter, StorageReader_exports as StorageReader, StorageWriter_exports as StorageWriter, Table_exports as Table, TableInfo_exports as TableInfo, VectorSearch_exports as VectorSearch };
37
+ export { ActionCtx_exports as ActionCtx, ActionRunner_exports as ActionRunner, Api_exports as Api, Auth_exports as Auth, BlobNotFoundError_exports as BlobNotFoundError, ConvexConfigProvider_exports as ConvexConfigProvider, CronJob_exports as CronJob, CronJobs_exports as CronJobs, DataModel_exports as DataModel, DatabaseReader_exports as DatabaseReader, DatabaseSchema_exports as DatabaseSchema, DatabaseWriter_exports as DatabaseWriter, Document_exports as Document, FunctionImpl_exports as FunctionImpl, GroupImpl_exports as GroupImpl, Handler_exports as Handler, HttpApi_exports as HttpApi, MutationCtx_exports as MutationCtx, MutationRunner_exports as MutationRunner, OrderedQuery_exports as OrderedQuery, QueryCtx_exports as QueryCtx, QueryInitializer_exports as QueryInitializer, QueryRunner_exports as QueryRunner, RegisteredConvexFunction_exports as RegisteredConvexFunction, RegisteredFunction_exports as RegisteredFunction, RegisteredFunctions_exports as RegisteredFunctions, RegistryItem_exports as RegistryItem, Scheduler_exports as Scheduler, SchemaToValidator_exports as SchemaToValidator, StorageActionWriter_exports as StorageActionWriter, StorageReader_exports as StorageReader, StorageWriter_exports as StorageWriter, Table_exports as Table, TableInfo_exports as TableInfo, VectorSearch_exports as VectorSearch };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@confect/server",
3
- "version": "8.0.0",
3
+ "version": "9.0.0-next.0",
4
4
  "description": "Backend bindings to the Convex platform",
5
5
  "repository": {
6
6
  "type": "git",
@@ -66,16 +66,14 @@
66
66
  "typescript-eslint": "8.56.1",
67
67
  "vite": "7.3.1",
68
68
  "vite-tsconfig-paths": "6.1.1",
69
- "vitest": "3.2.4",
70
- "@confect/cli": "8.0.0",
71
- "@confect/test": "8.0.0"
69
+ "vitest": "3.2.4"
72
70
  },
73
71
  "peerDependencies": {
74
72
  "@effect/platform": "^0.96.1",
75
73
  "@effect/platform-node": "^0.106.0",
76
- "convex": "^1.30.0",
74
+ "convex": "1.39.1",
77
75
  "effect": "^3.21.2",
78
- "@confect/core": "^8.0.0"
76
+ "@confect/core": "^9.0.0-next.0"
79
77
  },
80
78
  "engines": {
81
79
  "node": ">=22",
@@ -3,15 +3,18 @@ import {
3
3
  defineSchema as defineConvexSchema,
4
4
  type SchemaDefinition,
5
5
  } from "convex/server";
6
- import { Array, pipe, Record } from "effect";
6
+ import { Array, pipe, Predicate, Record } from "effect";
7
7
  import * as Table from "./Table";
8
- import { TypeId } from "@confect/core/DatabaseSchema";
9
8
 
10
- export {
11
- type Any,
12
- isDatabaseSchema,
13
- TypeId,
14
- } from "@confect/core/DatabaseSchema";
9
+ export const TypeId = "@confect/server/DatabaseSchema";
10
+ export type TypeId = typeof TypeId;
11
+
12
+ export interface Any {
13
+ readonly [TypeId]: TypeId;
14
+ }
15
+
16
+ export const isDatabaseSchema = (u: unknown): u is Any =>
17
+ Predicate.hasProperty(u, TypeId);
15
18
 
16
19
  /**
17
20
  * A schema definition tracks the schema and its Convex schema definition.
@@ -1,11 +1,11 @@
1
1
  import type * as FunctionSpec from "@confect/core/FunctionSpec";
2
- import type * as GroupPath from "@confect/core/GroupPath";
3
2
  import type * as GroupSpec from "@confect/core/GroupSpec";
4
- import { Array, Context, Effect, Layer, Ref, String } from "effect";
3
+ import * as Registry from "@confect/core/Registry";
4
+ import { Context, Effect, Layer, Ref, String } from "effect";
5
5
  import type * as Api from "./Api";
6
+ import { resolveGroupPathUnsafe } from "./GroupPath";
6
7
  import type * as Handler from "./Handler";
7
8
  import { setNestedProperty } from "./internal/utils";
8
- import * as Registry from "./Registry";
9
9
  import * as RegistryItem from "./RegistryItem";
10
10
 
11
11
  export interface FunctionImpl<
@@ -32,34 +32,23 @@ export const FunctionImpl = <
32
32
 
33
33
  export const make = <
34
34
  Api_ extends Api.AnyWithProps,
35
- const GroupPath_ extends GroupPath.All<Api.Groups<Api_>>,
36
- const FunctionName extends FunctionSpec.Name<
37
- GroupSpec.Functions<GroupPath.GroupAt<Api.Groups<Api_>, GroupPath_>>
38
- >,
35
+ Group extends GroupSpec.AnyWithProps,
36
+ const FunctionName extends FunctionSpec.Name<GroupSpec.Functions<Group>>,
39
37
  >(
40
38
  api: Api_,
41
- groupPath: GroupPath_,
39
+ group: Group,
42
40
  functionName: FunctionName,
43
41
  handler: Handler.WithName<
44
42
  Api.Schema<Api_>,
45
- GroupSpec.Functions<GroupPath.GroupAt<Api.Groups<Api_>, GroupPath_>>,
43
+ GroupSpec.Functions<Group>,
46
44
  FunctionName
47
45
  >,
48
- ): Layer.Layer<FunctionImpl<GroupPath_, FunctionName>> => {
49
- const groupPathParts = String.split(groupPath, ".");
50
- const [firstGroupPathPart, ...restGroupPathParts] = groupPathParts;
51
-
52
- const group_: GroupSpec.AnyWithProps = Array.reduce(
53
- restGroupPathParts,
54
- (api as any).spec.groups[firstGroupPathPart as any]!,
55
- (currentGroup: any, groupPathPart: any) =>
56
- currentGroup.groups[groupPathPart],
57
- );
58
-
59
- const functionSpec = group_.functions[functionName]!;
46
+ ): Layer.Layer<FunctionImpl<string, FunctionName>> => {
47
+ const groupPath = resolveGroupPathUnsafe(api.spec, group);
48
+ const functionSpec = group.functions[functionName]!;
60
49
 
61
50
  return Layer.effect(
62
- FunctionImpl<GroupPath_, FunctionName>({
51
+ FunctionImpl<string, FunctionName>({
63
52
  groupPath,
64
53
  functionName,
65
54
  }),
@@ -69,7 +58,7 @@ export const make = <
69
58
  yield* Ref.update(registry, (registryItems) =>
70
59
  setNestedProperty(
71
60
  registryItems,
72
- [...groupPathParts, functionName],
61
+ [...String.split(groupPath, "."), functionName],
73
62
  RegistryItem.make({
74
63
  functionSpec,
75
64
  handler,
@@ -94,19 +83,21 @@ export type ForGroupPathAndFunction<
94
83
  > = FunctionImpl<GroupPath_, FunctionName>;
95
84
 
96
85
  /**
97
- * Get all function implementation services required for a group at a given path.
86
+ * Get all function implementation services required for a group spec.
98
87
  */
99
- export type FromGroupAtPath<
100
- GroupPath_ extends string,
101
- Group extends GroupSpec.AnyWithProps,
102
- > =
103
- GroupPath.GroupAt<Group, GroupPath_> extends infer GroupAtPath extends
104
- GroupSpec.AnyWithProps
105
- ? FunctionSpec.Name<
106
- GroupSpec.Functions<GroupAtPath>
107
- > extends infer FunctionNames extends string
108
- ? FunctionNames extends string
109
- ? FunctionImpl<GroupPath_, FunctionNames>
110
- : never
88
+ export type FromGroupSpec<Group extends GroupSpec.AnyWithProps> =
89
+ FunctionSpec.Name<
90
+ GroupSpec.Functions<Group>
91
+ > extends infer FunctionNames extends string
92
+ ? FunctionNames extends string
93
+ ? FunctionImpl<string, FunctionNames>
111
94
  : never
112
95
  : never;
96
+
97
+ /**
98
+ * @deprecated Use {@link FromGroupSpec} instead.
99
+ */
100
+ export type FromGroupAtPath<
101
+ _GroupPath extends string,
102
+ Group extends GroupSpec.AnyWithProps,
103
+ > = FromGroupSpec<Group>;
package/src/GroupImpl.ts CHANGED
@@ -1,60 +1,196 @@
1
- import type * as GroupPath from "@confect/core/GroupPath";
2
1
  import type * as GroupSpec from "@confect/core/GroupSpec";
3
- import { Context, Layer } from "effect";
2
+ import * as Registry from "@confect/core/Registry";
3
+ import {
4
+ Array,
5
+ Context,
6
+ Effect,
7
+ Layer,
8
+ Option,
9
+ pipe,
10
+ Predicate,
11
+ Record,
12
+ Ref,
13
+ String,
14
+ } from "effect";
4
15
  import type * as Api from "./Api";
5
16
  import type * as FunctionImpl from "./FunctionImpl";
17
+ import { resolveGroupPathUnsafe } from "./GroupPath";
6
18
 
7
- export interface GroupImpl<GroupPath_ extends string> {
19
+ export const TypeId = "@confect/server/GroupImpl";
20
+ export type TypeId = typeof TypeId;
21
+
22
+ export type FinalizationStatus = "Unfinalized" | "Finalized";
23
+
24
+ export interface GroupImpl<
25
+ GroupPath_ extends string,
26
+ FinalizationStatus_ extends FinalizationStatus = "Unfinalized",
27
+ > {
28
+ readonly [TypeId]: TypeId;
8
29
  readonly groupPath: GroupPath_;
30
+ readonly finalizationStatus: FinalizationStatus_;
31
+ /**
32
+ * Names of every function registered into this group's layer scope by
33
+ * `FunctionImpl.make`. Authoritative only when `finalizationStatus` is
34
+ * `"Finalized"`; the `"Unfinalized"` value is set to `[]` at `make`-time
35
+ * since the list is only known once `finalize` snapshots the registry.
36
+ */
37
+ readonly registeredFunctionNames: ReadonlyArray<string>;
9
38
  }
10
39
 
11
- export const GroupImpl = <GroupPath_ extends string>({
40
+ export interface Any extends GroupImpl<string, FinalizationStatus> {}
41
+
42
+ export const isGroupImpl = (u: unknown): u is Any =>
43
+ Predicate.hasProperty(u, TypeId);
44
+
45
+ export interface AnyFinalized extends GroupImpl<string, "Finalized"> {}
46
+ export interface AnyUnfinalized extends GroupImpl<string, "Unfinalized"> {}
47
+
48
+ export const isFinalizedGroupImpl = (u: unknown): u is AnyFinalized =>
49
+ isGroupImpl(u) && u.finalizationStatus === "Finalized";
50
+
51
+ export const isUnfinalizedGroupImpl = (u: unknown): u is AnyUnfinalized =>
52
+ isGroupImpl(u) && u.finalizationStatus === "Unfinalized";
53
+
54
+ /**
55
+ * Build the runtime tag for a `GroupImpl` service. The finalization status is
56
+ * embedded in the tag string so that `Unfinalized` and `Finalized` are distinct
57
+ * services at runtime; consumers of a finalized layer (the server's
58
+ * `RegisteredFunctions.buildForGroup` and the CLI's `implValidation`) retrieve
59
+ * the typed `Finalized` service directly rather than scanning the context.
60
+ */
61
+ export const GroupImpl = <
62
+ GroupPath_ extends string,
63
+ FinalizationStatus_ extends FinalizationStatus,
64
+ >({
12
65
  groupPath,
66
+ finalizationStatus,
13
67
  }: {
14
68
  groupPath: GroupPath_;
69
+ finalizationStatus: FinalizationStatus_;
15
70
  }) =>
16
- Context.GenericTag<GroupImpl<GroupPath_>>(
17
- `@confect/server/GroupImpl/${groupPath}`,
71
+ Context.GenericTag<GroupImpl<GroupPath_, FinalizationStatus_>>(
72
+ `@confect/server/GroupImpl/${finalizationStatus}/${groupPath}`,
18
73
  );
19
74
 
20
75
  export const make = <
21
76
  Api_ extends Api.AnyWithProps,
22
- const GroupPath_ extends GroupPath.All<Api.Groups<Api_>>,
77
+ Group extends GroupSpec.AnyWithProps,
23
78
  >(
24
- _api: Api_,
25
- groupPath: GroupPath_,
79
+ api: Api_,
80
+ group: Group,
26
81
  ): Layer.Layer<
27
- GroupImpl<GroupPath_>,
82
+ GroupImpl<string, "Unfinalized">,
28
83
  never,
29
- | FromGroupWithPath<GroupPath_, Api.Groups<Api_>>
30
- | FunctionImpl.FromGroupAtPath<GroupPath_, Api.Groups<Api_>>
31
- > =>
32
- Layer.succeed(
33
- GroupImpl<GroupPath_>({
84
+ FunctionImpl.FromGroupSpec<Group>
85
+ > => {
86
+ const groupPath = resolveGroupPathUnsafe(api.spec, group);
87
+
88
+ return Layer.succeed(
89
+ GroupImpl<string, "Unfinalized">({
34
90
  groupPath,
91
+ finalizationStatus: "Unfinalized",
35
92
  }),
36
93
  {
94
+ [TypeId]: TypeId,
37
95
  groupPath,
96
+ finalizationStatus: "Unfinalized" as const,
97
+ registeredFunctionNames: [],
38
98
  },
39
99
  ) as Layer.Layer<
40
- GroupImpl<GroupPath_>,
100
+ GroupImpl<string, "Unfinalized">,
41
101
  never,
42
- | FromGroupWithPath<GroupPath_, Api.Groups<Api_>>
43
- | FunctionImpl.FromGroupAtPath<GroupPath_, Api.Groups<Api_>>
102
+ FunctionImpl.FromGroupSpec<Group>
44
103
  >;
104
+ };
45
105
 
46
- export type FromGroups<Groups extends GroupSpec.Any> = Groups extends never
47
- ? never
48
- : Groups extends GroupSpec.AnyWithProps
49
- ? GroupImpl<GroupSpec.Name<Groups>>
50
- : never;
106
+ const isFunctionShaped = (value: unknown): boolean =>
107
+ Predicate.isRecord(value) && "functionSpec" in value;
51
108
 
52
- export type FromGroupWithPath<
53
- GroupPath_ extends string,
54
- Group extends GroupSpec.AnyWithProps,
55
- > =
56
- GroupPath.SubGroupsAt<Group, GroupPath_> extends infer SubGroupPaths
57
- ? SubGroupPaths extends string
58
- ? GroupImpl<SubGroupPaths>
59
- : never
60
- : never;
109
+ /**
110
+ * Walk a `RegistryItems` tree to the entries at `groupPath` and return the
111
+ * names of the function-shaped leaves directly underneath.
112
+ */
113
+ const collectFunctionNamesAtPath = (
114
+ items: Registry.RegistryItems,
115
+ groupPath: string,
116
+ ): ReadonlyArray<string> =>
117
+ pipe(
118
+ String.split(groupPath, "."),
119
+ Array.reduce(Option.some<unknown>(items), (acc, segment) =>
120
+ acc.pipe(
121
+ Option.filter(Predicate.isRecord),
122
+ Option.flatMap((node) =>
123
+ segment in node ? Option.some(node[segment]) : Option.none(),
124
+ ),
125
+ ),
126
+ ),
127
+ Option.filter(Predicate.isRecord),
128
+ Option.map(Record.toEntries),
129
+ Option.map(
130
+ Array.filterMap(([name, value]) =>
131
+ isFunctionShaped(value) ? Option.some(name) : Option.none(),
132
+ ),
133
+ ),
134
+ Option.getOrElse((): ReadonlyArray<string> => []),
135
+ );
136
+
137
+ const findUnfinalizedGroupImpl = <S>(
138
+ context: Context.Context<S>,
139
+ ): Option.Option<AnyUnfinalized> =>
140
+ Array.findFirst(context.unsafeMap.values(), isUnfinalizedGroupImpl);
141
+
142
+ /**
143
+ * Mark a `GroupImpl` layer as fully implemented. The parameter type defaults
144
+ * `RIn = never`, so passing a layer that still requires any `FunctionImpl`
145
+ * service produces a type error at the impl author's site. The codegen
146
+ * boundary requires the resulting `"Finalized"` brand, so omitting this call
147
+ * is also rejected downstream.
148
+ *
149
+ * As a side effect of finalization, the names of every `FunctionImpl` that
150
+ * registered into this group's scope are snapshotted onto the produced
151
+ * service value's `registeredFunctionNames` field, so consumers can verify
152
+ * impl completeness against a `GroupSpec`'s expected functions without
153
+ * having to inspect the `Registry` themselves.
154
+ */
155
+ export const finalize = <GroupPath_ extends string>(
156
+ group: Layer.Layer<GroupImpl<GroupPath_, "Unfinalized">>,
157
+ ): Layer.Layer<GroupImpl<GroupPath_, "Finalized">> =>
158
+ Layer.flatMap(
159
+ group,
160
+ (context): Layer.Layer<GroupImpl<GroupPath_, "Finalized">> =>
161
+ findUnfinalizedGroupImpl(context).pipe(
162
+ Option.match({
163
+ onNone: () =>
164
+ Layer.die(
165
+ new Error(
166
+ "GroupImpl.finalize: no Unfinalized GroupImpl service was found in the layer's context.",
167
+ ),
168
+ ),
169
+ onSome: (unfinalized) => {
170
+ const groupPath = unfinalized.groupPath as GroupPath_;
171
+ return Layer.effect(
172
+ GroupImpl<GroupPath_, "Finalized">({
173
+ groupPath,
174
+ finalizationStatus: "Finalized",
175
+ }),
176
+ Effect.gen(function* () {
177
+ const registry = yield* Registry.Registry;
178
+ const items = yield* Ref.get(registry);
179
+ return {
180
+ [TypeId]: TypeId,
181
+ groupPath,
182
+ finalizationStatus: "Finalized" as const,
183
+ registeredFunctionNames: collectFunctionNamesAtPath(
184
+ items,
185
+ groupPath,
186
+ ),
187
+ };
188
+ }),
189
+ );
190
+ },
191
+ }),
192
+ ),
193
+ );
194
+
195
+ export type FromGroupSpec<Group extends GroupSpec.AnyWithProps> =
196
+ FunctionImpl.FromGroupSpec<Group>;
@@ -0,0 +1,43 @@
1
+ import type * as GroupSpec from "@confect/core/GroupSpec";
2
+ import type * as Spec from "@confect/core/Spec";
3
+ import { Array, Option, pipe, Record } from "effect";
4
+
5
+ const resolveGroupPathInGroup = (
6
+ group: GroupSpec.AnyWithProps,
7
+ target: GroupSpec.AnyWithProps,
8
+ pathSegments: ReadonlyArray<string>,
9
+ ): Option.Option<string> =>
10
+ pipe(
11
+ Record.toEntries(group.groups),
12
+ Array.findFirst(([name, child]) =>
13
+ child === target
14
+ ? Option.some(Array.join([...pathSegments, name], "."))
15
+ : resolveGroupPathInGroup(child, target, [...pathSegments, name]),
16
+ ),
17
+ );
18
+
19
+ const resolveGroupPath = (
20
+ spec: Spec.AnyWithProps,
21
+ target: GroupSpec.AnyWithProps,
22
+ ): Option.Option<string> =>
23
+ pipe(
24
+ Record.toEntries(spec.groups),
25
+ Array.findFirst(([name, group]) =>
26
+ group === target
27
+ ? Option.some(name)
28
+ : resolveGroupPathInGroup(group, target, [name]),
29
+ ),
30
+ );
31
+
32
+ export const resolveGroupPathUnsafe = (
33
+ spec: Spec.AnyWithProps,
34
+ target: GroupSpec.AnyWithProps,
35
+ ): string =>
36
+ resolveGroupPath(spec, target).pipe(
37
+ Option.getOrThrowWith(
38
+ () =>
39
+ new Error(
40
+ "Could not resolve group path for the provided GroupSpec. Ensure the spec is part of the assembled API spec tree.",
41
+ ),
42
+ ),
43
+ );