@confect/server 1.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 (157) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/LICENSE +7 -0
  3. package/dist/ActionCtx.d.ts +12 -0
  4. package/dist/ActionCtx.d.ts.map +1 -0
  5. package/dist/ActionCtx.js +10 -0
  6. package/dist/ActionCtx.js.map +1 -0
  7. package/dist/ActionRunner.d.ts +15 -0
  8. package/dist/ActionRunner.d.ts.map +1 -0
  9. package/dist/ActionRunner.js +23 -0
  10. package/dist/ActionRunner.js.map +1 -0
  11. package/dist/Api.d.ts +27 -0
  12. package/dist/Api.d.ts.map +1 -0
  13. package/dist/Api.js +26 -0
  14. package/dist/Api.js.map +1 -0
  15. package/dist/Auth.d.ts +30 -0
  16. package/dist/Auth.d.ts.map +1 -0
  17. package/dist/Auth.js +24 -0
  18. package/dist/Auth.js.map +1 -0
  19. package/dist/DataModel.d.ts +33 -0
  20. package/dist/DataModel.d.ts.map +1 -0
  21. package/dist/DataModel.js +6 -0
  22. package/dist/DataModel.js.map +1 -0
  23. package/dist/DatabaseReader.d.ts +73 -0
  24. package/dist/DatabaseReader.d.ts.map +1 -0
  25. package/dist/DatabaseReader.js +32 -0
  26. package/dist/DatabaseReader.js.map +1 -0
  27. package/dist/DatabaseSchema.d.ts +139 -0
  28. package/dist/DatabaseSchema.d.ts.map +1 -0
  29. package/dist/DatabaseSchema.js +45 -0
  30. package/dist/DatabaseSchema.js.map +1 -0
  31. package/dist/DatabaseWriter.d.ts +39 -0
  32. package/dist/DatabaseWriter.d.ts.map +1 -0
  33. package/dist/DatabaseWriter.js +43 -0
  34. package/dist/DatabaseWriter.js.map +1 -0
  35. package/dist/Document.d.ts +47 -0
  36. package/dist/Document.d.ts.map +1 -0
  37. package/dist/Document.js +66 -0
  38. package/dist/Document.js.map +1 -0
  39. package/dist/FunctionImpl.d.ts +34 -0
  40. package/dist/FunctionImpl.d.ts.map +1 -0
  41. package/dist/FunctionImpl.js +35 -0
  42. package/dist/FunctionImpl.js.map +1 -0
  43. package/dist/GroupImpl.d.ts +24 -0
  44. package/dist/GroupImpl.d.ts.map +1 -0
  45. package/dist/GroupImpl.js +14 -0
  46. package/dist/GroupImpl.js.map +1 -0
  47. package/dist/Handler.d.ts +31 -0
  48. package/dist/Handler.d.ts.map +1 -0
  49. package/dist/Handler.js +6 -0
  50. package/dist/Handler.js.map +1 -0
  51. package/dist/HttpApi.d.ts +26 -0
  52. package/dist/HttpApi.d.ts.map +1 -0
  53. package/dist/HttpApi.js +74 -0
  54. package/dist/HttpApi.js.map +1 -0
  55. package/dist/Impl.d.ts +24 -0
  56. package/dist/Impl.d.ts.map +1 -0
  57. package/dist/Impl.js +28 -0
  58. package/dist/Impl.js.map +1 -0
  59. package/dist/MutationCtx.d.ts +12 -0
  60. package/dist/MutationCtx.d.ts.map +1 -0
  61. package/dist/MutationCtx.js +10 -0
  62. package/dist/MutationCtx.js.map +1 -0
  63. package/dist/MutationRunner.d.ts +24 -0
  64. package/dist/MutationRunner.d.ts.map +1 -0
  65. package/dist/MutationRunner.js +33 -0
  66. package/dist/MutationRunner.js.map +1 -0
  67. package/dist/OrderedQuery.d.ts +23 -0
  68. package/dist/OrderedQuery.d.ts.map +1 -0
  69. package/dist/OrderedQuery.js +34 -0
  70. package/dist/OrderedQuery.js.map +1 -0
  71. package/dist/QueryCtx.d.ts +12 -0
  72. package/dist/QueryCtx.d.ts.map +1 -0
  73. package/dist/QueryCtx.js +10 -0
  74. package/dist/QueryCtx.js.map +1 -0
  75. package/dist/QueryInitializer.d.ts +49 -0
  76. package/dist/QueryInitializer.d.ts.map +1 -0
  77. package/dist/QueryInitializer.js +83 -0
  78. package/dist/QueryInitializer.js.map +1 -0
  79. package/dist/QueryRunner.d.ts +14 -0
  80. package/dist/QueryRunner.d.ts.map +1 -0
  81. package/dist/QueryRunner.js +23 -0
  82. package/dist/QueryRunner.js.map +1 -0
  83. package/dist/RegisteredFunctions.d.ts +66 -0
  84. package/dist/RegisteredFunctions.d.ts.map +1 -0
  85. package/dist/RegisteredFunctions.js +71 -0
  86. package/dist/RegisteredFunctions.js.map +1 -0
  87. package/dist/Registry.d.ts +15 -0
  88. package/dist/Registry.d.ts.map +1 -0
  89. package/dist/Registry.js +10 -0
  90. package/dist/Registry.js.map +1 -0
  91. package/dist/RegistryItem.d.ts +31 -0
  92. package/dist/RegistryItem.d.ts.map +1 -0
  93. package/dist/RegistryItem.js +20 -0
  94. package/dist/RegistryItem.js.map +1 -0
  95. package/dist/Scheduler.d.ts +23 -0
  96. package/dist/Scheduler.d.ts.map +1 -0
  97. package/dist/Scheduler.js +24 -0
  98. package/dist/Scheduler.js.map +1 -0
  99. package/dist/SchemaToValidator.d.ts +88 -0
  100. package/dist/SchemaToValidator.d.ts.map +1 -0
  101. package/dist/SchemaToValidator.js +155 -0
  102. package/dist/SchemaToValidator.js.map +1 -0
  103. package/dist/Storage.d.ts +69 -0
  104. package/dist/Storage.d.ts.map +1 -0
  105. package/dist/Storage.js +46 -0
  106. package/dist/Storage.js.map +1 -0
  107. package/dist/Table.d.ts +247 -0
  108. package/dist/Table.d.ts.map +1 -0
  109. package/dist/Table.js +97 -0
  110. package/dist/Table.js.map +1 -0
  111. package/dist/TableInfo.d.ts +48 -0
  112. package/dist/TableInfo.d.ts.map +1 -0
  113. package/dist/TableInfo.js +6 -0
  114. package/dist/TableInfo.js.map +1 -0
  115. package/dist/VectorSearch.d.ts +42 -0
  116. package/dist/VectorSearch.d.ts.map +1 -0
  117. package/dist/VectorSearch.js +16 -0
  118. package/dist/VectorSearch.js.map +1 -0
  119. package/dist/_virtual/rolldown_runtime.js +13 -0
  120. package/dist/index.d.ts +30 -0
  121. package/dist/index.js +31 -0
  122. package/dist/internal/utils.d.ts +15 -0
  123. package/dist/internal/utils.d.ts.map +1 -0
  124. package/dist/internal/utils.js +49 -0
  125. package/dist/internal/utils.js.map +1 -0
  126. package/package.json +90 -0
  127. package/src/ActionCtx.ts +9 -0
  128. package/src/ActionRunner.ts +28 -0
  129. package/src/Api.ts +63 -0
  130. package/src/Auth.ts +31 -0
  131. package/src/DataModel.ts +69 -0
  132. package/src/DatabaseReader.ts +75 -0
  133. package/src/DatabaseSchema.ts +134 -0
  134. package/src/DatabaseWriter.ts +166 -0
  135. package/src/Document.ts +200 -0
  136. package/src/FunctionImpl.ts +112 -0
  137. package/src/GroupImpl.ts +60 -0
  138. package/src/Handler.ts +105 -0
  139. package/src/HttpApi.ts +232 -0
  140. package/src/Impl.ts +57 -0
  141. package/src/MutationCtx.ts +11 -0
  142. package/src/MutationRunner.ts +41 -0
  143. package/src/OrderedQuery.ts +109 -0
  144. package/src/QueryCtx.ts +9 -0
  145. package/src/QueryInitializer.ts +308 -0
  146. package/src/QueryRunner.ts +29 -0
  147. package/src/RegisteredFunctions.ts +381 -0
  148. package/src/Registry.ts +13 -0
  149. package/src/RegistryItem.ts +44 -0
  150. package/src/Scheduler.ts +39 -0
  151. package/src/SchemaToValidator.ts +619 -0
  152. package/src/Storage.ts +86 -0
  153. package/src/Table.ts +439 -0
  154. package/src/TableInfo.ts +91 -0
  155. package/src/VectorSearch.ts +46 -0
  156. package/src/index.ts +29 -0
  157. package/src/internal/utils.ts +87 -0
@@ -0,0 +1,112 @@
1
+ import type * as FunctionSpec from "@confect/core/FunctionSpec";
2
+ import type * as GroupPath from "@confect/core/GroupPath";
3
+ import type * as GroupSpec from "@confect/core/GroupSpec";
4
+ import { Array, Context, Effect, Layer, Ref, String } from "effect";
5
+ import type * as Api from "./Api";
6
+ import type * as Handler from "./Handler";
7
+ import { setNestedProperty } from "./internal/utils";
8
+ import * as Registry from "./Registry";
9
+ import * as RegistryItem from "./RegistryItem";
10
+
11
+ export interface FunctionImpl<
12
+ GroupPath_ extends string,
13
+ FunctionName extends string,
14
+ > {
15
+ readonly groupPath: GroupPath_;
16
+ readonly functionName: FunctionName;
17
+ }
18
+
19
+ export const FunctionImpl = <
20
+ GroupPath_ extends string,
21
+ FunctionName extends string,
22
+ >({
23
+ groupPath,
24
+ functionName,
25
+ }: {
26
+ groupPath: GroupPath_;
27
+ functionName: FunctionName;
28
+ }) =>
29
+ Context.GenericTag<FunctionImpl<GroupPath_, FunctionName>>(
30
+ `@confect/server/FunctionImpl/${groupPath}/${functionName}`,
31
+ );
32
+
33
+ export const make = <
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
+ >,
39
+ >(
40
+ api: Api_,
41
+ groupPath: GroupPath_,
42
+ functionName: FunctionName,
43
+ handler: Handler.WithName<
44
+ Api.Schema<Api_>,
45
+ GroupSpec.Functions<GroupPath.GroupAt<Api.Groups<Api_>, GroupPath_>>,
46
+ FunctionName
47
+ >,
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 function_ = group_.functions[functionName]!;
60
+
61
+ return Layer.effect(
62
+ FunctionImpl<GroupPath_, FunctionName>({
63
+ groupPath,
64
+ functionName,
65
+ }),
66
+ Effect.gen(function* () {
67
+ const registry = yield* Registry.Registry;
68
+
69
+ yield* Ref.update(registry, (registryItems) =>
70
+ setNestedProperty(
71
+ registryItems,
72
+ [...groupPathParts, functionName],
73
+ RegistryItem.make({
74
+ function_,
75
+ handler: handler as Handler.AnyWithProps,
76
+ }),
77
+ ),
78
+ );
79
+
80
+ return {
81
+ groupPath,
82
+ functionName,
83
+ };
84
+ }),
85
+ );
86
+ };
87
+
88
+ /**
89
+ * Get the function implementation service type for a specific group path and function name.
90
+ */
91
+ export type ForGroupPathAndFunction<
92
+ GroupPath_ extends string,
93
+ FunctionName extends string,
94
+ > = FunctionImpl<GroupPath_, FunctionName>;
95
+
96
+ /**
97
+ * Get all function implementation services required for a group at a given path.
98
+ */
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
111
+ : never
112
+ : never;
@@ -0,0 +1,60 @@
1
+ import type * as GroupPath from "@confect/core/GroupPath";
2
+ import type * as GroupSpec from "@confect/core/GroupSpec";
3
+ import { Context, Layer } from "effect";
4
+ import type * as Api from "./Api";
5
+ import type * as FunctionImpl from "./FunctionImpl";
6
+
7
+ export interface GroupImpl<GroupPath_ extends string> {
8
+ readonly groupPath: GroupPath_;
9
+ }
10
+
11
+ export const GroupImpl = <GroupPath_ extends string>({
12
+ groupPath,
13
+ }: {
14
+ groupPath: GroupPath_;
15
+ }) =>
16
+ Context.GenericTag<GroupImpl<GroupPath_>>(
17
+ `@confect/server/GroupImpl/${groupPath}`,
18
+ );
19
+
20
+ export const make = <
21
+ Api_ extends Api.AnyWithProps,
22
+ const GroupPath_ extends GroupPath.All<Api.Groups<Api_>>,
23
+ >(
24
+ _api: Api_,
25
+ groupPath: GroupPath_,
26
+ ): Layer.Layer<
27
+ GroupImpl<GroupPath_>,
28
+ never,
29
+ | FromGroupWithPath<GroupPath_, Api.Groups<Api_>>
30
+ | FunctionImpl.FromGroupAtPath<GroupPath_, Api.Groups<Api_>>
31
+ > =>
32
+ Layer.succeed(
33
+ GroupImpl<GroupPath_>({
34
+ groupPath,
35
+ }),
36
+ {
37
+ groupPath,
38
+ },
39
+ ) as Layer.Layer<
40
+ GroupImpl<GroupPath_>,
41
+ never,
42
+ | FromGroupWithPath<GroupPath_, Api.Groups<Api_>>
43
+ | FunctionImpl.FromGroupAtPath<GroupPath_, Api.Groups<Api_>>
44
+ >;
45
+
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;
51
+
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;
package/src/Handler.ts ADDED
@@ -0,0 +1,105 @@
1
+ import type { FunctionSpec } from "@confect/core";
2
+ import type { Effect } from "effect";
3
+ import type * as ActionCtx from "./ActionCtx";
4
+ import type * as ActionRunner from "./ActionRunner";
5
+ import type * as Auth from "./Auth";
6
+ import type * as DatabaseReader from "./DatabaseReader";
7
+ import type * as DatabaseSchema from "./DatabaseSchema";
8
+ import type * as DatabaseWriter from "./DatabaseWriter";
9
+ import type * as DataModel from "./DataModel";
10
+ import type * as MutationCtx from "./MutationCtx";
11
+ import type * as MutationRunner from "./MutationRunner";
12
+ import type * as QueryCtx from "./QueryCtx";
13
+ import type * as QueryRunner from "./QueryRunner";
14
+ import type * as Scheduler from "./Scheduler";
15
+ import type {
16
+ StorageActionWriter,
17
+ StorageReader,
18
+ StorageWriter,
19
+ } from "./Storage";
20
+ import type * as VectorSearch from "./VectorSearch";
21
+
22
+ export type Handler<
23
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
24
+ FunctionSpec_ extends FunctionSpec.AnyWithProps,
25
+ > =
26
+ FunctionSpec_ extends FunctionSpec.WithFunctionType<FunctionSpec_, "query">
27
+ ? Query<DatabaseSchema_, FunctionSpec_>
28
+ : FunctionSpec_ extends FunctionSpec.WithFunctionType<
29
+ FunctionSpec_,
30
+ "mutation"
31
+ >
32
+ ? Mutation<DatabaseSchema_, FunctionSpec_>
33
+ : FunctionSpec_ extends FunctionSpec.WithFunctionType<
34
+ FunctionSpec_,
35
+ "action"
36
+ >
37
+ ? Action<DatabaseSchema_, FunctionSpec_>
38
+ : never;
39
+
40
+ export type Query<
41
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
42
+ FunctionSpec_ extends FunctionSpec.AnyWithPropsWithFunctionType<"query">,
43
+ > = Base<
44
+ FunctionSpec_,
45
+ | DatabaseReader.DatabaseReader<DatabaseSchema_>
46
+ | Auth.Auth
47
+ | StorageReader
48
+ | QueryRunner.QueryRunner
49
+ | QueryCtx.QueryCtx<DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>>
50
+ >;
51
+
52
+ export type Mutation<
53
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
54
+ FunctionSpec_ extends FunctionSpec.AnyWithPropsWithFunctionType<"mutation">,
55
+ > = Base<
56
+ FunctionSpec_,
57
+ | DatabaseReader.DatabaseReader<DatabaseSchema_>
58
+ | DatabaseWriter.DatabaseWriter<DatabaseSchema_>
59
+ | Auth.Auth
60
+ | Scheduler.Scheduler
61
+ | StorageReader
62
+ | StorageWriter
63
+ | QueryRunner.QueryRunner
64
+ | MutationRunner.MutationRunner
65
+ | MutationCtx.MutationCtx<
66
+ DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>
67
+ >
68
+ >;
69
+
70
+ export type Action<
71
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
72
+ FunctionSpec_ extends FunctionSpec.AnyWithPropsWithFunctionType<"action">,
73
+ > = Base<
74
+ FunctionSpec_,
75
+ | Scheduler.Scheduler
76
+ | Auth.Auth
77
+ | StorageReader
78
+ | StorageWriter
79
+ | StorageActionWriter
80
+ | QueryRunner.QueryRunner
81
+ | MutationRunner.MutationRunner
82
+ | ActionRunner.ActionRunner
83
+ | VectorSearch.VectorSearch<DataModel.FromSchema<DatabaseSchema_>>
84
+ | ActionCtx.ActionCtx<
85
+ DataModel.ToConvex<DataModel.FromSchema<DatabaseSchema_>>
86
+ >
87
+ >;
88
+
89
+ type Base<FunctionSpec_ extends FunctionSpec.AnyWithProps, R> = (
90
+ args: FunctionSpec.Args<FunctionSpec_>["Type"],
91
+ ) => Effect.Effect<FunctionSpec.Returns<FunctionSpec_>["Type"], never, R>;
92
+
93
+ export type AnyWithProps = Handler<
94
+ DatabaseSchema.AnyWithProps,
95
+ FunctionSpec.AnyWithProps
96
+ >;
97
+
98
+ export type WithName<
99
+ DatabaseSchema_ extends DatabaseSchema.AnyWithProps,
100
+ FunctionSpec_ extends FunctionSpec.AnyWithProps,
101
+ FunctionName extends string,
102
+ > = Handler<
103
+ DatabaseSchema_,
104
+ FunctionSpec.WithName<FunctionSpec_, FunctionName>
105
+ >;
package/src/HttpApi.ts ADDED
@@ -0,0 +1,232 @@
1
+ import {
2
+ type HttpApi,
3
+ HttpApiBuilder,
4
+ HttpApiScalar,
5
+ type HttpApp,
6
+ type HttpRouter,
7
+ HttpServer,
8
+ } from "@effect/platform";
9
+ import {
10
+ type HttpRouter as ConvexHttpRouter,
11
+ type GenericActionCtx,
12
+ type GenericDataModel,
13
+ httpActionGeneric,
14
+ httpRouter,
15
+ ROUTABLE_HTTP_METHODS,
16
+ type RouteSpecWithPathPrefix,
17
+ } from "convex/server";
18
+ import { Array, Layer, pipe, Record } from "effect";
19
+ import * as ActionRunner from "./ActionRunner";
20
+ import * as Auth from "./Auth";
21
+ import * as MutationRunner from "./MutationRunner";
22
+ import * as QueryRunner from "./QueryRunner";
23
+ import * as Scheduler from "./Scheduler";
24
+ import { StorageActionWriter, StorageReader, StorageWriter } from "./Storage";
25
+ import * as ActionCtx from "./ActionCtx";
26
+
27
+ type Middleware = (
28
+ httpApp: HttpApp.Default,
29
+ ) => HttpApp.Default<
30
+ never,
31
+ HttpApi.Api | HttpApiBuilder.Router | HttpRouter.HttpRouter.DefaultServices
32
+ >;
33
+
34
+ const makeHandler =
35
+ <DataModel extends GenericDataModel>({
36
+ pathPrefix,
37
+ apiLive,
38
+ middleware,
39
+ scalar,
40
+ }: {
41
+ pathPrefix: RoutePath;
42
+ apiLive: Layer.Layer<
43
+ HttpApi.Api,
44
+ never,
45
+ | QueryRunner.QueryRunner
46
+ | MutationRunner.MutationRunner
47
+ | ActionRunner.ActionRunner
48
+ | Scheduler.Scheduler
49
+ | Auth.Auth
50
+ | StorageReader
51
+ | StorageWriter
52
+ | StorageActionWriter
53
+ | GenericActionCtx<DataModel>
54
+ >;
55
+ middleware?: Middleware;
56
+ scalar?: HttpApiScalar.ScalarConfig;
57
+ }) =>
58
+ (ctx: GenericActionCtx<DataModel>, request: Request): Promise<Response> => {
59
+ const ApiLive = apiLive.pipe(
60
+ Layer.provide(
61
+ Layer.mergeAll(
62
+ QueryRunner.layer(ctx.runQuery),
63
+ MutationRunner.layer(ctx.runMutation),
64
+ ActionRunner.layer(ctx.runAction),
65
+ Scheduler.layer(ctx.scheduler),
66
+ Auth.layer(ctx.auth),
67
+ StorageReader.layer(ctx.storage),
68
+ StorageWriter.layer(ctx.storage),
69
+ StorageActionWriter.layer(ctx.storage),
70
+ Layer.succeed(ActionCtx.ActionCtx<DataModel>(), ctx),
71
+ ),
72
+ ),
73
+ );
74
+
75
+ const ApiDocsLive = HttpApiScalar.layer({
76
+ path: `${pathPrefix}docs`,
77
+ scalar: {
78
+ baseServerURL: `${process.env["CONVEX_SITE_URL"]}${pathPrefix}`,
79
+ ...scalar,
80
+ },
81
+ }).pipe(Layer.provide(ApiLive));
82
+
83
+ const EnvLive = Layer.mergeAll(
84
+ ApiLive,
85
+ ApiDocsLive,
86
+ HttpServer.layerContext,
87
+ );
88
+
89
+ const { handler } = HttpApiBuilder.toWebHandler(
90
+ EnvLive,
91
+ middleware ? { middleware } : {},
92
+ );
93
+
94
+ return handler(request);
95
+ };
96
+
97
+ const makeHttpAction = <DataModel extends GenericDataModel>({
98
+ pathPrefix,
99
+ apiLive,
100
+ middleware,
101
+ scalar,
102
+ }: {
103
+ pathPrefix: RoutePath;
104
+ apiLive: Layer.Layer<
105
+ HttpApi.Api,
106
+ never,
107
+ | QueryRunner.QueryRunner
108
+ | MutationRunner.MutationRunner
109
+ | ActionRunner.ActionRunner
110
+ | Scheduler.Scheduler
111
+ | Auth.Auth
112
+ | StorageReader
113
+ | StorageWriter
114
+ | StorageActionWriter
115
+ | GenericActionCtx<DataModel>
116
+ >;
117
+ middleware?: Middleware;
118
+ scalar?: HttpApiScalar.ScalarConfig;
119
+ }) =>
120
+ httpActionGeneric(
121
+ makeHandler<DataModel>({
122
+ pathPrefix,
123
+ apiLive,
124
+ ...(middleware ? { middleware } : {}),
125
+ ...(scalar ? { scalar } : {}),
126
+ }) as unknown as (
127
+ ctx: GenericActionCtx<GenericDataModel>,
128
+ request: Request,
129
+ ) => Promise<Response>,
130
+ );
131
+
132
+ export type HttpApi_ = {
133
+ apiLive: Layer.Layer<
134
+ HttpApi.Api,
135
+ never,
136
+ | QueryRunner.QueryRunner
137
+ | MutationRunner.MutationRunner
138
+ | ActionRunner.ActionRunner
139
+ | Scheduler.Scheduler
140
+ | Auth.Auth
141
+ | StorageReader
142
+ | StorageWriter
143
+ | StorageActionWriter
144
+ >;
145
+ middleware?: Middleware;
146
+ scalar?: HttpApiScalar.ScalarConfig;
147
+ };
148
+
149
+ export type RoutePath = "/" | `/${string}/`;
150
+
151
+ const mountEffectHttpApi =
152
+ <DataModel extends GenericDataModel>({
153
+ pathPrefix,
154
+ apiLive,
155
+ middleware,
156
+ scalar,
157
+ }: {
158
+ pathPrefix: RoutePath;
159
+ apiLive: Layer.Layer<
160
+ HttpApi.Api,
161
+ never,
162
+ | QueryRunner.QueryRunner
163
+ | MutationRunner.MutationRunner
164
+ | ActionRunner.ActionRunner
165
+ | Scheduler.Scheduler
166
+ | Auth.Auth
167
+ | StorageReader
168
+ | StorageWriter
169
+ | StorageActionWriter
170
+ | GenericActionCtx<DataModel>
171
+ >;
172
+ middleware?: Middleware;
173
+ scalar?: HttpApiScalar.ScalarConfig;
174
+ }) =>
175
+ (convexHttpRouter: ConvexHttpRouter): ConvexHttpRouter => {
176
+ const handler = makeHttpAction<DataModel>({
177
+ pathPrefix,
178
+ apiLive,
179
+ ...(middleware ? { middleware } : {}),
180
+ ...(scalar ? { scalar } : {}),
181
+ });
182
+
183
+ Array.forEach(ROUTABLE_HTTP_METHODS, (method) => {
184
+ const routeSpec: RouteSpecWithPathPrefix = {
185
+ pathPrefix,
186
+ method,
187
+ handler,
188
+ };
189
+ convexHttpRouter.route(routeSpec);
190
+ });
191
+
192
+ return convexHttpRouter;
193
+ };
194
+
195
+ type HttpApis = Partial<Record<RoutePath, HttpApi_>>;
196
+
197
+ export const make = (httpApis: HttpApis): ConvexHttpRouter => {
198
+ applyMonkeyPatches();
199
+
200
+ return pipe(
201
+ httpApis as Record<RoutePath, HttpApi_>,
202
+ Record.toEntries,
203
+ Array.reduce(
204
+ httpRouter(),
205
+ (convexHttpRouter, [pathPrefix, { apiLive, middleware, scalar }]) =>
206
+ mountEffectHttpApi({
207
+ pathPrefix: pathPrefix as RoutePath,
208
+ apiLive,
209
+ ...(middleware ? { middleware } : {}),
210
+ ...(scalar ? { scalar } : {}),
211
+ })(convexHttpRouter),
212
+ ),
213
+ );
214
+ };
215
+
216
+ const applyMonkeyPatches = () => {
217
+ // These are necessary until the Convex runtime supports these APIs. See https://discord.com/channels/1019350475847499849/1281364098419785760
218
+
219
+ // eslint-disable-next-line no-global-assign
220
+ URL = class extends URL {
221
+ override get username() {
222
+ return "";
223
+ }
224
+ override get password() {
225
+ return "";
226
+ }
227
+ };
228
+
229
+ Object.defineProperty(Request.prototype, "signal", {
230
+ get: () => new AbortSignal(),
231
+ });
232
+ };
package/src/Impl.ts ADDED
@@ -0,0 +1,57 @@
1
+ import { Context, Effect, Layer, Predicate } from "effect";
2
+ import type * as Api from "./Api";
3
+ import type { Groups as ApiGroups } from "./Api";
4
+ import type * as GroupImpl from "./GroupImpl";
5
+
6
+ export const TypeId = "@confect/server/Impl";
7
+ export type TypeId = typeof TypeId;
8
+
9
+ export const isImpl = (u: unknown): u is AnyWithProps =>
10
+ Predicate.hasProperty(u, TypeId);
11
+
12
+ export interface Impl<
13
+ Api_ extends Api.AnyWithProps,
14
+ FinalizationStatus_ extends FinalizationStatus,
15
+ > {
16
+ readonly [TypeId]: TypeId;
17
+ readonly api: Api_;
18
+ readonly finalizationStatus: FinalizationStatus_;
19
+ }
20
+
21
+ export type FinalizationStatus = "Unfinalized" | "Finalized";
22
+
23
+ export interface AnyWithProps
24
+ extends Impl<Api.AnyWithProps, FinalizationStatus> {}
25
+
26
+ export const Impl = <
27
+ Api_ extends Api.AnyWithProps,
28
+ FinalizationStatus_ extends FinalizationStatus,
29
+ >() =>
30
+ Context.GenericTag<Impl<Api_, FinalizationStatus_>>(`@confect/server/Impl`);
31
+
32
+ export const make = <Api_ extends Api.AnyWithProps>(
33
+ api: Api_,
34
+ ): Layer.Layer<
35
+ Impl<Api_, "Unfinalized">,
36
+ never,
37
+ GroupImpl.FromGroups<ApiGroups<Api_>>
38
+ > =>
39
+ Layer.effect(
40
+ Impl<Api_, "Unfinalized">(),
41
+ Effect.succeed({
42
+ [TypeId]: TypeId,
43
+ api,
44
+ finalizationStatus: "Unfinalized" as const,
45
+ }),
46
+ );
47
+
48
+ export const finalize = <Api_ extends Api.AnyWithProps>(
49
+ impl: Layer.Layer<Impl<Api_, "Unfinalized">>,
50
+ ): Layer.Layer<Impl<Api_, "Finalized">> =>
51
+ Layer.map(impl, (context) =>
52
+ Context.make(Impl<Api_, "Finalized">(), {
53
+ [TypeId]: TypeId,
54
+ api: Context.get(context, Impl<Api_, "Unfinalized">()).api,
55
+ finalizationStatus: "Finalized",
56
+ }),
57
+ );
@@ -0,0 +1,11 @@
1
+ import type { GenericDataModel, GenericMutationCtx } from "convex/server";
2
+ import { Context } from "effect";
3
+
4
+ export const MutationCtx = <DataModel extends GenericDataModel>() =>
5
+ Context.GenericTag<GenericMutationCtx<DataModel>>(
6
+ "@confect/server/MutationCtx",
7
+ );
8
+
9
+ export type MutationCtx<DataModel extends GenericDataModel> = ReturnType<
10
+ typeof MutationCtx<DataModel>
11
+ >["Identifier"];
@@ -0,0 +1,41 @@
1
+ import * as Ref from "@confect/core/Ref";
2
+ import { type GenericMutationCtx } from "convex/server";
3
+ import { Context, Effect, Layer, Schema } from "effect";
4
+
5
+ const makeMutationRunner =
6
+ (runMutation: GenericMutationCtx<any>["runMutation"]) =>
7
+ <Mutation extends Ref.AnyMutation>(
8
+ mutation: Mutation,
9
+ args: Ref.Args<Mutation>["Type"],
10
+ ) =>
11
+ Effect.gen(function* () {
12
+ const function_ = Ref.getFunction(mutation);
13
+ const functionName = Ref.getConvexFunctionName(mutation);
14
+
15
+ const encodedArgs = yield* Schema.encode(function_.args)(args);
16
+ const encodedReturns = yield* Effect.promise(() =>
17
+ runMutation(functionName as any, encodedArgs),
18
+ );
19
+ return yield* Schema.decode(function_.returns)(encodedReturns);
20
+ });
21
+
22
+ export const MutationRunner = Context.GenericTag<
23
+ ReturnType<typeof makeMutationRunner>
24
+ >("@confect/server/MutationRunner");
25
+ export type MutationRunner = typeof MutationRunner.Identifier;
26
+
27
+ export const layer = (runMutation: GenericMutationCtx<any>["runMutation"]) =>
28
+ Layer.succeed(MutationRunner, makeMutationRunner(runMutation));
29
+
30
+ export class MutationRollback extends Schema.TaggedError<MutationRollback>(
31
+ "MutationRollback",
32
+ )("MutationRollback", {
33
+ mutationName: Schema.String,
34
+ error: Schema.Unknown,
35
+ }) {
36
+ /* v8 ignore start */
37
+ override get message(): string {
38
+ return `Mutation ${this.mutationName} failed and was rolled back.\n\n${this.error}`;
39
+ }
40
+ /* v8 ignore stop */
41
+ }