@dr_nikson/effect-grpc 0.2.0-mvp-9971208edee70b39058f12d8c2a1fcfaeecd3b51 → 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 +863 -0
- package/dist/client.d.ts +38 -3
- package/dist/client.d.ts.map +1 -1
- package/dist/client.internal.d.ts.map +1 -1
- package/dist/client.internal.js +36 -9
- package/dist/client.internal.js.map +1 -1
- package/dist/client.js +2 -0
- package/dist/client.js.map +1 -1
- package/dist/grpcException.d.ts +243 -0
- package/dist/grpcException.d.ts.map +1 -0
- package/dist/grpcException.internal.d.ts +28 -0
- package/dist/grpcException.internal.d.ts.map +1 -0
- package/dist/grpcException.internal.js +72 -0
- package/dist/grpcException.internal.js.map +1 -0
- package/dist/grpcException.js +218 -0
- package/dist/grpcException.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/protoRuntime.d.ts +7 -6
- package/dist/protoRuntime.d.ts.map +1 -1
- package/dist/protoRuntime.internal.d.ts +7 -6
- package/dist/protoRuntime.internal.d.ts.map +1 -1
- package/dist/protoRuntime.internal.js +20 -6
- package/dist/protoRuntime.internal.js.map +1 -1
- package/dist/protoRuntime.js +2 -0
- package/dist/protoRuntime.js.map +1 -1
- package/dist/protocGenPlugin.d.ts.map +1 -1
- package/dist/protocGenPlugin.js +360 -147
- package/dist/protocGenPlugin.js.map +1 -1
- package/dist/server.d.ts +10 -6
- package/dist/server.d.ts.map +1 -1
- package/dist/server.internal.d.ts +6 -5
- package/dist/server.internal.d.ts.map +1 -1
- package/dist/server.internal.js +51 -14
- package/dist/server.internal.js.map +1 -1
- package/dist/server.js +5 -1
- package/dist/server.js.map +1 -1
- package/dist/typeUtils.js +1 -1
- package/dist/typeUtils.js.map +1 -1
- package/package.json +1 -1
package/dist/protocGenPlugin.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
// #!/usr/bin/env -S node
|
|
2
1
|
import { createEcmaScriptPlugin, safeIdentifier } from "@bufbuild/protoplugin";
|
|
3
2
|
import packageJson from "../package.json" with { type: "json" };
|
|
4
3
|
export const protocGenEffectGrpc = createEcmaScriptPlugin({
|
|
@@ -10,7 +9,9 @@ function generateTs(schema) {
|
|
|
10
9
|
for (const file of schema.files) {
|
|
11
10
|
const f = schema.generateFile(file.name + "_effect.ts");
|
|
12
11
|
f.preamble(file);
|
|
13
|
-
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));
|
|
14
15
|
/*
|
|
15
16
|
for (const service of file.services) {
|
|
16
17
|
f.print(f.jsDoc(service));
|
|
@@ -61,206 +62,418 @@ function generateTs(schema) {
|
|
|
61
62
|
}*/
|
|
62
63
|
}
|
|
63
64
|
}
|
|
64
|
-
function generateEffectService(f, service) {
|
|
65
|
+
function generateEffectService(f, service, fileBasename) {
|
|
65
66
|
const importEffect = f.import("Effect", "effect");
|
|
66
67
|
const importContext = f.import("Context", "effect");
|
|
67
68
|
const importLayer = f.import("Layer", "effect");
|
|
68
|
-
const
|
|
69
|
+
const importScope = f.import("Scope", "effect");
|
|
69
70
|
const importEffectGrpcService = f.import("EffectGrpcServer", packageJson.name);
|
|
71
|
+
const importGrpcException = f.import("GrpcException", packageJson.name);
|
|
70
72
|
const importService = f.importSchema(service);
|
|
71
73
|
const serviceId = service.typeName;
|
|
72
|
-
const serviceIdSymbol = safeIdentifier(service.name + "
|
|
74
|
+
const serviceIdSymbol = safeIdentifier(service.name + "ProtoId");
|
|
73
75
|
const serviceSymbol = safeIdentifier(service.name + "Service");
|
|
74
76
|
const grpcServiceSymbol = safeIdentifier(service.name + "GrpcService");
|
|
75
|
-
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");
|
|
76
79
|
const makeTagSymbol = safeIdentifier("make" + service.name + "ServiceTag");
|
|
77
|
-
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(" */");
|
|
78
90
|
f.print(f.export("const", serviceIdSymbol), " = ", f.string(serviceId), " as const;");
|
|
79
91
|
f.print(f.export("type", serviceIdSymbol), " = typeof ", serviceIdSymbol, ";");
|
|
80
92
|
f.print();
|
|
81
|
-
|
|
82
|
-
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> {");
|
|
83
118
|
service.methods.forEach(generateServerMethod);
|
|
84
119
|
f.print("}");
|
|
85
|
-
/**
|
|
86
|
-
* @example
|
|
87
|
-
* ```typescript
|
|
88
|
-
* export const DebugAPIService: {
|
|
89
|
-
* makeTag<Ctx>(ctxKey: string): DebugAPITag<Ctx>;
|
|
90
|
-
*
|
|
91
|
-
* liveLayer<Ctx>(service: DebugAPIService<Ctx>):
|
|
92
|
-
* <Tag extends DebugAPITag<Ctx>>(tag: Tag) => Layer.Layer<Context.Tag.Identifier<Tag>, never, never>;
|
|
93
|
-
* } = {
|
|
94
|
-
* makeTag: makeDebugAPIServiceTag,
|
|
95
|
-
* liveLayer: makeDebugApiLiveLayer
|
|
96
|
-
* };
|
|
97
|
-
* ```
|
|
98
|
-
*/
|
|
99
|
-
f.print(f.export("const", serviceSymbol), ": {");
|
|
100
|
-
f.print(" makeTag<Ctx>(ctxKey: string): ", serviceTagSymbol, "<Ctx>;");
|
|
101
120
|
f.print();
|
|
102
|
-
|
|
103
|
-
f.print("
|
|
104
|
-
f.print("
|
|
105
|
-
f.print("
|
|
106
|
-
f.print("
|
|
107
|
-
f.print("
|
|
108
|
-
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, ");");
|
|
109
227
|
f.print();
|
|
110
|
-
// export type DebugAPIGrpcService<Ctx = HandlerContext> = EffectGrpcServer.GrpcService<"DebugAPI", typeof proto.DebugAPI, Ctx>
|
|
111
|
-
f.print(f.export("type", grpcServiceSymbol), "<Ctx = ", importHandlerContext, ">", " = ", importEffectGrpcService, `.GrpcService<"`, service.typeName, `", typeof `, importService, ", Ctx>");
|
|
112
|
-
// export type DebugAPITag<Ctx> = Context.Tag<DebugAPIGrpcService<Ctx>, DebugAPIGrpcService<Ctx>>
|
|
113
|
-
f.print(f.export("type", serviceTagSymbol), "<Ctx>", " = ", importContext, ".Tag<", grpcServiceSymbol, "<Ctx>, ", grpcServiceSymbol, "<Ctx>>");
|
|
114
228
|
/**
|
|
115
229
|
* @example
|
|
116
230
|
* ```typescript
|
|
117
|
-
* function makeDebugAPIServiceTag<Ctx>(ctxKey: string
|
|
118
|
-
* 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}}>`);
|
|
119
233
|
* }
|
|
120
234
|
* ```
|
|
121
235
|
*/
|
|
122
|
-
f.print("function ", makeTagSymbol, "<Ctx>(ctxKey: string): ", serviceTagSymbol, "<Ctx> {");
|
|
236
|
+
f.print("function ", makeTagSymbol, "<Ctx = any>(ctxKey: string = ", f.string("any"), "): ", serviceTagSymbol, "<Ctx> {");
|
|
123
237
|
f.print(" return ", importContext, ".GenericTag<", grpcServiceSymbol, "<Ctx>>(`", service.typeName, "<${ctxKey}>`);");
|
|
124
238
|
f.print("}");
|
|
125
239
|
f.print();
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
* return {
|
|
136
|
-
* getDebugInfo: (req: proto.GetDebugInfoRequest, ctx: HandlerContext) => {
|
|
137
|
-
* return executor.unary(
|
|
138
|
-
* req,
|
|
139
|
-
* ctx,
|
|
140
|
-
* (req, ctx) => service.getDebugInfo(req, ctx)
|
|
141
|
-
* );
|
|
142
|
-
* }
|
|
143
|
-
* } as ServiceImpl<Proto>;
|
|
144
|
-
* }
|
|
145
|
-
* );
|
|
146
|
-
*
|
|
147
|
-
* return Layer.succeed(tag, instance);
|
|
148
|
-
*
|
|
149
|
-
* }
|
|
150
|
-
* }
|
|
151
|
-
* ```
|
|
152
|
-
*/
|
|
153
|
-
f.print("function ", makeLiveLayerSymbol, "<Ctx>(service: ", serviceSymbol, "<Ctx>) {");
|
|
154
|
-
f.print(" return <Tag extends ", serviceTagSymbol, "<Ctx>>(tag: Tag) => {");
|
|
155
|
-
f.print();
|
|
156
|
-
f.print(" const instance: ", grpcServiceSymbol, "<Ctx> = ", importEffectGrpcService, '.GrpcService("', service.typeName, '" as const, ', importService, ")(");
|
|
157
|
-
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) => ({");
|
|
158
249
|
service.methods.forEach((method) => {
|
|
159
250
|
if (method.methodKind === "unary") {
|
|
160
|
-
f.print("
|
|
251
|
+
f.print(" ", method.localName, ": (req, ctx) =>");
|
|
252
|
+
f.print(" executor.unary(req, ctx, (req, ctx) => service.", method.localName, "(req, ctx)),");
|
|
161
253
|
}
|
|
162
254
|
});
|
|
163
|
-
f.print("
|
|
164
|
-
f.print(" );");
|
|
255
|
+
f.print(" }));");
|
|
165
256
|
f.print();
|
|
166
|
-
f.print("
|
|
167
|
-
f.print(" };");
|
|
257
|
+
f.print(" return ", importLayer, ".succeed(tag, instance);");
|
|
168
258
|
f.print("}");
|
|
169
259
|
f.print();
|
|
170
260
|
const clientSymbol = safeIdentifier(service.name + "Client");
|
|
171
|
-
/**
|
|
172
|
-
* @example
|
|
173
|
-
* ```typescript
|
|
174
|
-
* export interface DebugAPIClient<Meta> {
|
|
175
|
-
* getDebugInfo(
|
|
176
|
-
* request: MessageInitShape<typeof proto.GetDebugInfoRequestSchema>,
|
|
177
|
-
* meta: Meta,
|
|
178
|
-
* ): Effect.Effect<proto.GetDebugInfoResponse>
|
|
179
|
-
* }
|
|
180
|
-
* ```
|
|
181
|
-
*/
|
|
182
|
-
f.print(f.jsDoc(service));
|
|
183
|
-
f.print(f.export("interface", clientSymbol), "<Meta> {");
|
|
184
|
-
service.methods.forEach(generateClientMethod);
|
|
185
|
-
f.print("}");
|
|
186
|
-
/**
|
|
187
|
-
* @example
|
|
188
|
-
* ```typescript
|
|
189
|
-
* export const DebugAPIClient: {
|
|
190
|
-
* makeTag<Meta>(metaKey: string): DebugAPIClientTag<Meta>
|
|
191
|
-
*
|
|
192
|
-
* liveLayer<Meta>(transformMeta: (meta: Meta) => RequestMeta):
|
|
193
|
-
* <Tag extends DebugAPIClientTag<Meta>>(tag: Tag) => Layer.Layer<Context.Tag.Identifier<Tag>, never, EffectGrpcClient.GrpcClient>
|
|
194
|
-
* } = {
|
|
195
|
-
* makeTag: makeDebugAPIClientTag,
|
|
196
|
-
* liveLayer: makeDebugApiClientLiveLayer,
|
|
197
|
-
* }
|
|
198
|
-
* ```
|
|
199
|
-
*/
|
|
200
261
|
const importEffectGrpcClient = f.import("EffectGrpcClient", packageJson.name);
|
|
201
262
|
const clientTagSymbol = safeIdentifier(service.name + "ClientTag");
|
|
263
|
+
const clientLiveLayerSymbol = safeIdentifier(service.name.charAt(0).toLowerCase() + service.name.slice(1) + "ClientLiveLayer");
|
|
202
264
|
const configTagSymbol = safeIdentifier(service.name + "ConfigTag");
|
|
203
265
|
const makeClientTagSymbol = safeIdentifier("make" + service.name + "ClientTag");
|
|
204
266
|
const makeClientLiveLayerSymbol = safeIdentifier("make" + service.name + "ClientLiveLayer");
|
|
205
|
-
|
|
206
|
-
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, ");");
|
|
207
357
|
f.print();
|
|
208
|
-
|
|
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>(");
|
|
209
417
|
f.print(" transformMeta: (meta: Meta) => ", importEffectGrpcClient, ".RequestMeta,");
|
|
210
|
-
f.print(" tag: Tag");
|
|
418
|
+
f.print(" tag: Tag,");
|
|
211
419
|
f.print(" ): ", importLayer, ".Layer<");
|
|
212
420
|
f.print(" ", importContext, ".Tag.Identifier<Tag>,");
|
|
213
421
|
f.print(" never,");
|
|
214
|
-
f.print(" ",
|
|
422
|
+
f.print(" ", configTagSymbol, "[\"Identifier\"] | ", importEffectGrpcClient, ".GrpcClientRuntime | ", importScope, ".Scope");
|
|
215
423
|
f.print(" >;");
|
|
216
424
|
f.print();
|
|
217
|
-
f.print("
|
|
218
|
-
f.print(" tag: Tag");
|
|
425
|
+
f.print(" <Tag extends ", clientTagSymbol, ">(");
|
|
426
|
+
f.print(" tag: Tag,");
|
|
219
427
|
f.print(" ): ", importLayer, ".Layer<");
|
|
220
428
|
f.print(" ", importContext, ".Tag.Identifier<Tag>,");
|
|
221
429
|
f.print(" never,");
|
|
222
|
-
f.print(" ",
|
|
430
|
+
f.print(" ", configTagSymbol, "[\"Identifier\"] | ", importEffectGrpcClient, ".GrpcClientRuntime | ", importScope, ".Scope");
|
|
223
431
|
f.print(" >;");
|
|
224
|
-
f.print("} =
|
|
225
|
-
f.print(" makeTag: ", makeClientTagSymbol, ",");
|
|
226
|
-
f.print(" liveLayer: ", makeClientLiveLayerSymbol, ",");
|
|
227
|
-
f.print("};");
|
|
228
|
-
f.print();
|
|
229
|
-
// export type DebugAPIClientTag<Meta> = Context.Tag<DebugAPIClient<Meta>, DebugAPIClient<Meta>>
|
|
230
|
-
f.print(f.export("type", clientTagSymbol), "<Meta>", " = ", importContext, ".Tag<", clientSymbol, "<Meta>, ", clientSymbol, "<Meta>>");
|
|
432
|
+
f.print("} = ", makeClientLiveLayerSymbol, ";");
|
|
231
433
|
f.print();
|
|
232
|
-
//
|
|
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(" */");
|
|
233
443
|
f.print(f.export("type", configTagSymbol), " = ", importContext, ".Tag<", importEffectGrpcClient, ".GrpcClientConfig<", serviceIdSymbol, ">, ", importEffectGrpcClient, ".GrpcClientConfig<", serviceIdSymbol, ">>");
|
|
234
|
-
|
|
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(" */");
|
|
235
469
|
f.print(f.export("const", configTagSymbol), ": ", configTagSymbol, " = ", importEffectGrpcClient, ".GrpcClientConfig.makeTag(", serviceIdSymbol, ");");
|
|
236
470
|
f.print();
|
|
237
|
-
//
|
|
471
|
+
// Generate makeClientTag helper function
|
|
238
472
|
f.print("function ", makeClientTagSymbol, "<Meta>(metaKey: string): ", clientTagSymbol, "<Meta> {");
|
|
239
|
-
f.print(" return ", importContext, ".GenericTag<", clientSymbol, "<Meta>>(
|
|
473
|
+
f.print(" return ", importContext, ".GenericTag<", clientSymbol, "<Meta>>(`${", serviceIdSymbol, "}<${metaKey}>`);");
|
|
240
474
|
f.print("}");
|
|
241
475
|
f.print();
|
|
242
|
-
|
|
243
|
-
* @example
|
|
244
|
-
* ```typescript
|
|
245
|
-
* function makeDebugApiClientLiveLayer<Meta>(transformMeta: (meta: Meta) => RequestMeta) {
|
|
246
|
-
* return <Tag extends DebugAPIClientTag<Meta>>(tag: Tag) => {
|
|
247
|
-
* const prog = Effect.gen(function* () {
|
|
248
|
-
* const grpcService = yield* EffectGrpcClient.GrpcClient
|
|
249
|
-
* const executor = grpcService.makeExecutor(proto.DebugAPI, ["getDebugInfo"]);
|
|
250
|
-
*
|
|
251
|
-
* return {
|
|
252
|
-
* getDebugInfo(req, meta) {
|
|
253
|
-
* return executor.getDebugInfo(req, meta ?? transformMeta(meta));
|
|
254
|
-
* },
|
|
255
|
-
* } as DebugAPIClient<Meta>;
|
|
256
|
-
* });
|
|
257
|
-
*
|
|
258
|
-
* return Layer.effect(tag, prog);
|
|
259
|
-
* }
|
|
260
|
-
* }
|
|
261
|
-
* ```
|
|
262
|
-
*/
|
|
263
|
-
// function makeDebugAPIClientLiveLayer<Tag extends DebugAPIClientTag<Meta>, Meta = object>(...args: readonly [(meta: Meta) => RequestMeta, Tag] | readonly [Tag])
|
|
476
|
+
// Generate client implementation function with overload handling
|
|
264
477
|
f.print("function ", makeClientLiveLayerSymbol, "<Tag extends ", clientTagSymbol, "<Meta>, Meta = object>(");
|
|
265
478
|
f.print(" ...args: readonly [(meta: Meta) => ", importEffectGrpcClient, ".RequestMeta, Tag] | readonly [Tag]");
|
|
266
479
|
f.print(") {");
|
|
@@ -324,7 +537,7 @@ function generateEffectService(f, service) {
|
|
|
324
537
|
case "unary":
|
|
325
538
|
f.print();
|
|
326
539
|
f.print(f.jsDoc(method, " "));
|
|
327
|
-
f.print(" ", method.localName, "(request: ", inputType, ", ctx: Ctx): ", importEffect, ".Effect<", importMessageInitShape, "<typeof ", outputDesc, "
|
|
540
|
+
f.print(" ", method.localName, "(request: ", inputType, ", ctx: Ctx): ", importEffect, ".Effect<", importMessageInitShape, "<typeof ", outputDesc, ">, ", importGrpcException, ".GrpcException>;");
|
|
328
541
|
return;
|
|
329
542
|
default:
|
|
330
543
|
f.print("// Method[", method.localName, "] wasn't generated: methodKind is not yet supported [", method.methodKind, "]");
|