@dr_nikson/effect-grpc 3.0.0-alpha.0 → 3.0.0-alpha.1
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/README.md +139 -204
- package/dist/protoRuntime.d.ts +3 -3
- package/dist/protoRuntime.d.ts.map +1 -1
- package/dist/protoRuntime.internal.d.ts +5 -5
- package/dist/protoRuntime.internal.d.ts.map +1 -1
- package/dist/protoRuntime.internal.js +5 -5
- package/dist/protoRuntime.internal.js.map +1 -1
- package/dist/protocGenPlugin.js +357 -145
- package/dist/protocGenPlugin.js.map +1 -1
- package/dist/server.d.ts +3 -3
- package/dist/server.d.ts.map +1 -1
- package/dist/server.internal.d.ts +4 -4
- package/dist/server.internal.d.ts.map +1 -1
- package/dist/server.internal.js +2 -2
- package/dist/server.internal.js.map +1 -1
- package/dist/server.js +1 -1
- package/package.json +1 -1
|
@@ -15,7 +15,8 @@ export class ServerExecutorLive {
|
|
|
15
15
|
return new ServerExecutorLive(runtime);
|
|
16
16
|
}
|
|
17
17
|
unary(req, ctx, prog) {
|
|
18
|
-
|
|
18
|
+
// Pass undefined as the context since the default is 'any' and no transformation has been applied
|
|
19
|
+
return Runtime.runPromiseExit(this.runtime, prog(req, undefined), { signal: ctx.signal }).then(Exit.match({
|
|
19
20
|
onFailure: (cause) => {
|
|
20
21
|
throw Cause.match(cause, {
|
|
21
22
|
onEmpty: new ConnectError("Unknown error", Code.Unknown),
|
|
@@ -45,11 +46,10 @@ export class ServerExecutorTransformerLive {
|
|
|
45
46
|
}
|
|
46
47
|
transformContext(f) {
|
|
47
48
|
return new ServerExecutorTransformerLive((underlying) => {
|
|
48
|
-
const executor = this.transformation(underlying);
|
|
49
49
|
return {
|
|
50
|
-
unary(req,
|
|
51
|
-
return
|
|
52
|
-
return Effect.flatMap(f(
|
|
50
|
+
unary(req, handlerCtx, prog) {
|
|
51
|
+
return underlying.unary(req, handlerCtx, (req) => {
|
|
52
|
+
return Effect.flatMap(f(handlerCtx), (ctx1) => prog(req, ctx1));
|
|
53
53
|
});
|
|
54
54
|
},
|
|
55
55
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"protoRuntime.internal.js","sourceRoot":"","sources":["../src/protoRuntime.internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGzD,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAC;AAGpD;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IACD;IAA5B,YAA4B,OAA+B;QAA/B,YAAO,GAAP,OAAO,CAAwB;IAAG,CAAC;IAE/D,MAAM,CAAC,IAAI,CAAC,OAA+B;QACzC,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CACH,GAAO,EACP,GAAmB,EACnB,
|
|
1
|
+
{"version":3,"file":"protoRuntime.internal.js","sourceRoot":"","sources":["../src/protoRuntime.internal.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,QAAQ,CAAC;AAEtD,OAAO,EAAE,IAAI,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAGzD,OAAO,KAAK,aAAa,MAAM,oBAAoB,CAAC;AAGpD;;;;GAIG;AACH,MAAM,OAAO,kBAAkB;IACD;IAA5B,YAA4B,OAA+B;QAA/B,YAAO,GAAP,OAAO,CAAwB;IAAG,CAAC;IAE/D,MAAM,CAAC,IAAI,CAAC,OAA+B;QACzC,OAAO,IAAI,kBAAkB,CAAC,OAAO,CAAC,CAAC;IACzC,CAAC;IAED,KAAK,CACH,GAAO,EACP,GAAmB,EACnB,IAA4E;QAE5E,kGAAkG;QAClG,OAAO,OAAO,CAAC,cAAc,CAAC,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,EAAE,EAAE,MAAM,EAAE,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAC5F,IAAI,CAAC,KAAK,CAAC;YACT,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE;gBACnB,MAAM,KAAK,CAAC,KAAK,CAAC,KAAK,EAAE;oBACvB,OAAO,EAAE,IAAI,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,OAAO,CAAC;oBACxD,MAAM,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,cAAc,CAAC,KAAK,CAAC;oBACtD,KAAK,EAAE,CAAC,MAAM,EAAE,EAAE,CAChB,IAAI,YAAY,CACd,uBAAuB,EACvB,IAAI,CAAC,QAAQ,EACb,SAAS,EACT,SAAS,EACT,MAAM,CACP;oBACH,WAAW,EAAE,GAAG,EAAE,CAAC,IAAI,YAAY,CAAC,sBAAsB,EAAE,IAAI,CAAC,OAAO,CAAC;oBACzE,YAAY,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC5B,IAAI,YAAY,CAAC,GAAG,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC;oBAC5E,UAAU,EAAE,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAC1B,IAAI,YAAY,CAAC,GAAG,IAAI,CAAC,UAAU,MAAM,KAAK,CAAC,UAAU,EAAE,EAAE,IAAI,CAAC,QAAQ,CAAC;iBAC9E,CAAC,CAAC;YACL,CAAC;YACD,SAAS,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK;SAC5B,CAAC,CACH,CAAC;IACJ,CAAC;CACF;AAED;;;;GAIG;AACH,MAAM,OAAO,6BAA6B;IAEtB;IADlB,YACkB,cAEU;QAFV,mBAAc,GAAd,cAAc,CAEJ;IACzB,CAAC;IAEJ,MAAM,CAAC,KAAK;QACV,OAAO,IAAI,6BAA6B,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC;IACvE,CAAC;IAED,gBAAgB,CACd,CAAmF;QAEnF,OAAO,IAAI,6BAA6B,CAAO,CAAC,UAAU,EAAE,EAAE;YAC5D,OAAO;gBACL,KAAK,CACH,GAAO,EACP,UAA0B,EAC1B,IAA6E;oBAE7E,OAAO,UAAU,CAAC,KAAK,CAAC,GAAG,EAAE,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE;wBAC/C,OAAO,MAAM,CAAC,OAAO,CAAC,CAAC,CAAC,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;oBAClE,CAAC,CAAC,CAAC;gBACL,CAAC;aACwB,CAAC;QAC9B,CAAC,CAAC,CAAC;IACL,CAAC;CACF"}
|
package/dist/protocGenPlugin.js
CHANGED
|
@@ -9,7 +9,9 @@ function generateTs(schema) {
|
|
|
9
9
|
for (const file of schema.files) {
|
|
10
10
|
const f = schema.generateFile(file.name + "_effect.ts");
|
|
11
11
|
f.preamble(file);
|
|
12
|
-
file
|
|
12
|
+
// Extract basename from file path for import examples
|
|
13
|
+
const fileBasename = file.name.split('/').pop() || file.name;
|
|
14
|
+
file.services.forEach(service => generateEffectService(f, service, fileBasename));
|
|
13
15
|
/*
|
|
14
16
|
for (const service of file.services) {
|
|
15
17
|
f.print(f.jsDoc(service));
|
|
@@ -60,208 +62,418 @@ function generateTs(schema) {
|
|
|
60
62
|
}*/
|
|
61
63
|
}
|
|
62
64
|
}
|
|
63
|
-
function generateEffectService(f, service) {
|
|
65
|
+
function generateEffectService(f, service, fileBasename) {
|
|
64
66
|
const importEffect = f.import("Effect", "effect");
|
|
65
67
|
const importContext = f.import("Context", "effect");
|
|
66
68
|
const importLayer = f.import("Layer", "effect");
|
|
67
69
|
const importScope = f.import("Scope", "effect");
|
|
68
|
-
const importHandlerContext = f.import("HandlerContext", "@connectrpc/connect", true);
|
|
69
70
|
const importEffectGrpcService = f.import("EffectGrpcServer", packageJson.name);
|
|
70
71
|
const importGrpcException = f.import("GrpcException", packageJson.name);
|
|
71
72
|
const importService = f.importSchema(service);
|
|
72
73
|
const serviceId = service.typeName;
|
|
73
|
-
const serviceIdSymbol = safeIdentifier(service.name + "
|
|
74
|
+
const serviceIdSymbol = safeIdentifier(service.name + "ProtoId");
|
|
74
75
|
const serviceSymbol = safeIdentifier(service.name + "Service");
|
|
75
76
|
const grpcServiceSymbol = safeIdentifier(service.name + "GrpcService");
|
|
76
|
-
const serviceTagSymbol = safeIdentifier(service.name + "
|
|
77
|
+
const serviceTagSymbol = safeIdentifier(service.name + "ServiceTag");
|
|
78
|
+
const serviceLiveLayerSymbol = safeIdentifier(service.name.charAt(0).toLowerCase() + service.name.slice(1) + "ServiceLiveLayer");
|
|
77
79
|
const makeTagSymbol = safeIdentifier("make" + service.name + "ServiceTag");
|
|
78
|
-
const makeLiveLayerSymbol = safeIdentifier("make" + service.name + "
|
|
80
|
+
const makeLiveLayerSymbol = safeIdentifier("make" + service.name + "ServiceLiveLayer");
|
|
81
|
+
// Generate service ID constant with JSDoc
|
|
82
|
+
f.print("/**");
|
|
83
|
+
f.print(" * Unique identifier for the ", service.name, " gRPC service.");
|
|
84
|
+
f.print(" *");
|
|
85
|
+
f.print(" * This constant represents the fully qualified service name from the Protocol Buffer definition.");
|
|
86
|
+
f.print(" * It's used to identify and wire gRPC dependencies.");
|
|
87
|
+
f.print(" *");
|
|
88
|
+
f.print(" * @generated from service ", service.typeName);
|
|
89
|
+
f.print(" */");
|
|
79
90
|
f.print(f.export("const", serviceIdSymbol), " = ", f.string(serviceId), " as const;");
|
|
80
91
|
f.print(f.export("type", serviceIdSymbol), " = typeof ", serviceIdSymbol, ";");
|
|
81
92
|
f.print();
|
|
82
|
-
|
|
83
|
-
f.print(
|
|
93
|
+
// Generate comprehensive JSDoc for service interface
|
|
94
|
+
f.print("/**");
|
|
95
|
+
f.print(" * gRPC service interface.");
|
|
96
|
+
f.print(" *");
|
|
97
|
+
f.print(" * @typeParam Ctx - The type of context passed to service methods. Use this to inject");
|
|
98
|
+
f.print(" * dependencies or pass request-scoped data (e.g., authentication info)");
|
|
99
|
+
f.print(" *");
|
|
100
|
+
f.print(" * @example");
|
|
101
|
+
f.print(" * ```typescript");
|
|
102
|
+
f.print(" * import { Effect } from \"effect\";");
|
|
103
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
104
|
+
f.print(" *");
|
|
105
|
+
f.print(" * // Simple implementation with default context");
|
|
106
|
+
f.print(" * const service: effectProto.", serviceSymbol, " = {");
|
|
107
|
+
if (service.methods.length > 0 && service.methods[0] !== undefined) {
|
|
108
|
+
const firstMethod = service.methods[0];
|
|
109
|
+
f.print(" * ", firstMethod.localName, ": (request) =>");
|
|
110
|
+
f.print(" * Effect.succeed({})");
|
|
111
|
+
}
|
|
112
|
+
f.print(" * };");
|
|
113
|
+
f.print(" * ```");
|
|
114
|
+
f.print(" *");
|
|
115
|
+
f.print(" * @generated from service ", service.typeName);
|
|
116
|
+
f.print(" */");
|
|
117
|
+
f.print(f.export("interface", serviceSymbol), "<Ctx = any> {");
|
|
84
118
|
service.methods.forEach(generateServerMethod);
|
|
85
119
|
f.print("}");
|
|
86
|
-
/**
|
|
87
|
-
* @example
|
|
88
|
-
* ```typescript
|
|
89
|
-
* export const DebugAPIService: {
|
|
90
|
-
* makeTag<Ctx>(ctxKey: string): DebugAPITag<Ctx>;
|
|
91
|
-
*
|
|
92
|
-
* liveLayer<Ctx>(service: DebugAPIService<Ctx>):
|
|
93
|
-
* <Tag extends DebugAPITag<Ctx>>(tag: Tag) => Layer.Layer<Context.Tag.Identifier<Tag>, never, never>;
|
|
94
|
-
* } = {
|
|
95
|
-
* makeTag: makeDebugAPIServiceTag,
|
|
96
|
-
* liveLayer: makeDebugApiLiveLayer
|
|
97
|
-
* };
|
|
98
|
-
* ```
|
|
99
|
-
*/
|
|
100
|
-
f.print(f.export("const", serviceSymbol), ": {");
|
|
101
|
-
f.print(" makeTag<Ctx>(ctxKey: string): ", serviceTagSymbol, "<Ctx>;");
|
|
102
120
|
f.print();
|
|
103
|
-
|
|
104
|
-
f.print("
|
|
105
|
-
f.print("
|
|
106
|
-
f.print("
|
|
107
|
-
f.print("
|
|
108
|
-
f.print("
|
|
109
|
-
f.print("
|
|
121
|
+
// Generate direct exported layer function with JSDoc
|
|
122
|
+
f.print("/**");
|
|
123
|
+
f.print(" * Creates a Layer that provides a gRPC service implementation.");
|
|
124
|
+
f.print(" *");
|
|
125
|
+
f.print(" * This function takes your service implementation and wraps it in a Layer that can be");
|
|
126
|
+
f.print(" * composed with other layers to build your application. The returned function accepts");
|
|
127
|
+
f.print(" * a Context.Tag to identify the service in the Effect context.");
|
|
128
|
+
f.print(" *");
|
|
129
|
+
f.print(" * @typeParam Ctx - The context type used by your service implementation");
|
|
130
|
+
f.print(" *");
|
|
131
|
+
f.print(" * @param tag - Context tag to identify the service");
|
|
132
|
+
f.print(" * @param service - Your implementation of the ", serviceSymbol, " interface");
|
|
133
|
+
f.print(" * @returns A Layer providing the gRPC service");
|
|
134
|
+
f.print(" *");
|
|
135
|
+
f.print(" * @example");
|
|
136
|
+
f.print(" * ```typescript");
|
|
137
|
+
f.print(" * import { Effect, Layer } from \"effect\";");
|
|
138
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
139
|
+
f.print(" *");
|
|
140
|
+
f.print(" * // Define your service implementation");
|
|
141
|
+
f.print(" * const myService: effectProto.", serviceSymbol, " = {");
|
|
142
|
+
if (service.methods.length > 0 && service.methods[0] !== undefined) {
|
|
143
|
+
const firstMethod = service.methods[0];
|
|
144
|
+
f.print(" * ", firstMethod.localName, ": (request) =>");
|
|
145
|
+
f.print(" * Effect.succeed({})");
|
|
146
|
+
}
|
|
147
|
+
f.print(" * };");
|
|
148
|
+
f.print(" *");
|
|
149
|
+
f.print(" * // Create the service layer");
|
|
150
|
+
f.print(" * const ServiceLayer = effectProto.", serviceLiveLayerSymbol, "(effectProto.", serviceTagSymbol, ", myService);");
|
|
151
|
+
f.print(" * ```");
|
|
152
|
+
f.print(" *");
|
|
153
|
+
f.print(" * @generated from service ", service.typeName);
|
|
154
|
+
f.print(" */");
|
|
155
|
+
f.print(f.export("const", serviceLiveLayerSymbol), ": {");
|
|
156
|
+
f.print(" <Tag extends ", serviceTagSymbol, "<Ctx>, Ctx>(");
|
|
157
|
+
f.print(" tag: Tag,");
|
|
158
|
+
f.print(" service: ", serviceSymbol, "<Ctx>,");
|
|
159
|
+
f.print(" ): ", importLayer, ".Layer<", importContext, ".Tag.Identifier<Tag>>;");
|
|
160
|
+
f.print("} = ", makeLiveLayerSymbol, ";");
|
|
161
|
+
f.print();
|
|
162
|
+
// Generate type aliases with JSDoc
|
|
163
|
+
f.print("/**");
|
|
164
|
+
f.print(" * Type alias for the wrapped gRPC service.");
|
|
165
|
+
f.print(" *");
|
|
166
|
+
f.print(" * This represents the Effect-wrapped version of your service implementation,");
|
|
167
|
+
f.print(" * ready to be registered with a gRPC server.");
|
|
168
|
+
f.print(" *");
|
|
169
|
+
f.print(" * @typeParam Ctx - The context type used by the service");
|
|
170
|
+
f.print(" *");
|
|
171
|
+
f.print(" * @generated from service ", service.typeName);
|
|
172
|
+
f.print(" */");
|
|
173
|
+
f.print(f.export("type", grpcServiceSymbol), "<Ctx = any>", " = ", importEffectGrpcService, ".GrpcService<", serviceIdSymbol, ", typeof ", importService, ", Ctx>");
|
|
174
|
+
f.print();
|
|
175
|
+
// export type DebugAPIServiceTag<Ctx = any> = Context.Tag<DebugAPIGrpcService<Ctx>, DebugAPIGrpcService<Ctx>>
|
|
176
|
+
f.print("/**");
|
|
177
|
+
f.print(" * Type alias for the service Context.Tag.");
|
|
178
|
+
f.print(" *");
|
|
179
|
+
f.print(" * Used to identify and retrieve the service from the Effect context.");
|
|
180
|
+
f.print(" *");
|
|
181
|
+
f.print(" * @typeParam Ctx - The context type used by the service");
|
|
182
|
+
f.print(" *");
|
|
183
|
+
f.print(" * @generated from service ", service.typeName);
|
|
184
|
+
f.print(" */");
|
|
185
|
+
f.print(f.export("type", serviceTagSymbol), "<Ctx = any>", " = ", importContext, ".Tag<", grpcServiceSymbol, "<Ctx>, ", grpcServiceSymbol, "<Ctx>>");
|
|
186
|
+
f.print();
|
|
187
|
+
// Generate ServiceTag constant with comprehensive JSDoc
|
|
188
|
+
f.print("/**");
|
|
189
|
+
f.print(" * Context.Tag for identifying the ", service.name, " service implementation.");
|
|
190
|
+
f.print(" *");
|
|
191
|
+
f.print(" * This tag is used to provide and retrieve the service from the Effect context.");
|
|
192
|
+
f.print(" * It supports both default (any) context and typed context via the function overload.");
|
|
193
|
+
f.print(" *");
|
|
194
|
+
f.print(" * @example");
|
|
195
|
+
f.print(" * ```typescript");
|
|
196
|
+
f.print(" * import { Effect } from \"effect\";");
|
|
197
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
198
|
+
f.print(" *");
|
|
199
|
+
f.print(" * // Use default context tag");
|
|
200
|
+
f.print(" * const myService = {");
|
|
201
|
+
if (service.methods.length > 0 && service.methods[0] !== undefined) {
|
|
202
|
+
const firstMethod = service.methods[0];
|
|
203
|
+
f.print(" * ", firstMethod.localName, ": (request) => Effect.succeed({})");
|
|
204
|
+
}
|
|
205
|
+
f.print(" * };");
|
|
206
|
+
f.print(" * const ServiceLayer = effectProto.", serviceLiveLayerSymbol, "(effectProto.", serviceTagSymbol, ", myService);");
|
|
207
|
+
f.print(" * ```");
|
|
208
|
+
f.print(" *");
|
|
209
|
+
f.print(" * @example");
|
|
210
|
+
f.print(" * ```typescript");
|
|
211
|
+
f.print(" * import { Effect } from \"effect\";");
|
|
212
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
213
|
+
f.print(" *");
|
|
214
|
+
f.print(" * // Create a typed context tag");
|
|
215
|
+
f.print(" * interface MyContext {");
|
|
216
|
+
f.print(" * userId: string;");
|
|
217
|
+
f.print(" * }");
|
|
218
|
+
f.print(" *");
|
|
219
|
+
f.print(" * const TypedServiceTag = effectProto.", serviceTagSymbol, "<MyContext>(\"MyContext\");");
|
|
220
|
+
f.print(" * ```");
|
|
221
|
+
f.print(" *");
|
|
222
|
+
f.print(" * @generated from service ", service.typeName);
|
|
223
|
+
f.print(" */");
|
|
224
|
+
f.print(f.export("const", serviceTagSymbol), ": ", serviceTagSymbol, " & {");
|
|
225
|
+
f.print(" <Ctx>(ctxKey: string): ", serviceTagSymbol, "<Ctx>;");
|
|
226
|
+
f.print("} = Object.assign(", makeTagSymbol, "(), ", makeTagSymbol, ");");
|
|
110
227
|
f.print();
|
|
111
|
-
// export type DebugAPIGrpcService<Ctx = HandlerContext> = EffectGrpcServer.GrpcService<"DebugAPI", typeof proto.DebugAPI, Ctx>
|
|
112
|
-
f.print(f.export("type", grpcServiceSymbol), "<Ctx = ", importHandlerContext, ">", " = ", importEffectGrpcService, `.GrpcService<"`, service.typeName, `", typeof `, importService, ", Ctx>");
|
|
113
|
-
// export type DebugAPITag<Ctx> = Context.Tag<DebugAPIGrpcService<Ctx>, DebugAPIGrpcService<Ctx>>
|
|
114
|
-
f.print(f.export("type", serviceTagSymbol), "<Ctx>", " = ", importContext, ".Tag<", grpcServiceSymbol, "<Ctx>, ", grpcServiceSymbol, "<Ctx>>");
|
|
115
228
|
/**
|
|
116
229
|
* @example
|
|
117
230
|
* ```typescript
|
|
118
|
-
* function makeDebugAPIServiceTag<Ctx>(ctxKey: string
|
|
119
|
-
* return Context.GenericTag<DebugAPIGrpcService<Ctx
|
|
231
|
+
* function makeDebugAPIServiceTag<Ctx = any>(ctxKey: string = "any"): DebugAPITag<Ctx> {
|
|
232
|
+
* return Context.GenericTag<DebugAPIGrpcService<Ctx>>(`${proto.DebugAPI.typeName}<${ctxKey}}>`);
|
|
120
233
|
* }
|
|
121
234
|
* ```
|
|
122
235
|
*/
|
|
123
|
-
f.print("function ", makeTagSymbol, "<Ctx>(ctxKey: string): ", serviceTagSymbol, "<Ctx> {");
|
|
236
|
+
f.print("function ", makeTagSymbol, "<Ctx = any>(ctxKey: string = ", f.string("any"), "): ", serviceTagSymbol, "<Ctx> {");
|
|
124
237
|
f.print(" return ", importContext, ".GenericTag<", grpcServiceSymbol, "<Ctx>>(`", service.typeName, "<${ctxKey}>`);");
|
|
125
238
|
f.print("}");
|
|
126
239
|
f.print();
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
* return {
|
|
137
|
-
* getDebugInfo: (req: proto.GetDebugInfoRequest, ctx: HandlerContext) => {
|
|
138
|
-
* return executor.unary(
|
|
139
|
-
* req,
|
|
140
|
-
* ctx,
|
|
141
|
-
* (req, ctx) => service.getDebugInfo(req, ctx)
|
|
142
|
-
* );
|
|
143
|
-
* }
|
|
144
|
-
* } as ServiceImpl<Proto>;
|
|
145
|
-
* }
|
|
146
|
-
* );
|
|
147
|
-
*
|
|
148
|
-
* return Layer.succeed(tag, instance);
|
|
149
|
-
*
|
|
150
|
-
* }
|
|
151
|
-
* }
|
|
152
|
-
* ```
|
|
153
|
-
*/
|
|
154
|
-
f.print("function ", makeLiveLayerSymbol, "<Ctx>(service: ", serviceSymbol, "<Ctx>) {");
|
|
155
|
-
f.print(" return <Tag extends ", serviceTagSymbol, "<Ctx>>(tag: Tag) => {");
|
|
156
|
-
f.print();
|
|
157
|
-
f.print(" const instance: ", grpcServiceSymbol, "<Ctx> = ", importEffectGrpcService, '.GrpcService("', service.typeName, '" as const, ', importService, ")(");
|
|
158
|
-
f.print(" (executor) => ({");
|
|
240
|
+
// Generate implementation function
|
|
241
|
+
f.print("function ", makeLiveLayerSymbol, "<Tag extends ", serviceTagSymbol, "<Ctx>, Ctx>(");
|
|
242
|
+
f.print(" tag: Tag,");
|
|
243
|
+
f.print(" service: ", serviceSymbol, "<Ctx>,");
|
|
244
|
+
f.print("): ", importLayer, ".Layer<", importContext, ".Tag.Identifier<Tag>> {");
|
|
245
|
+
f.print(" const instance: ", grpcServiceSymbol, "<Ctx> = ", importEffectGrpcService, ".GrpcService(");
|
|
246
|
+
f.print(" ", serviceIdSymbol, ",");
|
|
247
|
+
f.print(" ", importService, ",");
|
|
248
|
+
f.print(" )((executor) => ({");
|
|
159
249
|
service.methods.forEach((method) => {
|
|
160
250
|
if (method.methodKind === "unary") {
|
|
161
|
-
f.print("
|
|
251
|
+
f.print(" ", method.localName, ": (req, ctx) =>");
|
|
252
|
+
f.print(" executor.unary(req, ctx, (req, ctx) => service.", method.localName, "(req, ctx)),");
|
|
162
253
|
}
|
|
163
254
|
});
|
|
164
|
-
f.print("
|
|
165
|
-
f.print(" );");
|
|
255
|
+
f.print(" }));");
|
|
166
256
|
f.print();
|
|
167
|
-
f.print("
|
|
168
|
-
f.print(" };");
|
|
257
|
+
f.print(" return ", importLayer, ".succeed(tag, instance);");
|
|
169
258
|
f.print("}");
|
|
170
259
|
f.print();
|
|
171
260
|
const clientSymbol = safeIdentifier(service.name + "Client");
|
|
172
|
-
/**
|
|
173
|
-
* @example
|
|
174
|
-
* ```typescript
|
|
175
|
-
* export interface DebugAPIClient<Meta> {
|
|
176
|
-
* getDebugInfo(
|
|
177
|
-
* request: MessageInitShape<typeof proto.GetDebugInfoRequestSchema>,
|
|
178
|
-
* meta: Meta,
|
|
179
|
-
* ): Effect.Effect<proto.GetDebugInfoResponse>
|
|
180
|
-
* }
|
|
181
|
-
* ```
|
|
182
|
-
*/
|
|
183
|
-
f.print(f.jsDoc(service));
|
|
184
|
-
f.print(f.export("interface", clientSymbol), "<Meta> {");
|
|
185
|
-
service.methods.forEach(generateClientMethod);
|
|
186
|
-
f.print("}");
|
|
187
|
-
/**
|
|
188
|
-
* @example
|
|
189
|
-
* ```typescript
|
|
190
|
-
* export const DebugAPIClient: {
|
|
191
|
-
* makeTag<Meta>(metaKey: string): DebugAPIClientTag<Meta>
|
|
192
|
-
*
|
|
193
|
-
* liveLayer<Meta>(transformMeta: (meta: Meta) => RequestMeta):
|
|
194
|
-
* <Tag extends DebugAPIClientTag<Meta>>(tag: Tag) => Layer.Layer<Context.Tag.Identifier<Tag>, never, EffectGrpcClient.GrpcClient>
|
|
195
|
-
* } = {
|
|
196
|
-
* makeTag: makeDebugAPIClientTag,
|
|
197
|
-
* liveLayer: makeDebugApiClientLiveLayer,
|
|
198
|
-
* }
|
|
199
|
-
* ```
|
|
200
|
-
*/
|
|
201
261
|
const importEffectGrpcClient = f.import("EffectGrpcClient", packageJson.name);
|
|
202
262
|
const clientTagSymbol = safeIdentifier(service.name + "ClientTag");
|
|
263
|
+
const clientLiveLayerSymbol = safeIdentifier(service.name.charAt(0).toLowerCase() + service.name.slice(1) + "ClientLiveLayer");
|
|
203
264
|
const configTagSymbol = safeIdentifier(service.name + "ConfigTag");
|
|
204
265
|
const makeClientTagSymbol = safeIdentifier("make" + service.name + "ClientTag");
|
|
205
266
|
const makeClientLiveLayerSymbol = safeIdentifier("make" + service.name + "ClientLiveLayer");
|
|
206
|
-
|
|
207
|
-
f.print("
|
|
267
|
+
// Generate comprehensive JSDoc for client interface
|
|
268
|
+
f.print("/**");
|
|
269
|
+
f.print(" * Client interface for making gRPC calls to ", service.name, " service.");
|
|
270
|
+
f.print(" *");
|
|
271
|
+
f.print(" * This interface defines the client-side methods for calling the gRPC service.");
|
|
272
|
+
f.print(" * Each method accepts a request message and metadata, and returns an Effect that");
|
|
273
|
+
f.print(" * produces the response or fails with an error.");
|
|
274
|
+
f.print(" *");
|
|
275
|
+
f.print(" * @typeParam Meta - The type of metadata to pass with each request. This can be used");
|
|
276
|
+
f.print(" * for authentication tokens, tracing headers, or other per-request data.");
|
|
277
|
+
f.print(" *");
|
|
278
|
+
f.print(" * @example");
|
|
279
|
+
f.print(" * ```typescript");
|
|
280
|
+
f.print(" * import { Effect } from \"effect\";");
|
|
281
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
282
|
+
f.print(" *");
|
|
283
|
+
f.print(" * // Use the client in an Effect");
|
|
284
|
+
f.print(" * const program = Effect.gen(function* () {");
|
|
285
|
+
f.print(" * const client = yield* effectProto.", clientTagSymbol, ";");
|
|
286
|
+
if (service.methods.length > 0 && service.methods[0] !== undefined) {
|
|
287
|
+
const firstMethod = service.methods[0];
|
|
288
|
+
f.print(" * const response = yield* client.", firstMethod.localName, "({}, {});");
|
|
289
|
+
f.print(" * return response;");
|
|
290
|
+
}
|
|
291
|
+
f.print(" * });");
|
|
292
|
+
f.print(" * ```");
|
|
293
|
+
f.print(" *");
|
|
294
|
+
f.print(" * @generated from service ", service.typeName);
|
|
295
|
+
f.print(" */");
|
|
296
|
+
f.print(f.export("interface", clientSymbol), "<Meta> {");
|
|
297
|
+
service.methods.forEach(generateClientMethod);
|
|
298
|
+
f.print("}");
|
|
299
|
+
f.print();
|
|
300
|
+
// Generate ClientTag type with JSDoc
|
|
301
|
+
f.print("/**");
|
|
302
|
+
f.print(" * Type alias for the client Context.Tag.");
|
|
303
|
+
f.print(" *");
|
|
304
|
+
f.print(" * Used to identify and retrieve the client from the Effect context.");
|
|
305
|
+
f.print(" *");
|
|
306
|
+
f.print(" * @typeParam Meta - The metadata type used by the client");
|
|
307
|
+
f.print(" *");
|
|
308
|
+
f.print(" * @generated from service ", service.typeName);
|
|
309
|
+
f.print(" */");
|
|
310
|
+
f.print(f.export("type", clientTagSymbol), "<Meta = any>", " = ", importContext, ".Tag<", clientSymbol, "<Meta>, ", clientSymbol, "<Meta>>");
|
|
311
|
+
f.print();
|
|
312
|
+
// Generate ClientTag constant with comprehensive JSDoc
|
|
313
|
+
f.print("/**");
|
|
314
|
+
f.print(" * Context.Tag for identifying the ", service.name, " client.");
|
|
315
|
+
f.print(" *");
|
|
316
|
+
f.print(" * This tag is used to provide and retrieve the gRPC client from the Effect context.");
|
|
317
|
+
f.print(" * It supports both default (any) metadata and typed metadata via the function overload.");
|
|
318
|
+
f.print(" *");
|
|
319
|
+
f.print(" * @example");
|
|
320
|
+
f.print(" * ```typescript");
|
|
321
|
+
f.print(" * import { Effect, Layer } from \"effect\";");
|
|
322
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
323
|
+
f.print(" * import { EffectGrpcClient } from \"@dr_nikson/effect-grpc\";");
|
|
324
|
+
f.print(" *");
|
|
325
|
+
f.print(" * // Use default metadata type");
|
|
326
|
+
f.print(" * const ClientLayer = effectProto.", clientLiveLayerSymbol, "(effectProto.", clientTagSymbol, ");");
|
|
327
|
+
f.print(" *");
|
|
328
|
+
f.print(" * // Use the client");
|
|
329
|
+
f.print(" * const program = Effect.gen(function* () {");
|
|
330
|
+
f.print(" * const client = yield* effectProto.", clientTagSymbol, ";");
|
|
331
|
+
if (service.methods.length > 0 && service.methods[0] !== undefined) {
|
|
332
|
+
const firstMethod = service.methods[0];
|
|
333
|
+
f.print(" * return yield* client.", firstMethod.localName, "({}, {});");
|
|
334
|
+
}
|
|
335
|
+
f.print(" * });");
|
|
336
|
+
f.print(" * ```");
|
|
337
|
+
f.print(" *");
|
|
338
|
+
f.print(" * @example");
|
|
339
|
+
f.print(" * ```typescript");
|
|
340
|
+
f.print(" * import { Effect, Layer } from \"effect\";");
|
|
341
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
342
|
+
f.print(" *");
|
|
343
|
+
f.print(" * // Create a typed metadata tag");
|
|
344
|
+
f.print(" * interface RequestMeta {");
|
|
345
|
+
f.print(" * userId: string;");
|
|
346
|
+
f.print(" * traceId: string;");
|
|
347
|
+
f.print(" * }");
|
|
348
|
+
f.print(" *");
|
|
349
|
+
f.print(" * const TypedClientTag = effectProto.", clientTagSymbol, "<RequestMeta>(\"RequestMeta\");");
|
|
350
|
+
f.print(" * ```");
|
|
351
|
+
f.print(" *");
|
|
352
|
+
f.print(" * @generated from service ", service.typeName);
|
|
353
|
+
f.print(" */");
|
|
354
|
+
f.print(f.export("const", clientTagSymbol), ": ", clientTagSymbol, " & {");
|
|
355
|
+
f.print(" <Meta>(metaKey: string): ", clientTagSymbol, "<Meta>;");
|
|
356
|
+
f.print("} = Object.assign(", makeClientTagSymbol, "<any>(", f.string("any"), "), ", makeClientTagSymbol, ");");
|
|
208
357
|
f.print();
|
|
209
|
-
|
|
358
|
+
// Generate clientLiveLayer function with comprehensive JSDoc
|
|
359
|
+
f.print("/**");
|
|
360
|
+
f.print(" * Creates a Layer that provides a gRPC client for the ", service.name, " service.");
|
|
361
|
+
f.print(" *");
|
|
362
|
+
f.print(" * This function creates a client that can make gRPC calls to the service.");
|
|
363
|
+
f.print(" * It has two overloads:");
|
|
364
|
+
f.print(" * 1. Simple version: Just pass the tag (uses default empty metadata)");
|
|
365
|
+
f.print(" * 2. Advanced version: Pass a metadata transformer function and the tag");
|
|
366
|
+
f.print(" *");
|
|
367
|
+
f.print(" * The client layer requires:");
|
|
368
|
+
f.print(" * - ", configTagSymbol, ": Configuration with the server URL");
|
|
369
|
+
f.print(" * - EffectGrpcClient.GrpcClientRuntime: The gRPC runtime");
|
|
370
|
+
f.print(" * - Scope.Scope: For resource management");
|
|
371
|
+
f.print(" *");
|
|
372
|
+
f.print(" * @example");
|
|
373
|
+
f.print(" * ```typescript");
|
|
374
|
+
f.print(" * import { Effect, Layer } from \"effect\";");
|
|
375
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
376
|
+
f.print(" * import { EffectGrpcClient } from \"@dr_nikson/effect-grpc\";");
|
|
377
|
+
f.print(" *");
|
|
378
|
+
f.print(" * // Simple usage with default metadata");
|
|
379
|
+
f.print(" * const ClientLayer = effectProto.", clientLiveLayerSymbol, "(effectProto.", clientTagSymbol, ");");
|
|
380
|
+
f.print(" *");
|
|
381
|
+
f.print(" * // Configuration layer");
|
|
382
|
+
f.print(" * const ConfigLayer = Layer.succeed(");
|
|
383
|
+
f.print(" * effectProto.", configTagSymbol, ",");
|
|
384
|
+
f.print(" * { baseUrl: new URL(\"http://localhost:50051\") }");
|
|
385
|
+
f.print(" * );");
|
|
386
|
+
f.print(" * ```");
|
|
387
|
+
f.print(" *");
|
|
388
|
+
f.print(" * @example");
|
|
389
|
+
f.print(" * ```typescript");
|
|
390
|
+
f.print(" * import { Effect } from \"effect\";");
|
|
391
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
392
|
+
f.print(" *");
|
|
393
|
+
f.print(" * // Advanced usage with metadata transformation");
|
|
394
|
+
f.print(" * interface AppMeta {");
|
|
395
|
+
f.print(" * userId: string;");
|
|
396
|
+
f.print(" * authToken: string;");
|
|
397
|
+
f.print(" * }");
|
|
398
|
+
f.print(" *");
|
|
399
|
+
f.print(" * const AppClientTag = effectProto.", clientTagSymbol, "<AppMeta>(\"AppMeta\");");
|
|
400
|
+
f.print(" *");
|
|
401
|
+
f.print(" * // Transform app metadata to gRPC headers");
|
|
402
|
+
f.print(" * const ClientLayer = effectProto.", clientLiveLayerSymbol, "(");
|
|
403
|
+
f.print(" * (meta: AppMeta) => ({");
|
|
404
|
+
f.print(" * headers: {");
|
|
405
|
+
f.print(" * \"authorization\": `Bearer ${meta.authToken}`,");
|
|
406
|
+
f.print(" * \"x-user-id\": meta.userId");
|
|
407
|
+
f.print(" * }");
|
|
408
|
+
f.print(" * }),");
|
|
409
|
+
f.print(" * AppClientTag");
|
|
410
|
+
f.print(" * );");
|
|
411
|
+
f.print(" * ```");
|
|
412
|
+
f.print(" *");
|
|
413
|
+
f.print(" * @generated from service ", service.typeName);
|
|
414
|
+
f.print(" */");
|
|
415
|
+
f.print(f.export("const", clientLiveLayerSymbol), ": {");
|
|
416
|
+
f.print(" <Tag extends ", clientTagSymbol, "<Meta>, Meta>(");
|
|
210
417
|
f.print(" transformMeta: (meta: Meta) => ", importEffectGrpcClient, ".RequestMeta,");
|
|
211
|
-
f.print(" tag: Tag");
|
|
418
|
+
f.print(" tag: Tag,");
|
|
212
419
|
f.print(" ): ", importLayer, ".Layer<");
|
|
213
420
|
f.print(" ", importContext, ".Tag.Identifier<Tag>,");
|
|
214
421
|
f.print(" never,");
|
|
215
|
-
f.print(" ",
|
|
422
|
+
f.print(" ", configTagSymbol, "[\"Identifier\"] | ", importEffectGrpcClient, ".GrpcClientRuntime | ", importScope, ".Scope");
|
|
216
423
|
f.print(" >;");
|
|
217
424
|
f.print();
|
|
218
|
-
f.print("
|
|
219
|
-
f.print(" tag: Tag");
|
|
425
|
+
f.print(" <Tag extends ", clientTagSymbol, ">(");
|
|
426
|
+
f.print(" tag: Tag,");
|
|
220
427
|
f.print(" ): ", importLayer, ".Layer<");
|
|
221
428
|
f.print(" ", importContext, ".Tag.Identifier<Tag>,");
|
|
222
429
|
f.print(" never,");
|
|
223
|
-
f.print(" ",
|
|
430
|
+
f.print(" ", configTagSymbol, "[\"Identifier\"] | ", importEffectGrpcClient, ".GrpcClientRuntime | ", importScope, ".Scope");
|
|
224
431
|
f.print(" >;");
|
|
225
|
-
f.print("} =
|
|
226
|
-
f.print(" makeTag: ", makeClientTagSymbol, ",");
|
|
227
|
-
f.print(" liveLayer: ", makeClientLiveLayerSymbol, ",");
|
|
228
|
-
f.print("};");
|
|
229
|
-
f.print();
|
|
230
|
-
// export type DebugAPIClientTag<Meta> = Context.Tag<DebugAPIClient<Meta>, DebugAPIClient<Meta>>
|
|
231
|
-
f.print(f.export("type", clientTagSymbol), "<Meta>", " = ", importContext, ".Tag<", clientSymbol, "<Meta>, ", clientSymbol, "<Meta>>");
|
|
432
|
+
f.print("} = ", makeClientLiveLayerSymbol, ";");
|
|
232
433
|
f.print();
|
|
233
|
-
//
|
|
434
|
+
// Generate ConfigTag type with JSDoc
|
|
435
|
+
f.print("/**");
|
|
436
|
+
f.print(" * Type alias for the client configuration Context.Tag.");
|
|
437
|
+
f.print(" *");
|
|
438
|
+
f.print(" * This tag provides the configuration required by the gRPC client,");
|
|
439
|
+
f.print(" * such as the server URL and connection options.");
|
|
440
|
+
f.print(" *");
|
|
441
|
+
f.print(" * @generated from service ", service.typeName);
|
|
442
|
+
f.print(" */");
|
|
234
443
|
f.print(f.export("type", configTagSymbol), " = ", importContext, ".Tag<", importEffectGrpcClient, ".GrpcClientConfig<", serviceIdSymbol, ">, ", importEffectGrpcClient, ".GrpcClientConfig<", serviceIdSymbol, ">>");
|
|
235
|
-
|
|
444
|
+
f.print();
|
|
445
|
+
// Generate ConfigTag constant with comprehensive JSDoc
|
|
446
|
+
f.print("/**");
|
|
447
|
+
f.print(" * Context.Tag for the ", service.name, " client configuration.");
|
|
448
|
+
f.print(" *");
|
|
449
|
+
f.print(" * This tag is used to provide the gRPC client configuration, which includes:");
|
|
450
|
+
f.print(" * - baseUrl: The URL of the gRPC server (e.g., \"http://localhost:50051\")");
|
|
451
|
+
f.print(" * - Additional connection options (timeouts, interceptors, etc.)");
|
|
452
|
+
f.print(" *");
|
|
453
|
+
f.print(" * You must provide this configuration as a Layer when using the client.");
|
|
454
|
+
f.print(" *");
|
|
455
|
+
f.print(" * @example");
|
|
456
|
+
f.print(" * ```typescript");
|
|
457
|
+
f.print(" * import { Layer } from \"effect\";");
|
|
458
|
+
f.print(" * import * as effectProto from \"./" + fileBasename + "_effect.js\";");
|
|
459
|
+
f.print(" *");
|
|
460
|
+
f.print(" * // Simple configuration with just the base URL");
|
|
461
|
+
f.print(" * const ConfigLayer = Layer.succeed(");
|
|
462
|
+
f.print(" * effectProto.", configTagSymbol, ",");
|
|
463
|
+
f.print(" * { baseUrl: new URL(\"http://localhost:50051\") }");
|
|
464
|
+
f.print(" * );");
|
|
465
|
+
f.print(" * ```");
|
|
466
|
+
f.print(" *");
|
|
467
|
+
f.print(" * @generated from service ", service.typeName);
|
|
468
|
+
f.print(" */");
|
|
236
469
|
f.print(f.export("const", configTagSymbol), ": ", configTagSymbol, " = ", importEffectGrpcClient, ".GrpcClientConfig.makeTag(", serviceIdSymbol, ");");
|
|
237
470
|
f.print();
|
|
238
|
-
//
|
|
471
|
+
// Generate makeClientTag helper function
|
|
239
472
|
f.print("function ", makeClientTagSymbol, "<Meta>(metaKey: string): ", clientTagSymbol, "<Meta> {");
|
|
240
|
-
f.print(" return ", importContext, ".GenericTag<", clientSymbol, "<Meta>>(
|
|
473
|
+
f.print(" return ", importContext, ".GenericTag<", clientSymbol, "<Meta>>(`${", serviceIdSymbol, "}<${metaKey}>`);");
|
|
241
474
|
f.print("}");
|
|
242
475
|
f.print();
|
|
243
|
-
|
|
244
|
-
* @example
|
|
245
|
-
* ```typescript
|
|
246
|
-
* function makeDebugApiClientLiveLayer<Meta>(transformMeta: (meta: Meta) => RequestMeta) {
|
|
247
|
-
* return <Tag extends DebugAPIClientTag<Meta>>(tag: Tag) => {
|
|
248
|
-
* const prog = Effect.gen(function* () {
|
|
249
|
-
* const grpcService = yield* EffectGrpcClient.GrpcClient
|
|
250
|
-
* const executor = grpcService.makeExecutor(proto.DebugAPI, ["getDebugInfo"]);
|
|
251
|
-
*
|
|
252
|
-
* return {
|
|
253
|
-
* getDebugInfo(req, meta) {
|
|
254
|
-
* return executor.getDebugInfo(req, meta ?? transformMeta(meta));
|
|
255
|
-
* },
|
|
256
|
-
* } as DebugAPIClient<Meta>;
|
|
257
|
-
* });
|
|
258
|
-
*
|
|
259
|
-
* return Layer.effect(tag, prog);
|
|
260
|
-
* }
|
|
261
|
-
* }
|
|
262
|
-
* ```
|
|
263
|
-
*/
|
|
264
|
-
// function makeDebugAPIClientLiveLayer<Tag extends DebugAPIClientTag<Meta>, Meta = object>(...args: readonly [(meta: Meta) => RequestMeta, Tag] | readonly [Tag])
|
|
476
|
+
// Generate client implementation function with overload handling
|
|
265
477
|
f.print("function ", makeClientLiveLayerSymbol, "<Tag extends ", clientTagSymbol, "<Meta>, Meta = object>(");
|
|
266
478
|
f.print(" ...args: readonly [(meta: Meta) => ", importEffectGrpcClient, ".RequestMeta, Tag] | readonly [Tag]");
|
|
267
479
|
f.print(") {");
|