@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.
Files changed (42) hide show
  1. package/README.md +863 -0
  2. package/dist/client.d.ts +38 -3
  3. package/dist/client.d.ts.map +1 -1
  4. package/dist/client.internal.d.ts.map +1 -1
  5. package/dist/client.internal.js +36 -9
  6. package/dist/client.internal.js.map +1 -1
  7. package/dist/client.js +2 -0
  8. package/dist/client.js.map +1 -1
  9. package/dist/grpcException.d.ts +243 -0
  10. package/dist/grpcException.d.ts.map +1 -0
  11. package/dist/grpcException.internal.d.ts +28 -0
  12. package/dist/grpcException.internal.d.ts.map +1 -0
  13. package/dist/grpcException.internal.js +72 -0
  14. package/dist/grpcException.internal.js.map +1 -0
  15. package/dist/grpcException.js +218 -0
  16. package/dist/grpcException.js.map +1 -0
  17. package/dist/index.d.ts +1 -0
  18. package/dist/index.d.ts.map +1 -1
  19. package/dist/index.js +1 -0
  20. package/dist/index.js.map +1 -1
  21. package/dist/protoRuntime.d.ts +7 -6
  22. package/dist/protoRuntime.d.ts.map +1 -1
  23. package/dist/protoRuntime.internal.d.ts +7 -6
  24. package/dist/protoRuntime.internal.d.ts.map +1 -1
  25. package/dist/protoRuntime.internal.js +20 -6
  26. package/dist/protoRuntime.internal.js.map +1 -1
  27. package/dist/protoRuntime.js +2 -0
  28. package/dist/protoRuntime.js.map +1 -1
  29. package/dist/protocGenPlugin.d.ts.map +1 -1
  30. package/dist/protocGenPlugin.js +360 -147
  31. package/dist/protocGenPlugin.js.map +1 -1
  32. package/dist/server.d.ts +10 -6
  33. package/dist/server.d.ts.map +1 -1
  34. package/dist/server.internal.d.ts +6 -5
  35. package/dist/server.internal.d.ts.map +1 -1
  36. package/dist/server.internal.js +51 -14
  37. package/dist/server.internal.js.map +1 -1
  38. package/dist/server.js +5 -1
  39. package/dist/server.js.map +1 -1
  40. package/dist/typeUtils.js +1 -1
  41. package/dist/typeUtils.js.map +1 -1
  42. package/package.json +1 -1
@@ -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.services.forEach(service => generateEffectService(f, service));
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 importHandlerContext = f.import("HandlerContext", "@connectrpc/connect");
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 + "Id");
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 + "Tag");
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 + "LiveLayer");
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
- f.print(f.jsDoc(service));
82
- f.print(f.export("interface", serviceSymbol), "<Ctx> {");
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
- f.print(" liveLayer<Ctx>(");
103
- f.print(" service: ", serviceSymbol, "<Ctx>");
104
- f.print(" ): <Tag extends ", serviceTagSymbol, "<Ctx>>(tag: Tag) => ", importLayer, ".Layer<", importContext, ".Tag.Identifier<Tag>, never, never>;");
105
- f.print("} = {");
106
- f.print(" makeTag: ", makeTagSymbol, ",");
107
- f.print(" liveLayer: ", makeLiveLayerSymbol);
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 & keyof Ctx): DebugAPITag<Ctx> {
118
- * return Context.GenericTag<DebugAPIGrpcService<Ctx>, DebugAPIGrpcService<Ctx>>(`${proto.DebugAPI.typeName}<${ctxKey}}>`);
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
- * @example
128
- * ```typescript
129
- * function makeDebugApiLiveLayer<Ctx>(service: DebugAPIService<Ctx>) {
130
- * return <Tag extends DebugAPITag<Ctx>>(tag: Tag) => {
131
- * type Proto = typeof proto.DebugAPI;
132
- *
133
- * const instance: DebugAPIGrpcService<Ctx> = EffectGrpcServer.GrpcService("DebugAPI" as const, proto.DebugAPI)(
134
- * (executor: EffectGrpcServer.Executor<Ctx>): ServiceImpl<Proto> => {
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(" ", method.localName, ": (req, ctx) => executor.unary(req, ctx, (req, ctx) => service.", method.localName, "(req, ctx))", ",");
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(" return ", importLayer, ".succeed(tag, instance);");
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
- f.print(f.export("const", clientSymbol), ": {");
206
- f.print(" makeTag<Meta>(metaKey: string): ", clientTagSymbol, "<Meta>;");
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
- f.print(" liveLayer<Tag extends ", clientTagSymbol, "<Meta>, Meta>(");
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(" ", importContext, ".Tag.Identifier<", configTagSymbol, "> | ", importEffectGrpcClient, ".GrpcClientRuntime");
422
+ f.print(" ", configTagSymbol, "[\"Identifier\"] | ", importEffectGrpcClient, ".GrpcClientRuntime | ", importScope, ".Scope");
215
423
  f.print(" >;");
216
424
  f.print();
217
- f.print(" liveLayer<Tag extends ", clientTagSymbol, "<object>>(");
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(" ", importContext, ".Tag.Identifier<", configTagSymbol, "> | ", importEffectGrpcClient, ".GrpcClientRuntime");
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
- // export type HelloWorldAPIConfigTag = Context.Tag<EffectGrpcClient.GrpcClientConfig<HelloWorldAPIId>, EffectGrpcClient.GrpcClientConfig<HelloWorldAPIId>>
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
- // export const HelloWorldAPIConfigTag: HelloWorldAPIConfigTag = EffectGrpcClient.GrpcClientConfig.makeTag(HelloWorldAPIId);
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
- // function makeDebugAPIClientTag<Meta>(metaKey: string): DebugAPIClientTag<Meta>
471
+ // Generate makeClientTag helper function
238
472
  f.print("function ", makeClientTagSymbol, "<Meta>(metaKey: string): ", clientTagSymbol, "<Meta> {");
239
- f.print(" return ", importContext, ".GenericTag<", clientSymbol, "<Meta>>(`", service.typeName, "Client<${metaKey}>`);");
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, "]");