@gaganref/confect-server 10.3.1-fork.17 → 10.3.1-fork.19
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/dist/GroupImpl.d.ts +21 -2
- package/dist/GroupImpl.d.ts.map +1 -1
- package/dist/GroupImpl.js +17 -18
- package/dist/GroupImpl.js.map +1 -1
- package/dist/RegistryItems.d.ts +7 -0
- package/dist/RegistryItems.d.ts.map +1 -1
- package/dist/RegistryItems.js +7 -2
- package/dist/RegistryItems.js.map +1 -1
- package/package.json +2 -2
- package/src/GroupImpl.ts +61 -8
- package/src/RegistryItems.ts +31 -0
package/dist/GroupImpl.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type * as GroupSpec from "@confect/core/GroupSpec";
|
|
2
2
|
import type * as MiddlewareSpec from "@confect/core/MiddlewareSpec";
|
|
3
|
+
import * as RegistryItems from "./RegistryItems.js";
|
|
3
4
|
import * as Context from "effect/Context";
|
|
4
5
|
import * as Layer from "effect/Layer";
|
|
5
6
|
import type * as DatabaseSchema from "./DatabaseSchema.js";
|
|
@@ -59,6 +60,15 @@ export declare const GroupImpl: <FinalizationStatus_ extends FinalizationStatus>
|
|
|
59
60
|
* runtime.
|
|
60
61
|
*/
|
|
61
62
|
export declare const make: <DatabaseSchema_ extends DatabaseSchema.AnyWithProps, Group extends GroupSpec.AnyWithProps>(_databaseSchema: DatabaseSchema_, _group: Group & MiddlewareSpec.ValidateImplRequires<GroupSpec.Functions<Group>, GroupSpec.MiddlewareSpecs<Group>>) => Layer.Layer<GroupImpl<"Unfinalized">, never, FunctionImpl.FromGroupSpec<Group> | MiddlewareImpl.FromGroupSpec<Group>>;
|
|
63
|
+
declare const MissingRequiredMiddlewareError_base: new <A extends Record<string, any> = {}>(args: import("effect/Types").VoidIfEmpty<{ readonly [P in keyof A as P extends "_tag" ? never : P]: A[P]; }>) => import("effect/Cause").YieldableError & {
|
|
64
|
+
readonly _tag: "MissingRequiredMiddlewareError";
|
|
65
|
+
} & Readonly<A>;
|
|
66
|
+
export declare class MissingRequiredMiddlewareError extends MissingRequiredMiddlewareError_base<{
|
|
67
|
+
readonly missing: ReadonlyArray<RegistryItems.MissingMiddleware>;
|
|
68
|
+
}> {
|
|
69
|
+
get message(): string;
|
|
70
|
+
}
|
|
71
|
+
type Finalizable = Layer.Layer<AnyUnfinalized> | Layer.Layer<AnyFinalized>;
|
|
62
72
|
/**
|
|
63
73
|
* Mark a `GroupImpl` layer as fully implemented. The parameter type defaults
|
|
64
74
|
* `RIn = never`, so passing a layer that still requires any `FunctionImpl`
|
|
@@ -70,8 +80,17 @@ export declare const make: <DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
|
|
|
70
80
|
* registered into this group's scope are snapshotted onto the produced service
|
|
71
81
|
* value's `registeredFunctionNames` field, so consumers can verify impl
|
|
72
82
|
* completeness against a `GroupSpec`'s expected functions without having to
|
|
73
|
-
* inspect the `Registry` themselves.
|
|
83
|
+
* inspect the `Registry` themselves. Pass `{ required }` to check every
|
|
84
|
+
* registered function's middleware at layer build, including internal
|
|
85
|
+
* functions. This form also accepts a finalized layer so generated registration
|
|
86
|
+
* can enforce the backend declaration without changing authored impls. Pass
|
|
87
|
+
* `groupPath` to qualify uncovered function names in the diagnostic.
|
|
74
88
|
*/
|
|
75
|
-
export declare
|
|
89
|
+
export declare function finalize(options: {
|
|
90
|
+
readonly groupPath?: string;
|
|
91
|
+
readonly required: ReadonlyArray<Pick<MiddlewareSpec.AnyMiddlewareSpec, "key">>;
|
|
92
|
+
}): (group: Finalizable) => Layer.Layer<GroupImpl<"Finalized">>;
|
|
93
|
+
export declare function finalize(group: Layer.Layer<GroupImpl<"Unfinalized">>): Layer.Layer<GroupImpl<"Finalized">>;
|
|
76
94
|
export type FromGroupSpec<Group extends GroupSpec.AnyWithProps> = FunctionImpl.FromGroupSpec<Group> | MiddlewareImpl.FromGroupSpec<Group>;
|
|
95
|
+
export {};
|
|
77
96
|
//# sourceMappingURL=GroupImpl.d.ts.map
|
package/dist/GroupImpl.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupImpl.d.ts","sourceRoot":"","sources":["../src/GroupImpl.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,KAAK,SAAS,MAAM,yBAAyB,CAAC;AAC1D,OAAO,KAAK,KAAK,cAAc,MAAM,8BAA8B,CAAC;
|
|
1
|
+
{"version":3,"file":"GroupImpl.d.ts","sourceRoot":"","sources":["../src/GroupImpl.ts"],"names":[],"mappings":"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"}
|
package/dist/GroupImpl.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { __exportAll } from "./_virtual/_rolldown/runtime.js";
|
|
2
2
|
import { Registry } from "./Registry.js";
|
|
3
|
-
import { functionNames, middlewareKeys } from "./RegistryItems.js";
|
|
3
|
+
import { functionNames, middlewareKeys, missingRequired } from "./RegistryItems.js";
|
|
4
4
|
import * as Context from "effect/Context";
|
|
5
5
|
import * as Layer from "effect/Layer";
|
|
6
6
|
import * as Effect from "effect/Effect";
|
|
@@ -8,9 +8,11 @@ import * as Option from "effect/Option";
|
|
|
8
8
|
import * as Array from "effect/Array";
|
|
9
9
|
import * as Predicate from "effect/Predicate";
|
|
10
10
|
import * as Ref from "effect/Ref";
|
|
11
|
+
import * as Data from "effect/Data";
|
|
11
12
|
//#region src/GroupImpl.ts
|
|
12
13
|
var GroupImpl_exports = /* @__PURE__ */ __exportAll({
|
|
13
14
|
GroupImpl: () => GroupImpl,
|
|
15
|
+
MissingRequiredMiddlewareError: () => MissingRequiredMiddlewareError,
|
|
14
16
|
TypeId: () => TypeId,
|
|
15
17
|
finalize: () => finalize,
|
|
16
18
|
isFinalizedGroupImpl: () => isFinalizedGroupImpl,
|
|
@@ -48,25 +50,22 @@ const make = (_databaseSchema, _group) => Layer.succeed(GroupImpl({ finalization
|
|
|
48
50
|
registeredFunctionNames: [],
|
|
49
51
|
registeredMiddlewareKeys: []
|
|
50
52
|
});
|
|
51
|
-
const
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
* completeness against a `GroupSpec`'s expected functions without having to
|
|
63
|
-
* inspect the `Registry` themselves.
|
|
64
|
-
*/
|
|
65
|
-
const finalize = (group) => Layer.flatMap(group, (context) => findUnfinalizedGroupImpl(context).pipe(Option.match({
|
|
66
|
-
onNone: () => Layer.effect(GroupImpl({ finalizationStatus: "Finalized" }), Effect.die(/* @__PURE__ */ new Error("GroupImpl.finalize: no Unfinalized GroupImpl service was found in the layer's context."))),
|
|
53
|
+
const findGroupImpl = (context) => Array.findFirst(context.mapUnsafe.values(), isGroupImpl);
|
|
54
|
+
var MissingRequiredMiddlewareError = class extends Data.TaggedError("MissingRequiredMiddlewareError") {
|
|
55
|
+
get message() {
|
|
56
|
+
return `GroupImpl.finalize: ${this.missing.map(({ functionName, middlewareKey }) => `${functionName} requires middleware "${middlewareKey}"`).join("\n")}`;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
function finalize(input) {
|
|
60
|
+
return "required" in input ? (group) => finalizeWith(group, input.required, input.groupPath) : finalizeWith(input, []);
|
|
61
|
+
}
|
|
62
|
+
const finalizeWith = (group, required, groupPath = "") => Layer.flatMap(group, (context) => findGroupImpl(context).pipe(Option.match({
|
|
63
|
+
onNone: () => Layer.effect(GroupImpl({ finalizationStatus: "Finalized" }), Effect.die(/* @__PURE__ */ new Error("GroupImpl.finalize: no GroupImpl service was found in the layer's context."))),
|
|
67
64
|
onSome: () => Layer.effect(GroupImpl({ finalizationStatus: "Finalized" }), Effect.gen(function* () {
|
|
68
65
|
const registry = yield* Registry;
|
|
69
66
|
const items = yield* Ref.get(registry);
|
|
67
|
+
const missing = missingRequired(items, required, groupPath);
|
|
68
|
+
if (missing.length > 0) return yield* Effect.die(new MissingRequiredMiddlewareError({ missing }));
|
|
70
69
|
return {
|
|
71
70
|
[TypeId]: TypeId,
|
|
72
71
|
finalizationStatus: "Finalized",
|
|
@@ -76,6 +75,6 @@ const finalize = (group) => Layer.flatMap(group, (context) => findUnfinalizedGro
|
|
|
76
75
|
}))
|
|
77
76
|
})));
|
|
78
77
|
//#endregion
|
|
79
|
-
export { GroupImpl, GroupImpl_exports, TypeId, finalize, isFinalizedGroupImpl, isGroupImpl, isUnfinalizedGroupImpl, make };
|
|
78
|
+
export { GroupImpl, GroupImpl_exports, MissingRequiredMiddlewareError, TypeId, finalize, isFinalizedGroupImpl, isGroupImpl, isUnfinalizedGroupImpl, make };
|
|
80
79
|
|
|
81
80
|
//# sourceMappingURL=GroupImpl.js.map
|
package/dist/GroupImpl.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"GroupImpl.js","names":["Registry.Registry","RegistryItems.functionNames","RegistryItems.middlewareKeys"],"sources":["../src/GroupImpl.ts"],"sourcesContent":["import type * as GroupSpec from \"@confect/core/GroupSpec\";\nimport type * as MiddlewareSpec from \"@confect/core/MiddlewareSpec\";\nimport * as Registry from \"./Registry.js\";\nimport * as RegistryItems from \"./RegistryItems.js\";\nimport * as Array from \"effect/Array\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Layer from \"effect/Layer\";\nimport * as Option from \"effect/Option\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Ref from \"effect/Ref\";\nimport type * as DatabaseSchema from \"./DatabaseSchema.js\";\nimport type * as FunctionImpl from \"./FunctionImpl.js\";\nimport type * as MiddlewareImpl from \"./MiddlewareImpl.js\";\n\nexport const TypeId = \"~@confect/server/GroupImpl\";\nexport type TypeId = typeof TypeId;\n\nexport type FinalizationStatus = \"Unfinalized\" | \"Finalized\";\n\nexport interface GroupImpl<\n FinalizationStatus_ extends FinalizationStatus = \"Unfinalized\",\n> {\n readonly [TypeId]: TypeId;\n readonly finalizationStatus: FinalizationStatus_;\n /**\n * Names of every function registered into this group's layer scope by\n * `FunctionImpl.make`. Authoritative only when `finalizationStatus` is\n * `\"Finalized\"`; the `\"Unfinalized\"` value is set to `[]` at `make`-time\n * since the list is only known once `finalize` snapshots the registry.\n */\n readonly registeredFunctionNames: ReadonlyArray<string>;\n /**\n * Keys of every middleware whose implementation registered into this group's\n * layer scope via `MiddlewareImpl.make` (and friends). Same authoritativeness\n * caveat as `registeredFunctionNames`. Lets consumers (the CLI's\n * `validateImpl`) verify middleware-impl completeness against a `GroupSpec`'s\n * attached middleware without inspecting the `Registry`.\n */\n readonly registeredMiddlewareKeys: ReadonlyArray<string>;\n}\n\nexport interface Any extends GroupImpl<FinalizationStatus> {}\n\nexport const isGroupImpl = (u: unknown): u is Any =>\n Predicate.hasProperty(u, TypeId);\n\nexport interface AnyFinalized extends GroupImpl<\"Finalized\"> {}\nexport interface AnyUnfinalized extends GroupImpl<\"Unfinalized\"> {}\n\nexport const isFinalizedGroupImpl = (u: unknown): u is AnyFinalized =>\n isGroupImpl(u) && u.finalizationStatus === \"Finalized\";\n\nexport const isUnfinalizedGroupImpl = (u: unknown): u is AnyUnfinalized =>\n isGroupImpl(u) && u.finalizationStatus === \"Unfinalized\";\n\n/**\n * Build the runtime tag for a `GroupImpl` service. The finalization status is\n * embedded in the tag string so that `Unfinalized` and `Finalized` are distinct\n * services at runtime; consumers of a finalized layer (the server's\n * `RegisteredFunctions.buildForGroup` and the CLI's `validateImpl`) retrieve\n * the typed `Finalized` service directly rather than scanning the context.\n *\n * The tag is keyed only by finalization status—no group path—because each\n * group's impl layer is built in its own isolated scope\n * (`buildForGroup`/`validateImpl` each provide a fresh `Registry`), so at most\n * one `GroupImpl` service of each status exists per build.\n */\nexport const GroupImpl = <FinalizationStatus_ extends FinalizationStatus>({\n finalizationStatus,\n}: {\n finalizationStatus: FinalizationStatus_;\n}) =>\n Context.Service<GroupImpl<FinalizationStatus_>>(\n `@confect/server/GroupImpl/${finalizationStatus}`,\n );\n\n/**\n * Begin a group's impl layer. `databaseSchema` and `group` are retained only as\n * type-level carriers (`group` drives the required `FunctionImpl` services via\n * `FromGroupSpec<Group>`; `databaseSchema` keeps the impl's dependency on\n * `_generated/schema` symmetric with `FunctionImpl.make`). Neither is read at\n * runtime.\n */\nexport const make = <\n DatabaseSchema_ extends DatabaseSchema.AnyWithProps,\n Group extends GroupSpec.AnyWithProps,\n>(\n _databaseSchema: DatabaseSchema_,\n _group: Group &\n MiddlewareSpec.ValidateImplRequires<\n GroupSpec.Functions<Group>,\n GroupSpec.MiddlewareSpecs<Group>\n >,\n): Layer.Layer<\n GroupImpl<\"Unfinalized\">,\n never,\n FunctionImpl.FromGroupSpec<Group> | MiddlewareImpl.FromGroupSpec<Group>\n> =>\n Layer.succeed(\n GroupImpl<\"Unfinalized\">({ finalizationStatus: \"Unfinalized\" }),\n {\n [TypeId]: TypeId,\n finalizationStatus: \"Unfinalized\" as const,\n registeredFunctionNames: [],\n registeredMiddlewareKeys: [],\n },\n ) as Layer.Layer<\n GroupImpl<\"Unfinalized\">,\n never,\n FunctionImpl.FromGroupSpec<Group> | MiddlewareImpl.FromGroupSpec<Group>\n >;\n\nconst findUnfinalizedGroupImpl = <S>(\n context: Context.Context<S>,\n): Option.Option<AnyUnfinalized> =>\n Array.findFirst(context.mapUnsafe.values(), isUnfinalizedGroupImpl);\n\n/**\n * Mark a `GroupImpl` layer as fully implemented. The parameter type defaults\n * `RIn = never`, so passing a layer that still requires any `FunctionImpl`\n * service produces a type error at the impl author's site. The codegen boundary\n * requires the resulting `\"Finalized\"` brand, so omitting this call is also\n * rejected downstream.\n *\n * As a side effect of finalization, the names of every `FunctionImpl` that\n * registered into this group's scope are snapshotted onto the produced service\n * value's `registeredFunctionNames` field, so consumers can verify impl\n * completeness against a `GroupSpec`'s expected functions without having to\n * inspect the `Registry` themselves.\n */\nexport const finalize = (\n group: Layer.Layer<GroupImpl<\"Unfinalized\">>,\n): Layer.Layer<GroupImpl<\"Finalized\">> =>\n Layer.flatMap(group, (context): Layer.Layer<GroupImpl<\"Finalized\">> =>\n findUnfinalizedGroupImpl(context).pipe(\n Option.match({\n onNone: () =>\n Layer.effect(\n GroupImpl<\"Finalized\">({ finalizationStatus: \"Finalized\" }),\n Effect.die(\n new Error(\n \"GroupImpl.finalize: no Unfinalized GroupImpl service was found in the layer's context.\",\n ),\n ),\n ),\n onSome: () =>\n Layer.effect(\n GroupImpl<\"Finalized\">({ finalizationStatus: \"Finalized\" }),\n Effect.gen(function* () {\n const registry = yield* Registry.Registry;\n const items = yield* Ref.get(registry);\n return {\n [TypeId]: TypeId,\n finalizationStatus: \"Finalized\" as const,\n registeredFunctionNames: RegistryItems.functionNames(items),\n registeredMiddlewareKeys: RegistryItems.middlewareKeys(items),\n };\n }),\n ),\n }),\n ),\n );\n\nexport type FromGroupSpec<Group extends GroupSpec.AnyWithProps> =\n | FunctionImpl.FromGroupSpec<Group>\n | MiddlewareImpl.FromGroupSpec<Group>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;AAeA,MAAa,SAAS;AA6BtB,MAAa,eAAe,MAC1B,UAAU,YAAY,GAAG,MAAM;AAKjC,MAAa,wBAAwB,MACnC,YAAY,CAAC,KAAK,EAAE,uBAAuB;AAE7C,MAAa,0BAA0B,MACrC,YAAY,CAAC,KAAK,EAAE,uBAAuB;;;;;;;;;;;;;AAc7C,MAAa,aAA6D,EACxE,yBAIA,QAAQ,QACN,6BAA6B,oBAC/B;;;;;;;;AASF,MAAa,QAIX,iBACA,WAUA,MAAM,QACJ,UAAyB,EAAE,oBAAoB,cAAc,CAAC,GAC9D;EACG,SAAS;CACV,oBAAoB;CACpB,yBAAyB,CAAC;CAC1B,0BAA0B,CAAC;AAC7B,CACF;AAMF,MAAM,4BACJ,YAEA,MAAM,UAAU,QAAQ,UAAU,OAAO,GAAG,sBAAsB;;;;;;;;;;;;;;AAepE,MAAa,YACX,UAEA,MAAM,QAAQ,QAAQ,YACpB,yBAAyB,OAAO,CAAC,CAAC,KAChC,OAAO,MAAM;CACX,cACE,MAAM,OACJ,UAAuB,EAAE,oBAAoB,YAAY,CAAC,GAC1D,OAAO,oBACL,IAAI,MACF,wFACF,CACF,CACF;CACF,cACE,MAAM,OACJ,UAAuB,EAAE,oBAAoB,YAAY,CAAC,GAC1D,OAAO,IAAI,aAAa;EACtB,MAAM,WAAW,OAAOA;EACxB,MAAM,QAAQ,OAAO,IAAI,IAAI,QAAQ;EACrC,OAAO;IACJ,SAAS;GACV,oBAAoB;GACpB,yBAAyBC,cAA4B,KAAK;GAC1D,0BAA0BC,eAA6B,KAAK;EAC9D;CACF,CAAC,CACH;AACJ,CAAC,CACH,CACF"}
|
|
1
|
+
{"version":3,"file":"GroupImpl.js","names":["Registry.Registry","RegistryItems.missingRequired","RegistryItems.functionNames","RegistryItems.middlewareKeys"],"sources":["../src/GroupImpl.ts"],"sourcesContent":["import type * as GroupSpec from \"@confect/core/GroupSpec\";\nimport type * as MiddlewareSpec from \"@confect/core/MiddlewareSpec\";\nimport * as Registry from \"./Registry.js\";\nimport * as RegistryItems from \"./RegistryItems.js\";\nimport * as Array from \"effect/Array\";\nimport * as Context from \"effect/Context\";\nimport * as Effect from \"effect/Effect\";\nimport * as Data from \"effect/Data\";\nimport * as Layer from \"effect/Layer\";\nimport * as Option from \"effect/Option\";\nimport * as Predicate from \"effect/Predicate\";\nimport * as Ref from \"effect/Ref\";\nimport type * as DatabaseSchema from \"./DatabaseSchema.js\";\nimport type * as FunctionImpl from \"./FunctionImpl.js\";\nimport type * as MiddlewareImpl from \"./MiddlewareImpl.js\";\n\nexport const TypeId = \"~@confect/server/GroupImpl\";\nexport type TypeId = typeof TypeId;\n\nexport type FinalizationStatus = \"Unfinalized\" | \"Finalized\";\n\nexport interface GroupImpl<\n FinalizationStatus_ extends FinalizationStatus = \"Unfinalized\",\n> {\n readonly [TypeId]: TypeId;\n readonly finalizationStatus: FinalizationStatus_;\n /**\n * Names of every function registered into this group's layer scope by\n * `FunctionImpl.make`. Authoritative only when `finalizationStatus` is\n * `\"Finalized\"`; the `\"Unfinalized\"` value is set to `[]` at `make`-time\n * since the list is only known once `finalize` snapshots the registry.\n */\n readonly registeredFunctionNames: ReadonlyArray<string>;\n /**\n * Keys of every middleware whose implementation registered into this group's\n * layer scope via `MiddlewareImpl.make` (and friends). Same authoritativeness\n * caveat as `registeredFunctionNames`. Lets consumers (the CLI's\n * `validateImpl`) verify middleware-impl completeness against a `GroupSpec`'s\n * attached middleware without inspecting the `Registry`.\n */\n readonly registeredMiddlewareKeys: ReadonlyArray<string>;\n}\n\nexport interface Any extends GroupImpl<FinalizationStatus> {}\n\nexport const isGroupImpl = (u: unknown): u is Any =>\n Predicate.hasProperty(u, TypeId);\n\nexport interface AnyFinalized extends GroupImpl<\"Finalized\"> {}\nexport interface AnyUnfinalized extends GroupImpl<\"Unfinalized\"> {}\n\nexport const isFinalizedGroupImpl = (u: unknown): u is AnyFinalized =>\n isGroupImpl(u) && u.finalizationStatus === \"Finalized\";\n\nexport const isUnfinalizedGroupImpl = (u: unknown): u is AnyUnfinalized =>\n isGroupImpl(u) && u.finalizationStatus === \"Unfinalized\";\n\n/**\n * Build the runtime tag for a `GroupImpl` service. The finalization status is\n * embedded in the tag string so that `Unfinalized` and `Finalized` are distinct\n * services at runtime; consumers of a finalized layer (the server's\n * `RegisteredFunctions.buildForGroup` and the CLI's `validateImpl`) retrieve\n * the typed `Finalized` service directly rather than scanning the context.\n *\n * The tag is keyed only by finalization status—no group path—because each\n * group's impl layer is built in its own isolated scope\n * (`buildForGroup`/`validateImpl` each provide a fresh `Registry`), so at most\n * one `GroupImpl` service of each status exists per build.\n */\nexport const GroupImpl = <FinalizationStatus_ extends FinalizationStatus>({\n finalizationStatus,\n}: {\n finalizationStatus: FinalizationStatus_;\n}) =>\n Context.Service<GroupImpl<FinalizationStatus_>>(\n `@confect/server/GroupImpl/${finalizationStatus}`,\n );\n\n/**\n * Begin a group's impl layer. `databaseSchema` and `group` are retained only as\n * type-level carriers (`group` drives the required `FunctionImpl` services via\n * `FromGroupSpec<Group>`; `databaseSchema` keeps the impl's dependency on\n * `_generated/schema` symmetric with `FunctionImpl.make`). Neither is read at\n * runtime.\n */\nexport const make = <\n DatabaseSchema_ extends DatabaseSchema.AnyWithProps,\n Group extends GroupSpec.AnyWithProps,\n>(\n _databaseSchema: DatabaseSchema_,\n _group: Group &\n MiddlewareSpec.ValidateImplRequires<\n GroupSpec.Functions<Group>,\n GroupSpec.MiddlewareSpecs<Group>\n >,\n): Layer.Layer<\n GroupImpl<\"Unfinalized\">,\n never,\n FunctionImpl.FromGroupSpec<Group> | MiddlewareImpl.FromGroupSpec<Group>\n> =>\n Layer.succeed(\n GroupImpl<\"Unfinalized\">({ finalizationStatus: \"Unfinalized\" }),\n {\n [TypeId]: TypeId,\n finalizationStatus: \"Unfinalized\" as const,\n registeredFunctionNames: [],\n registeredMiddlewareKeys: [],\n },\n ) as Layer.Layer<\n GroupImpl<\"Unfinalized\">,\n never,\n FunctionImpl.FromGroupSpec<Group> | MiddlewareImpl.FromGroupSpec<Group>\n >;\n\nconst findGroupImpl = <S>(context: Context.Context<S>): Option.Option<Any> =>\n Array.findFirst(context.mapUnsafe.values(), isGroupImpl);\n\nexport class MissingRequiredMiddlewareError extends Data.TaggedError(\n \"MissingRequiredMiddlewareError\",\n)<{\n readonly missing: ReadonlyArray<RegistryItems.MissingMiddleware>;\n}> {\n override get message(): string {\n return `GroupImpl.finalize: ${this.missing.map(({ functionName, middlewareKey }) => `${functionName} requires middleware \"${middlewareKey}\"`).join(\"\\n\")}`;\n }\n}\n\ntype Finalizable = Layer.Layer<AnyUnfinalized> | Layer.Layer<AnyFinalized>;\n\n/**\n * Mark a `GroupImpl` layer as fully implemented. The parameter type defaults\n * `RIn = never`, so passing a layer that still requires any `FunctionImpl`\n * service produces a type error at the impl author's site. The codegen boundary\n * requires the resulting `\"Finalized\"` brand, so omitting this call is also\n * rejected downstream.\n *\n * As a side effect of finalization, the names of every `FunctionImpl` that\n * registered into this group's scope are snapshotted onto the produced service\n * value's `registeredFunctionNames` field, so consumers can verify impl\n * completeness against a `GroupSpec`'s expected functions without having to\n * inspect the `Registry` themselves. Pass `{ required }` to check every\n * registered function's middleware at layer build, including internal\n * functions. This form also accepts a finalized layer so generated registration\n * can enforce the backend declaration without changing authored impls. Pass\n * `groupPath` to qualify uncovered function names in the diagnostic.\n */\nexport function finalize(options: {\n readonly groupPath?: string;\n readonly required: ReadonlyArray<\n Pick<MiddlewareSpec.AnyMiddlewareSpec, \"key\">\n >;\n}): (group: Finalizable) => Layer.Layer<GroupImpl<\"Finalized\">>;\nexport function finalize(\n group: Layer.Layer<GroupImpl<\"Unfinalized\">>,\n): Layer.Layer<GroupImpl<\"Finalized\">>;\nexport function finalize(\n input:\n | Finalizable\n | {\n readonly groupPath?: string;\n readonly required: ReadonlyArray<\n Pick<MiddlewareSpec.AnyMiddlewareSpec, \"key\">\n >;\n },\n):\n | Layer.Layer<GroupImpl<\"Finalized\">>\n | ((group: Finalizable) => Layer.Layer<GroupImpl<\"Finalized\">>) {\n return \"required\" in input\n ? (group) => finalizeWith(group, input.required, input.groupPath)\n : finalizeWith(input, []);\n}\n\nconst finalizeWith = (\n group: Layer.Layer<never>,\n required: ReadonlyArray<Pick<MiddlewareSpec.AnyMiddlewareSpec, \"key\">>,\n groupPath = \"\",\n): Layer.Layer<GroupImpl<\"Finalized\">> =>\n Layer.flatMap(group, (context): Layer.Layer<GroupImpl<\"Finalized\">> =>\n findGroupImpl(context).pipe(\n Option.match({\n onNone: () =>\n Layer.effect(\n GroupImpl<\"Finalized\">({ finalizationStatus: \"Finalized\" }),\n Effect.die(\n new Error(\n \"GroupImpl.finalize: no GroupImpl service was found in the layer's context.\",\n ),\n ),\n ),\n onSome: () =>\n Layer.effect(\n GroupImpl<\"Finalized\">({ finalizationStatus: \"Finalized\" }),\n Effect.gen(function* () {\n const registry = yield* Registry.Registry;\n const items = yield* Ref.get(registry);\n const missing = RegistryItems.missingRequired(\n items,\n required,\n groupPath,\n );\n if (missing.length > 0) {\n return yield* Effect.die(\n new MissingRequiredMiddlewareError({ missing }),\n );\n }\n return {\n [TypeId]: TypeId,\n finalizationStatus: \"Finalized\" as const,\n registeredFunctionNames: RegistryItems.functionNames(items),\n registeredMiddlewareKeys: RegistryItems.middlewareKeys(items),\n };\n }),\n ),\n }),\n ),\n );\n\nexport type FromGroupSpec<Group extends GroupSpec.AnyWithProps> =\n | FunctionImpl.FromGroupSpec<Group>\n | MiddlewareImpl.FromGroupSpec<Group>;\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAgBA,MAAa,SAAS;AA6BtB,MAAa,eAAe,MAC1B,UAAU,YAAY,GAAG,MAAM;AAKjC,MAAa,wBAAwB,MACnC,YAAY,CAAC,KAAK,EAAE,uBAAuB;AAE7C,MAAa,0BAA0B,MACrC,YAAY,CAAC,KAAK,EAAE,uBAAuB;;;;;;;;;;;;;AAc7C,MAAa,aAA6D,EACxE,yBAIA,QAAQ,QACN,6BAA6B,oBAC/B;;;;;;;;AASF,MAAa,QAIX,iBACA,WAUA,MAAM,QACJ,UAAyB,EAAE,oBAAoB,cAAc,CAAC,GAC9D;EACG,SAAS;CACV,oBAAoB;CACpB,yBAAyB,CAAC;CAC1B,0BAA0B,CAAC;AAC7B,CACF;AAMF,MAAM,iBAAoB,YACxB,MAAM,UAAU,QAAQ,UAAU,OAAO,GAAG,WAAW;AAEzD,IAAa,iCAAb,cAAoD,KAAK,YACvD,gCACF,CAAC,CAEE;CACD,IAAa,UAAkB;EAC7B,OAAO,uBAAuB,KAAK,QAAQ,KAAK,EAAE,cAAc,oBAAoB,GAAG,aAAa,wBAAwB,cAAc,EAAE,CAAC,CAAC,KAAK,IAAI;CACzJ;AACF;AA8BA,SAAgB,SACd,OAUgE;CAChE,OAAO,cAAc,SAChB,UAAU,aAAa,OAAO,MAAM,UAAU,MAAM,SAAS,IAC9D,aAAa,OAAO,CAAC,CAAC;AAC5B;AAEA,MAAM,gBACJ,OACA,UACA,YAAY,OAEZ,MAAM,QAAQ,QAAQ,YACpB,cAAc,OAAO,CAAC,CAAC,KACrB,OAAO,MAAM;CACX,cACE,MAAM,OACJ,UAAuB,EAAE,oBAAoB,YAAY,CAAC,GAC1D,OAAO,oBACL,IAAI,MACF,4EACF,CACF,CACF;CACF,cACE,MAAM,OACJ,UAAuB,EAAE,oBAAoB,YAAY,CAAC,GAC1D,OAAO,IAAI,aAAa;EACtB,MAAM,WAAW,OAAOA;EACxB,MAAM,QAAQ,OAAO,IAAI,IAAI,QAAQ;EACrC,MAAM,UAAUC,gBACd,OACA,UACA,SACF;EACA,IAAI,QAAQ,SAAS,GACnB,OAAO,OAAO,OAAO,IACnB,IAAI,+BAA+B,EAAE,QAAQ,CAAC,CAChD;EAEF,OAAO;IACJ,SAAS;GACV,oBAAoB;GACpB,yBAAyBC,cAA4B,KAAK;GAC1D,0BAA0BC,eAA6B,KAAK;EAC9D;CACF,CAAC,CACH;AACJ,CAAC,CACH,CACF"}
|
package/dist/RegistryItems.d.ts
CHANGED
|
@@ -18,4 +18,11 @@ export declare const functionNames: (items: RegistryItems) => ReadonlyArray<stri
|
|
|
18
18
|
* (stored under `middleware:<key>` entries by `MiddlewareImpl`).
|
|
19
19
|
*/
|
|
20
20
|
export declare const middlewareKeys: (items: RegistryItems) => ReadonlyArray<string>;
|
|
21
|
+
export interface MissingMiddleware {
|
|
22
|
+
readonly functionName: string;
|
|
23
|
+
readonly middlewareKey: string;
|
|
24
|
+
}
|
|
25
|
+
export declare const missingRequired: (items: RegistryItems, required: ReadonlyArray<{
|
|
26
|
+
readonly key: string;
|
|
27
|
+
}>, groupPath?: string) => ReadonlyArray<MissingMiddleware>;
|
|
21
28
|
//# sourceMappingURL=RegistryItems.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegistryItems.d.ts","sourceRoot":"","sources":["../src/RegistryItems.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,UAAW,aAAa,KAAG,aAAa,CAAC,MAAM,CAOtE,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,cAAc,UAAW,aAAa,KAAG,aAAa,CAAC,MAAM,CAKvE,CAAC"}
|
|
1
|
+
{"version":3,"file":"RegistryItems.d.ts","sourceRoot":"","sources":["../src/RegistryItems.ts"],"names":[],"mappings":"AAMA;;;;GAIG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;CACjC;AAED;;;;;GAKG;AACH,eAAO,MAAM,aAAa,UAAW,aAAa,KAAG,aAAa,CAAC,MAAM,CAOtE,CAAC;AAEJ;;;GAGG;AACH,eAAO,MAAM,cAAc,UAAW,aAAa,KAAG,aAAa,CAAC,MAAM,CAKvE,CAAC;AAEJ,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;CAChC;AAED,eAAO,MAAM,eAAe,UACnB,aAAa,YACV,aAAa,CAAC;IAAE,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAA;CAAE,CAAC,yBAEhD,aAAa,CAAC,iBAAiB,CAoB7B,CAAC"}
|
package/dist/RegistryItems.js
CHANGED
|
@@ -7,7 +7,8 @@ import * as Record from "effect/Record";
|
|
|
7
7
|
//#region src/RegistryItems.ts
|
|
8
8
|
var RegistryItems_exports = /* @__PURE__ */ __exportAll({
|
|
9
9
|
functionNames: () => functionNames,
|
|
10
|
-
middlewareKeys: () => middlewareKeys
|
|
10
|
+
middlewareKeys: () => middlewareKeys,
|
|
11
|
+
missingRequired: () => missingRequired
|
|
11
12
|
});
|
|
12
13
|
/**
|
|
13
14
|
* The names of the function-shaped entries in a group's (flat, isolated)
|
|
@@ -21,7 +22,11 @@ const functionNames = (items) => pipe(Record.toEntries(items), Array.filter(([,
|
|
|
21
22
|
* (stored under `middleware:<key>` entries by `MiddlewareImpl`).
|
|
22
23
|
*/
|
|
23
24
|
const middlewareKeys = (items) => pipe(Record.values(items), Array.filter(isMiddlewareRegistryItem), Array.map((item) => item.middlewareSpec.key));
|
|
25
|
+
const missingRequired = (items, required, groupPath = "") => Object.entries(items).sort(([left], [right]) => left.localeCompare(right)).flatMap(([functionName, value]) => isFunctionRegistryItem(value) ? required.flatMap(({ key }) => value._tag === "Confect" && value.middlewareAttachments.some(({ spec }) => spec.key === key) ? [] : [{
|
|
26
|
+
functionName: groupPath === "" ? functionName : `${groupPath}.${functionName}`,
|
|
27
|
+
middlewareKey: key
|
|
28
|
+
}]) : []);
|
|
24
29
|
//#endregion
|
|
25
|
-
export { RegistryItems_exports, functionNames, middlewareKeys };
|
|
30
|
+
export { RegistryItems_exports, functionNames, middlewareKeys, missingRequired };
|
|
26
31
|
|
|
27
32
|
//# sourceMappingURL=RegistryItems.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RegistryItems.js","names":["FunctionRegistryItem.isFunctionRegistryItem","MiddlewareRegistryItem.isMiddlewareRegistryItem"],"sources":["../src/RegistryItems.ts"],"sourcesContent":["import * as Array from \"effect/Array\";\nimport { pipe } from \"effect/Function\";\nimport * as Record from \"effect/Record\";\nimport * as FunctionRegistryItem from \"./FunctionRegistryItem.js\";\nimport * as MiddlewareRegistryItem from \"./MiddlewareRegistryItem.js\";\n\n/**\n * Runtime tree that mirrors a `Spec`'s group structure. Values remain unknown\n * at this boundary so producers and consumers can narrow branches and registry\n * items as needed.\n */\nexport interface RegistryItems {\n readonly [key: string]: unknown;\n}\n\n/**\n * The names of the function-shaped entries in a group's (flat, isolated)\n * registry. `FunctionImpl.make` registers each function under a single-segment\n * key, so the registry built for one group contains exactly that group's\n * functions at the top level.\n */\nexport const functionNames = (items: RegistryItems): ReadonlyArray<string> =>\n pipe(\n Record.toEntries(items),\n Array.filter(([, value]) =>\n FunctionRegistryItem.isFunctionRegistryItem(value),\n ),\n Array.map(([name]) => name),\n );\n\n/**\n * The keys of the middleware implementations registered into a group's registry\n * (stored under `middleware:<key>` entries by `MiddlewareImpl`).\n */\nexport const middlewareKeys = (items: RegistryItems): ReadonlyArray<string> =>\n pipe(\n Record.values(items),\n Array.filter(MiddlewareRegistryItem.isMiddlewareRegistryItem),\n Array.map((item) => item.middlewareSpec.key),\n );\n"],"mappings":"
|
|
1
|
+
{"version":3,"file":"RegistryItems.js","names":["FunctionRegistryItem.isFunctionRegistryItem","MiddlewareRegistryItem.isMiddlewareRegistryItem"],"sources":["../src/RegistryItems.ts"],"sourcesContent":["import * as Array from \"effect/Array\";\nimport { pipe } from \"effect/Function\";\nimport * as Record from \"effect/Record\";\nimport * as FunctionRegistryItem from \"./FunctionRegistryItem.js\";\nimport * as MiddlewareRegistryItem from \"./MiddlewareRegistryItem.js\";\n\n/**\n * Runtime tree that mirrors a `Spec`'s group structure. Values remain unknown\n * at this boundary so producers and consumers can narrow branches and registry\n * items as needed.\n */\nexport interface RegistryItems {\n readonly [key: string]: unknown;\n}\n\n/**\n * The names of the function-shaped entries in a group's (flat, isolated)\n * registry. `FunctionImpl.make` registers each function under a single-segment\n * key, so the registry built for one group contains exactly that group's\n * functions at the top level.\n */\nexport const functionNames = (items: RegistryItems): ReadonlyArray<string> =>\n pipe(\n Record.toEntries(items),\n Array.filter(([, value]) =>\n FunctionRegistryItem.isFunctionRegistryItem(value),\n ),\n Array.map(([name]) => name),\n );\n\n/**\n * The keys of the middleware implementations registered into a group's registry\n * (stored under `middleware:<key>` entries by `MiddlewareImpl`).\n */\nexport const middlewareKeys = (items: RegistryItems): ReadonlyArray<string> =>\n pipe(\n Record.values(items),\n Array.filter(MiddlewareRegistryItem.isMiddlewareRegistryItem),\n Array.map((item) => item.middlewareSpec.key),\n );\n\nexport interface MissingMiddleware {\n readonly functionName: string;\n readonly middlewareKey: string;\n}\n\nexport const missingRequired = (\n items: RegistryItems,\n required: ReadonlyArray<{ readonly key: string }>,\n groupPath = \"\",\n): ReadonlyArray<MissingMiddleware> =>\n Object.entries(items)\n .sort(([left], [right]) => left.localeCompare(right))\n .flatMap(([functionName, value]) =>\n FunctionRegistryItem.isFunctionRegistryItem(value)\n ? required.flatMap(({ key }) =>\n value._tag === \"Confect\" &&\n value.middlewareAttachments.some(({ spec }) => spec.key === key)\n ? []\n : [\n {\n functionName:\n groupPath === \"\"\n ? functionName\n : `${groupPath}.${functionName}`,\n middlewareKey: key,\n },\n ],\n )\n : [],\n );\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,MAAa,iBAAiB,UAC5B,KACE,OAAO,UAAU,KAAK,GACtB,MAAM,QAAQ,GAAG,WACfA,uBAA4C,KAAK,CACnD,GACA,MAAM,KAAK,CAAC,UAAU,IAAI,CAC5B;;;;;AAMF,MAAa,kBAAkB,UAC7B,KACE,OAAO,OAAO,KAAK,GACnB,MAAM,OAAOC,wBAA+C,GAC5D,MAAM,KAAK,SAAS,KAAK,eAAe,GAAG,CAC7C;AAOF,MAAa,mBACX,OACA,UACA,YAAY,OAEZ,OAAO,QAAQ,KAAK,CAAC,CAClB,MAAM,CAAC,OAAO,CAAC,WAAW,KAAK,cAAc,KAAK,CAAC,CAAC,CACpD,SAAS,CAAC,cAAc,WACvBD,uBAA4C,KAAK,IAC7C,SAAS,SAAS,EAAE,UAClB,MAAM,SAAS,aACf,MAAM,sBAAsB,MAAM,EAAE,WAAW,KAAK,QAAQ,GAAG,IAC3D,CAAC,IACD,CACE;CACE,cACE,cAAc,KACV,eACA,GAAG,UAAU,GAAG;CACtB,eAAe;AACjB,CACF,CACN,IACA,CAAC,CACP"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gaganref/confect-server",
|
|
3
|
-
"version": "10.3.1-fork.
|
|
3
|
+
"version": "10.3.1-fork.19",
|
|
4
4
|
"dependencies": {
|
|
5
5
|
"@effect/ai-openai-compat": "4.0.4",
|
|
6
6
|
"@effect/ai-openrouter": "4.0.4"
|
|
7
7
|
},
|
|
8
8
|
"peerDependencies": {
|
|
9
|
-
"@confect/core": "npm:@gaganref/confect-core@10.3.1-fork.
|
|
9
|
+
"@confect/core": "npm:@gaganref/confect-core@10.3.1-fork.19",
|
|
10
10
|
"@effect/platform-node": "^4.0.0",
|
|
11
11
|
"convex": "^1.45.0",
|
|
12
12
|
"effect": "^4.0.4"
|
package/src/GroupImpl.ts
CHANGED
|
@@ -5,6 +5,7 @@ import * as RegistryItems from "./RegistryItems.js";
|
|
|
5
5
|
import * as Array from "effect/Array";
|
|
6
6
|
import * as Context from "effect/Context";
|
|
7
7
|
import * as Effect from "effect/Effect";
|
|
8
|
+
import * as Data from "effect/Data";
|
|
8
9
|
import * as Layer from "effect/Layer";
|
|
9
10
|
import * as Option from "effect/Option";
|
|
10
11
|
import * as Predicate from "effect/Predicate";
|
|
@@ -111,10 +112,20 @@ export const make = <
|
|
|
111
112
|
FunctionImpl.FromGroupSpec<Group> | MiddlewareImpl.FromGroupSpec<Group>
|
|
112
113
|
>;
|
|
113
114
|
|
|
114
|
-
const
|
|
115
|
-
context
|
|
116
|
-
|
|
117
|
-
|
|
115
|
+
const findGroupImpl = <S>(context: Context.Context<S>): Option.Option<Any> =>
|
|
116
|
+
Array.findFirst(context.mapUnsafe.values(), isGroupImpl);
|
|
117
|
+
|
|
118
|
+
export class MissingRequiredMiddlewareError extends Data.TaggedError(
|
|
119
|
+
"MissingRequiredMiddlewareError",
|
|
120
|
+
)<{
|
|
121
|
+
readonly missing: ReadonlyArray<RegistryItems.MissingMiddleware>;
|
|
122
|
+
}> {
|
|
123
|
+
override get message(): string {
|
|
124
|
+
return `GroupImpl.finalize: ${this.missing.map(({ functionName, middlewareKey }) => `${functionName} requires middleware "${middlewareKey}"`).join("\n")}`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
type Finalizable = Layer.Layer<AnyUnfinalized> | Layer.Layer<AnyFinalized>;
|
|
118
129
|
|
|
119
130
|
/**
|
|
120
131
|
* Mark a `GroupImpl` layer as fully implemented. The parameter type defaults
|
|
@@ -127,20 +138,52 @@ const findUnfinalizedGroupImpl = <S>(
|
|
|
127
138
|
* registered into this group's scope are snapshotted onto the produced service
|
|
128
139
|
* value's `registeredFunctionNames` field, so consumers can verify impl
|
|
129
140
|
* completeness against a `GroupSpec`'s expected functions without having to
|
|
130
|
-
* inspect the `Registry` themselves.
|
|
141
|
+
* inspect the `Registry` themselves. Pass `{ required }` to check every
|
|
142
|
+
* registered function's middleware at layer build, including internal
|
|
143
|
+
* functions. This form also accepts a finalized layer so generated registration
|
|
144
|
+
* can enforce the backend declaration without changing authored impls. Pass
|
|
145
|
+
* `groupPath` to qualify uncovered function names in the diagnostic.
|
|
131
146
|
*/
|
|
132
|
-
export
|
|
147
|
+
export function finalize(options: {
|
|
148
|
+
readonly groupPath?: string;
|
|
149
|
+
readonly required: ReadonlyArray<
|
|
150
|
+
Pick<MiddlewareSpec.AnyMiddlewareSpec, "key">
|
|
151
|
+
>;
|
|
152
|
+
}): (group: Finalizable) => Layer.Layer<GroupImpl<"Finalized">>;
|
|
153
|
+
export function finalize(
|
|
133
154
|
group: Layer.Layer<GroupImpl<"Unfinalized">>,
|
|
155
|
+
): Layer.Layer<GroupImpl<"Finalized">>;
|
|
156
|
+
export function finalize(
|
|
157
|
+
input:
|
|
158
|
+
| Finalizable
|
|
159
|
+
| {
|
|
160
|
+
readonly groupPath?: string;
|
|
161
|
+
readonly required: ReadonlyArray<
|
|
162
|
+
Pick<MiddlewareSpec.AnyMiddlewareSpec, "key">
|
|
163
|
+
>;
|
|
164
|
+
},
|
|
165
|
+
):
|
|
166
|
+
| Layer.Layer<GroupImpl<"Finalized">>
|
|
167
|
+
| ((group: Finalizable) => Layer.Layer<GroupImpl<"Finalized">>) {
|
|
168
|
+
return "required" in input
|
|
169
|
+
? (group) => finalizeWith(group, input.required, input.groupPath)
|
|
170
|
+
: finalizeWith(input, []);
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const finalizeWith = (
|
|
174
|
+
group: Layer.Layer<never>,
|
|
175
|
+
required: ReadonlyArray<Pick<MiddlewareSpec.AnyMiddlewareSpec, "key">>,
|
|
176
|
+
groupPath = "",
|
|
134
177
|
): Layer.Layer<GroupImpl<"Finalized">> =>
|
|
135
178
|
Layer.flatMap(group, (context): Layer.Layer<GroupImpl<"Finalized">> =>
|
|
136
|
-
|
|
179
|
+
findGroupImpl(context).pipe(
|
|
137
180
|
Option.match({
|
|
138
181
|
onNone: () =>
|
|
139
182
|
Layer.effect(
|
|
140
183
|
GroupImpl<"Finalized">({ finalizationStatus: "Finalized" }),
|
|
141
184
|
Effect.die(
|
|
142
185
|
new Error(
|
|
143
|
-
"GroupImpl.finalize: no
|
|
186
|
+
"GroupImpl.finalize: no GroupImpl service was found in the layer's context.",
|
|
144
187
|
),
|
|
145
188
|
),
|
|
146
189
|
),
|
|
@@ -150,6 +193,16 @@ export const finalize = (
|
|
|
150
193
|
Effect.gen(function* () {
|
|
151
194
|
const registry = yield* Registry.Registry;
|
|
152
195
|
const items = yield* Ref.get(registry);
|
|
196
|
+
const missing = RegistryItems.missingRequired(
|
|
197
|
+
items,
|
|
198
|
+
required,
|
|
199
|
+
groupPath,
|
|
200
|
+
);
|
|
201
|
+
if (missing.length > 0) {
|
|
202
|
+
return yield* Effect.die(
|
|
203
|
+
new MissingRequiredMiddlewareError({ missing }),
|
|
204
|
+
);
|
|
205
|
+
}
|
|
153
206
|
return {
|
|
154
207
|
[TypeId]: TypeId,
|
|
155
208
|
finalizationStatus: "Finalized" as const,
|
package/src/RegistryItems.ts
CHANGED
|
@@ -38,3 +38,34 @@ export const middlewareKeys = (items: RegistryItems): ReadonlyArray<string> =>
|
|
|
38
38
|
Array.filter(MiddlewareRegistryItem.isMiddlewareRegistryItem),
|
|
39
39
|
Array.map((item) => item.middlewareSpec.key),
|
|
40
40
|
);
|
|
41
|
+
|
|
42
|
+
export interface MissingMiddleware {
|
|
43
|
+
readonly functionName: string;
|
|
44
|
+
readonly middlewareKey: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export const missingRequired = (
|
|
48
|
+
items: RegistryItems,
|
|
49
|
+
required: ReadonlyArray<{ readonly key: string }>,
|
|
50
|
+
groupPath = "",
|
|
51
|
+
): ReadonlyArray<MissingMiddleware> =>
|
|
52
|
+
Object.entries(items)
|
|
53
|
+
.sort(([left], [right]) => left.localeCompare(right))
|
|
54
|
+
.flatMap(([functionName, value]) =>
|
|
55
|
+
FunctionRegistryItem.isFunctionRegistryItem(value)
|
|
56
|
+
? required.flatMap(({ key }) =>
|
|
57
|
+
value._tag === "Confect" &&
|
|
58
|
+
value.middlewareAttachments.some(({ spec }) => spec.key === key)
|
|
59
|
+
? []
|
|
60
|
+
: [
|
|
61
|
+
{
|
|
62
|
+
functionName:
|
|
63
|
+
groupPath === ""
|
|
64
|
+
? functionName
|
|
65
|
+
: `${groupPath}.${functionName}`,
|
|
66
|
+
middlewareKey: key,
|
|
67
|
+
},
|
|
68
|
+
],
|
|
69
|
+
)
|
|
70
|
+
: [],
|
|
71
|
+
);
|