@effect/openapi-generator 4.0.0-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. package/LICENSE +21 -0
  2. package/dist/JsonSchemaGenerator.d.ts +8 -0
  3. package/dist/JsonSchemaGenerator.d.ts.map +1 -0
  4. package/dist/JsonSchemaGenerator.js +75 -0
  5. package/dist/JsonSchemaGenerator.js.map +1 -0
  6. package/dist/OpenApiGenerator.d.ts +32 -0
  7. package/dist/OpenApiGenerator.d.ts.map +1 -0
  8. package/dist/OpenApiGenerator.js +206 -0
  9. package/dist/OpenApiGenerator.js.map +1 -0
  10. package/dist/OpenApiPatch.d.ts +296 -0
  11. package/dist/OpenApiPatch.d.ts.map +1 -0
  12. package/dist/OpenApiPatch.js +448 -0
  13. package/dist/OpenApiPatch.js.map +1 -0
  14. package/dist/OpenApiTransformer.d.ts +24 -0
  15. package/dist/OpenApiTransformer.d.ts.map +1 -0
  16. package/dist/OpenApiTransformer.js +740 -0
  17. package/dist/OpenApiTransformer.js.map +1 -0
  18. package/dist/ParsedOperation.d.ts +29 -0
  19. package/dist/ParsedOperation.d.ts.map +1 -0
  20. package/dist/ParsedOperation.js +13 -0
  21. package/dist/ParsedOperation.js.map +1 -0
  22. package/dist/Utils.d.ts +6 -0
  23. package/dist/Utils.d.ts.map +1 -0
  24. package/dist/Utils.js +42 -0
  25. package/dist/Utils.js.map +1 -0
  26. package/dist/bin.d.ts +3 -0
  27. package/dist/bin.d.ts.map +1 -0
  28. package/dist/bin.js +7 -0
  29. package/dist/bin.js.map +1 -0
  30. package/dist/main.d.ts +5 -0
  31. package/dist/main.d.ts.map +1 -0
  32. package/dist/main.js +47 -0
  33. package/dist/main.js.map +1 -0
  34. package/package.json +67 -0
  35. package/src/JsonSchemaGenerator.ts +94 -0
  36. package/src/OpenApiGenerator.ts +309 -0
  37. package/src/OpenApiPatch.ts +514 -0
  38. package/src/OpenApiTransformer.ts +954 -0
  39. package/src/ParsedOperation.ts +43 -0
  40. package/src/Utils.ts +50 -0
  41. package/src/bin.ts +10 -0
  42. package/src/main.ts +68 -0
@@ -0,0 +1,740 @@
1
+ import * as Layer from "effect/Layer";
2
+ import * as Predicate from "effect/Predicate";
3
+ import * as ServiceMap from "effect/ServiceMap";
4
+ import * as Utils from "./Utils.js";
5
+ export class OpenApiTransformer extends /*#__PURE__*/ServiceMap.Service()("OpenApiTransformer") {}
6
+ const computeImportRequirements = operations => {
7
+ let eventStream = false;
8
+ let octetStream = false;
9
+ for (const op of operations) {
10
+ if (op.sseSchema) {
11
+ eventStream = true;
12
+ }
13
+ if (op.binaryResponse) {
14
+ octetStream = true;
15
+ }
16
+ }
17
+ return {
18
+ eventStream,
19
+ octetStream
20
+ };
21
+ };
22
+ const requiresStreaming = requirements => requirements.eventStream || requirements.octetStream;
23
+ const httpClientMethodNames = {
24
+ get: "get",
25
+ put: "put",
26
+ post: "post",
27
+ delete: "del",
28
+ options: "options",
29
+ head: "head",
30
+ patch: "patch",
31
+ trace: `make("TRACE")`
32
+ };
33
+ export const makeTransformerSchema = () => {
34
+ const operationsToInterface = (_importName, name, operations) => {
35
+ const methods = [];
36
+ for (const op of operations) {
37
+ methods.push(operationToMethod(name, op));
38
+ if (op.sseSchema) {
39
+ methods.push(operationToSseMethod(name, op));
40
+ }
41
+ if (op.binaryResponse) {
42
+ methods.push(operationToBinaryMethod(name, op));
43
+ }
44
+ }
45
+ return `export interface ${name} {
46
+ readonly httpClient: HttpClient.HttpClient
47
+ ${methods.join("\n ")}
48
+ }
49
+
50
+ ${clientErrorSource(name)}`;
51
+ };
52
+ const operationToMethod = (name, operation) => {
53
+ const args = [];
54
+ if (operation.pathIds.length > 0) {
55
+ Utils.spreadElementsInto(operation.pathIds.map(id => `${id}: string`), args);
56
+ }
57
+ const options = [];
58
+ if (operation.params) {
59
+ const key = `readonly params${operation.paramsOptional ? "?" : ""}`;
60
+ const type = `typeof ${operation.params}.Encoded${operation.paramsOptional ? " | undefined" : ""}`;
61
+ options.push(`${key}: ${type}`);
62
+ }
63
+ if (operation.payload) {
64
+ const key = `readonly payload`;
65
+ const type = `typeof ${operation.payload}.Encoded`;
66
+ options.push(`${key}: ${type}`);
67
+ }
68
+ options.push("readonly config?: Config | undefined");
69
+ // If all options are optional, the argument itself should be optional
70
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
71
+ if (hasOptions) {
72
+ args.push(`options: { ${options.join("; ")} }`);
73
+ } else {
74
+ args.push(`options: { ${options.join("; ")} } | undefined`);
75
+ }
76
+ let success = "void";
77
+ if (operation.successSchemas.size > 0) {
78
+ success = Array.from(operation.successSchemas.values()).map(schema => `typeof ${schema}.Type`).join(" | ");
79
+ }
80
+ const errors = ["HttpClientError.HttpClientError", "SchemaError"];
81
+ if (operation.errorSchemas.size > 0) {
82
+ Utils.spreadElementsInto(Array.from(operation.errorSchemas.values()).map(schema => `${name}Error<"${schema}", typeof ${schema}.Type>`), errors);
83
+ }
84
+ const jsdoc = Utils.toComment(operation.description);
85
+ const methodKey = `readonly "${operation.id}"`;
86
+ const generic = `<Config extends OperationConfig>`;
87
+ const parameters = args.join(", ");
88
+ const returnType = `Effect.Effect<WithOptionalResponse<${success}, Config>, ${errors.join(" | ")}>`;
89
+ return `${jsdoc}${methodKey}: ${generic}(${parameters}) => ${returnType}`;
90
+ };
91
+ const operationToSseMethod = (_name, operation) => {
92
+ const args = [];
93
+ if (operation.pathIds.length > 0) {
94
+ Utils.spreadElementsInto(operation.pathIds.map(id => `${id}: string`), args);
95
+ }
96
+ const options = [];
97
+ if (operation.params) {
98
+ const key = `readonly params${operation.paramsOptional ? "?" : ""}`;
99
+ const type = `typeof ${operation.params}.Encoded${operation.paramsOptional ? " | undefined" : ""}`;
100
+ options.push(`${key}: ${type}`);
101
+ }
102
+ if (operation.payload) {
103
+ options.push(`readonly payload: typeof ${operation.payload}.Encoded`);
104
+ }
105
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
106
+ if (hasOptions) {
107
+ args.push(`options: { ${options.join("; ")} }`);
108
+ } else if (options.length > 0) {
109
+ args.push(`options: { ${options.join("; ")} } | undefined`);
110
+ }
111
+ const jsdoc = Utils.toComment(operation.description);
112
+ const methodKey = `readonly "${operation.id}Sse"`;
113
+ const parameters = args.join(", ");
114
+ const returnType = `Stream.Stream<{ readonly event: string; readonly id: string | undefined; readonly data: typeof ${operation.sseSchema}.Type }, HttpClientError.HttpClientError | SchemaError | Sse.Retry, typeof ${operation.sseSchema}.DecodingServices>`;
115
+ return `${jsdoc}${methodKey}: (${parameters}) => ${returnType}`;
116
+ };
117
+ const operationToBinaryMethod = (_name, operation) => {
118
+ const args = [];
119
+ if (operation.pathIds.length > 0) {
120
+ Utils.spreadElementsInto(operation.pathIds.map(id => `${id}: string`), args);
121
+ }
122
+ const options = [];
123
+ if (operation.params) {
124
+ const key = `readonly params${operation.paramsOptional ? "?" : ""}`;
125
+ const type = `typeof ${operation.params}.Encoded${operation.paramsOptional ? " | undefined" : ""}`;
126
+ options.push(`${key}: ${type}`);
127
+ }
128
+ if (operation.payload) {
129
+ options.push(`readonly payload: typeof ${operation.payload}.Encoded`);
130
+ }
131
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
132
+ if (hasOptions) {
133
+ args.push(`options: { ${options.join("; ")} }`);
134
+ } else if (options.length > 0) {
135
+ args.push(`options: { ${options.join("; ")} } | undefined`);
136
+ }
137
+ const jsdoc = Utils.toComment(operation.description);
138
+ const methodKey = `readonly "${operation.id}Stream"`;
139
+ const parameters = args.join(", ");
140
+ const returnType = `Stream.Stream<Uint8Array, HttpClientError.HttpClientError>`;
141
+ return `${jsdoc}${methodKey}: (${parameters}) => ${returnType}`;
142
+ };
143
+ const operationsToImpl = (importName, name, operations) => {
144
+ const requirements = computeImportRequirements(operations);
145
+ const implMethods = [];
146
+ for (const op of operations) {
147
+ implMethods.push(operationToImpl(op));
148
+ if (op.sseSchema) {
149
+ implMethods.push(operationToSseImpl(importName, op));
150
+ }
151
+ if (op.binaryResponse) {
152
+ implMethods.push(operationToBinaryImpl(op));
153
+ }
154
+ }
155
+ const helpers = [commonSource];
156
+ if (requirements.eventStream) {
157
+ helpers.push(sseRequestSource(importName));
158
+ }
159
+ if (requirements.octetStream) {
160
+ helpers.push(binaryRequestSource);
161
+ }
162
+ return `export interface OperationConfig {
163
+ /**
164
+ * Whether or not the response should be included in the value returned from
165
+ * an operation.
166
+ *
167
+ * If set to \`true\`, a tuple of \`[A, HttpClientResponse]\` will be returned,
168
+ * where \`A\` is the success type of the operation.
169
+ *
170
+ * If set to \`false\`, only the success type of the operation will be returned.
171
+ */
172
+ readonly includeResponse?: boolean | undefined
173
+ }
174
+
175
+ /**
176
+ * A utility type which optionally includes the response in the return result
177
+ * of an operation based upon the value of the \`includeResponse\` configuration
178
+ * option.
179
+ */
180
+ export type WithOptionalResponse<A, Config extends OperationConfig> = Config extends {
181
+ readonly includeResponse: true
182
+ } ? [A, HttpClientResponse.HttpClientResponse] : A
183
+
184
+ export const make = (
185
+ httpClient: HttpClient.HttpClient,
186
+ options: {
187
+ readonly transformClient?: ((client: HttpClient.HttpClient) => Effect.Effect<HttpClient.HttpClient>) | undefined
188
+ } = {}
189
+ ): ${name} => {
190
+ ${helpers.join("\n ")}
191
+ const decodeSuccess =
192
+ <Schema extends ${importName}.Top>(schema: Schema) =>
193
+ (response: HttpClientResponse.HttpClientResponse) =>
194
+ HttpClientResponse.schemaBodyJson(schema)(response)
195
+ const decodeError =
196
+ <const Tag extends string, Schema extends ${importName}.Top>(tag: Tag, schema: Schema) =>
197
+ (response: HttpClientResponse.HttpClientResponse) =>
198
+ Effect.flatMap(
199
+ HttpClientResponse.schemaBodyJson(schema)(response),
200
+ (cause) => Effect.fail(${name}Error(tag, cause, response)),
201
+ )
202
+ return {
203
+ httpClient,
204
+ ${implMethods.join(",\n ")}
205
+ }
206
+ }`;
207
+ };
208
+ const operationToImpl = operation => {
209
+ const args = [...operation.pathIds, "options"];
210
+ const params = `${args.join(", ")}`;
211
+ const pipeline = [];
212
+ if (operation.params) {
213
+ const paramsAccessor = resolveParamsAccessor(operation, "options", "params");
214
+ if (operation.urlParams.length > 0) {
215
+ const props = operation.urlParams.map(param => `"${param}": ${paramsAccessor}["${param}"] as any`);
216
+ pipeline.push(`HttpClientRequest.setUrlParams({ ${props.join(", ")} })`);
217
+ }
218
+ if (operation.headers.length > 0) {
219
+ const props = operation.headers.map(param => `"${param}": ${paramsAccessor}["${param}"] ?? undefined`);
220
+ pipeline.push(`HttpClientRequest.setHeaders({ ${props.join(", ")} })`);
221
+ }
222
+ }
223
+ const payloadVarName = "options.payload";
224
+ if (operation.payloadFormData) {
225
+ pipeline.push(`HttpClientRequest.bodyFormData(${payloadVarName} as any)`);
226
+ } else if (operation.payload) {
227
+ pipeline.push(`HttpClientRequest.bodyJsonUnsafe(${payloadVarName})`);
228
+ }
229
+ const decodes = [];
230
+ const singleSuccessCode = operation.successSchemas.size === 1;
231
+ operation.successSchemas.forEach((schema, status) => {
232
+ const statusCode = singleSuccessCode && status.startsWith("2") ? "2xx" : status;
233
+ decodes.push(`"${statusCode}": decodeSuccess(${schema})`);
234
+ });
235
+ operation.errorSchemas.forEach((schema, status) => {
236
+ decodes.push(`"${status}": decodeError("${schema}", ${schema})`);
237
+ });
238
+ operation.voidSchemas.forEach(status => {
239
+ decodes.push(`"${status}": () => Effect.void`);
240
+ });
241
+ decodes.push(`orElse: unexpectedStatus`);
242
+ const configAccessor = resolveConfigAccessor(operation, "options", "config");
243
+ pipeline.push(`withResponse(${configAccessor})(HttpClientResponse.matchStatus({
244
+ ${decodes.join(",\n ")}
245
+ }))`);
246
+ return `"${operation.id}": (${params}) => ` + `HttpClientRequest.${httpClientMethodNames[operation.method]}(${operation.pathTemplate})` + `.pipe(\n ${pipeline.join(",\n ")}\n )`;
247
+ };
248
+ const operationToSseImpl = (_importName, operation) => {
249
+ const args = [...operation.pathIds];
250
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
251
+ if (hasOptions || operation.params || operation.payload) {
252
+ args.push("options");
253
+ }
254
+ const params = args.join(", ");
255
+ const pipeline = [];
256
+ if (operation.params) {
257
+ const paramsAccessor = resolveParamsAccessor(operation, "options", "params");
258
+ if (operation.urlParams.length > 0) {
259
+ const props = operation.urlParams.map(param => `"${param}": ${paramsAccessor}["${param}"] as any`);
260
+ pipeline.push(`HttpClientRequest.setUrlParams({ ${props.join(", ")} })`);
261
+ }
262
+ if (operation.headers.length > 0) {
263
+ const props = operation.headers.map(param => `"${param}": ${paramsAccessor}["${param}"] ?? undefined`);
264
+ pipeline.push(`HttpClientRequest.setHeaders({ ${props.join(", ")} })`);
265
+ }
266
+ }
267
+ if (operation.payloadFormData) {
268
+ pipeline.push(`HttpClientRequest.bodyFormData(options.payload as any)`);
269
+ } else if (operation.payload) {
270
+ pipeline.push(`HttpClientRequest.bodyJsonUnsafe(options.payload)`);
271
+ }
272
+ pipeline.push(`sseRequest(${operation.sseSchema})`);
273
+ return `"${operation.id}Sse": (${params}) => ` + `HttpClientRequest.${httpClientMethodNames[operation.method]}(${operation.pathTemplate})` + `.pipe(\n ${pipeline.join(",\n ")}\n )`;
274
+ };
275
+ const operationToBinaryImpl = operation => {
276
+ const args = [...operation.pathIds];
277
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
278
+ if (hasOptions || operation.params || operation.payload) {
279
+ args.push("options");
280
+ }
281
+ const params = args.join(", ");
282
+ const pipeline = [];
283
+ if (operation.params) {
284
+ const paramsAccessor = resolveParamsAccessor(operation, "options", "params");
285
+ if (operation.urlParams.length > 0) {
286
+ const props = operation.urlParams.map(param => `"${param}": ${paramsAccessor}["${param}"] as any`);
287
+ pipeline.push(`HttpClientRequest.setUrlParams({ ${props.join(", ")} })`);
288
+ }
289
+ if (operation.headers.length > 0) {
290
+ const props = operation.headers.map(param => `"${param}": ${paramsAccessor}["${param}"] ?? undefined`);
291
+ pipeline.push(`HttpClientRequest.setHeaders({ ${props.join(", ")} })`);
292
+ }
293
+ }
294
+ if (operation.payloadFormData) {
295
+ pipeline.push(`HttpClientRequest.bodyFormData(options.payload as any)`);
296
+ } else if (operation.payload) {
297
+ pipeline.push(`HttpClientRequest.bodyJsonUnsafe(options.payload)`);
298
+ }
299
+ pipeline.push(`binaryRequest`);
300
+ return `"${operation.id}Stream": (${params}) => ` + `HttpClientRequest.${httpClientMethodNames[operation.method]}(${operation.pathTemplate})` + `.pipe(\n ${pipeline.join(",\n ")}\n )`;
301
+ };
302
+ return OpenApiTransformer.of({
303
+ imports: (importName, operations) => {
304
+ const requirements = computeImportRequirements(operations);
305
+ const imports = [`import * as Data from "effect/Data"`, `import * as Effect from "effect/Effect"`, `import type { SchemaError } from "effect/Schema"`, `import * as ${importName} from "effect/Schema"`];
306
+ if (requiresStreaming(requirements)) {
307
+ imports.push(`import * as Stream from "effect/Stream"`);
308
+ }
309
+ if (requirements.eventStream) {
310
+ imports.push(`import * as Sse from "effect/unstable/encoding/Sse"`);
311
+ }
312
+ // HttpClient needs to be a value import when streaming is used (for filterStatusOk)
313
+ if (requiresStreaming(requirements)) {
314
+ imports.push(`import * as HttpClient from "effect/unstable/http/HttpClient"`);
315
+ } else {
316
+ imports.push(`import type * as HttpClient from "effect/unstable/http/HttpClient"`);
317
+ }
318
+ imports.push(`import * as HttpClientError from "effect/unstable/http/HttpClientError"`, `import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"`, `import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"`);
319
+ return imports.join("\n");
320
+ },
321
+ toTypes: operationsToInterface,
322
+ toImplementation: operationsToImpl
323
+ });
324
+ };
325
+ export const layerTransformerSchema = /*#__PURE__*/Layer.sync(OpenApiTransformer, makeTransformerSchema);
326
+ export const makeTransformerTs = () => {
327
+ const operationsToInterface = (_importName, name, operations) => {
328
+ const methods = [];
329
+ for (const op of operations) {
330
+ methods.push(operationToMethod(name, op));
331
+ if (op.sseSchema) {
332
+ methods.push(operationToSseMethod(op));
333
+ }
334
+ if (op.binaryResponse) {
335
+ methods.push(operationToBinaryMethod(op));
336
+ }
337
+ }
338
+ return `export interface ${name} {
339
+ readonly httpClient: HttpClient.HttpClient
340
+ ${methods.join("\n ")}
341
+ }
342
+
343
+ ${clientErrorSource(name)}`;
344
+ };
345
+ const operationToMethod = (name, operation) => {
346
+ const args = [];
347
+ if (operation.pathIds.length > 0) {
348
+ Utils.spreadElementsInto(operation.pathIds.map(id => `${id}: string`), args);
349
+ }
350
+ const options = [];
351
+ if (operation.params) {
352
+ const key = `readonly params${operation.paramsOptional ? "?" : ""}`;
353
+ const type = `${operation.params}${operation.paramsOptional ? " | undefined" : ""}`;
354
+ options.push(`${key}: ${type}`);
355
+ }
356
+ if (operation.payload) {
357
+ options.push(`readonly payload: ${operation.payload}`);
358
+ }
359
+ options.push("readonly config?: Config | undefined");
360
+ // If all options are optional, the argument itself should be optional
361
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
362
+ if (hasOptions) {
363
+ args.push(`options: { ${options.join("; ")} }`);
364
+ } else {
365
+ args.push(`options: { ${options.join("; ")} } | undefined`);
366
+ }
367
+ let success = "void";
368
+ if (operation.successSchemas.size > 0) {
369
+ success = Array.from(operation.successSchemas.values()).join(" | ");
370
+ }
371
+ const errors = ["HttpClientError.HttpClientError"];
372
+ if (operation.errorSchemas.size > 0) {
373
+ for (const schema of operation.errorSchemas.values()) {
374
+ errors.push(`${name}Error<"${schema}", ${schema}>`);
375
+ }
376
+ }
377
+ const jsdoc = Utils.toComment(operation.description);
378
+ const methodKey = `readonly "${operation.id}"`;
379
+ const generic = `<Config extends OperationConfig>`;
380
+ const parameters = args.join(", ");
381
+ const returnType = `Effect.Effect<WithOptionalResponse<${success}, Config>, ${errors.join(" | ")}>`;
382
+ return `${jsdoc}${methodKey}: ${generic}(${parameters}) => ${returnType}`;
383
+ };
384
+ const operationToSseMethod = operation => {
385
+ const args = [];
386
+ if (operation.pathIds.length > 0) {
387
+ Utils.spreadElementsInto(operation.pathIds.map(id => `${id}: string`), args);
388
+ }
389
+ const options = [];
390
+ if (operation.params) {
391
+ const key = `readonly params${operation.paramsOptional ? "?" : ""}`;
392
+ const type = `${operation.params}${operation.paramsOptional ? " | undefined" : ""}`;
393
+ options.push(`${key}: ${type}`);
394
+ }
395
+ if (operation.payload) {
396
+ options.push(`readonly payload: ${operation.payload}`);
397
+ }
398
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
399
+ if (hasOptions) {
400
+ args.push(`options: { ${options.join("; ")} }`);
401
+ } else if (options.length > 0) {
402
+ args.push(`options: { ${options.join("; ")} } | undefined`);
403
+ }
404
+ const jsdoc = Utils.toComment(operation.description);
405
+ const methodKey = `readonly "${operation.id}Sse"`;
406
+ const parameters = args.join(", ");
407
+ const returnType = `Stream.Stream<${operation.sseSchema}, HttpClientError.HttpClientError>`;
408
+ return `${jsdoc}${methodKey}: (${parameters}) => ${returnType}`;
409
+ };
410
+ const operationToBinaryMethod = operation => {
411
+ const args = [];
412
+ if (operation.pathIds.length > 0) {
413
+ Utils.spreadElementsInto(operation.pathIds.map(id => `${id}: string`), args);
414
+ }
415
+ const options = [];
416
+ if (operation.params) {
417
+ const key = `readonly params${operation.paramsOptional ? "?" : ""}`;
418
+ const type = `${operation.params}${operation.paramsOptional ? " | undefined" : ""}`;
419
+ options.push(`${key}: ${type}`);
420
+ }
421
+ if (operation.payload) {
422
+ options.push(`readonly payload: ${operation.payload}`);
423
+ }
424
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
425
+ if (hasOptions) {
426
+ args.push(`options: { ${options.join("; ")} }`);
427
+ } else if (options.length > 0) {
428
+ args.push(`options: { ${options.join("; ")} } | undefined`);
429
+ }
430
+ const jsdoc = Utils.toComment(operation.description);
431
+ const methodKey = `readonly "${operation.id}Stream"`;
432
+ const parameters = args.join(", ");
433
+ const returnType = `Stream.Stream<Uint8Array, HttpClientError.HttpClientError>`;
434
+ return `${jsdoc}${methodKey}: (${parameters}) => ${returnType}`;
435
+ };
436
+ const operationsToImpl = (_importName, name, operations) => {
437
+ const requirements = computeImportRequirements(operations);
438
+ const implMethods = [];
439
+ for (const op of operations) {
440
+ implMethods.push(operationToImpl(op));
441
+ if (op.sseSchema) {
442
+ implMethods.push(operationToSseImpl(op));
443
+ }
444
+ if (op.binaryResponse) {
445
+ implMethods.push(operationToBinaryImpl(op));
446
+ }
447
+ }
448
+ const helpers = [commonSource];
449
+ if (requirements.eventStream) {
450
+ helpers.push(sseRequestSourceTs);
451
+ }
452
+ if (requirements.octetStream) {
453
+ helpers.push(binaryRequestSourceTs);
454
+ }
455
+ return `export interface OperationConfig {
456
+ /**
457
+ * Whether or not the response should be included in the value returned from
458
+ * an operation.
459
+ *
460
+ * If set to \`true\`, a tuple of \`[A, HttpClientResponse]\` will be returned,
461
+ * where \`A\` is the success type of the operation.
462
+ *
463
+ * If set to \`false\`, only the success type of the operation will be returned.
464
+ */
465
+ readonly includeResponse?: boolean | undefined
466
+ }
467
+
468
+ /**
469
+ * A utility type which optionally includes the response in the return result
470
+ * of an operation based upon the value of the \`includeResponse\` configuration
471
+ * option.
472
+ */
473
+ export type WithOptionalResponse<A, Config extends OperationConfig> = Config extends {
474
+ readonly includeResponse: true
475
+ } ? [A, HttpClientResponse.HttpClientResponse] : A
476
+
477
+ export const make = (
478
+ httpClient: HttpClient.HttpClient,
479
+ options: {
480
+ readonly transformClient?: ((client: HttpClient.HttpClient) => Effect.Effect<HttpClient.HttpClient>) | undefined
481
+ } = {}
482
+ ): ${name} => {
483
+ ${helpers.join("\n ")}
484
+ const decodeSuccess = <A>(response: HttpClientResponse.HttpClientResponse) =>
485
+ response.json as Effect.Effect<A, HttpClientError.HttpClientError>
486
+ const decodeVoid = (_response: HttpClientResponse.HttpClientResponse) =>
487
+ Effect.void
488
+ const decodeError =
489
+ <Tag extends string, E>(tag: Tag) =>
490
+ (
491
+ response: HttpClientResponse.HttpClientResponse,
492
+ ): Effect.Effect<
493
+ never,
494
+ ${name}Error<Tag, E> | HttpClientError.HttpClientError
495
+ > =>
496
+ Effect.flatMap(
497
+ response.json as Effect.Effect<E, HttpClientError.HttpClientError>,
498
+ (cause) => Effect.fail(${name}Error(tag, cause, response)),
499
+ )
500
+ const onRequest = <Config extends OperationConfig>(config: Config | undefined) => (
501
+ successCodes: ReadonlyArray<string>,
502
+ errorCodes?: Record<string, string>,
503
+ ) => {
504
+ const cases: any = { orElse: unexpectedStatus }
505
+ for (const code of successCodes) {
506
+ cases[code] = decodeSuccess
507
+ }
508
+ if (errorCodes) {
509
+ for (const [code, tag] of Object.entries(errorCodes)) {
510
+ cases[code] = decodeError(tag)
511
+ }
512
+ }
513
+ if (successCodes.length === 0) {
514
+ cases["2xx"] = decodeVoid
515
+ }
516
+ return withResponse(config)(HttpClientResponse.matchStatus(cases) as any)
517
+ }
518
+ return {
519
+ httpClient,
520
+ ${implMethods.join(",\n ")}
521
+ }
522
+ }`;
523
+ };
524
+ const operationToImpl = operation => {
525
+ const args = [...operation.pathIds, "options"];
526
+ const params = `${args.join(", ")}`;
527
+ const pipeline = [];
528
+ if (operation.params) {
529
+ const paramsAccessor = resolveParamsAccessor(operation, "options", "params");
530
+ if (operation.urlParams.length > 0) {
531
+ const props = operation.urlParams.map(param => `"${param}": ${paramsAccessor}["${param}"] as any`);
532
+ pipeline.push(`HttpClientRequest.setUrlParams({ ${props.join(", ")} })`);
533
+ }
534
+ if (operation.headers.length > 0) {
535
+ const props = operation.headers.map(param => `"${param}": ${paramsAccessor}["${param}"] ?? undefined`);
536
+ pipeline.push(`HttpClientRequest.setHeaders({ ${props.join(", ")} })`);
537
+ }
538
+ }
539
+ const payloadAccessor = "options.payload";
540
+ if (operation.payloadFormData) {
541
+ pipeline.push(`HttpClientRequest.bodyFormDataRecord(${payloadAccessor} as any)`);
542
+ } else if (operation.payload) {
543
+ pipeline.push(`HttpClientRequest.bodyJsonUnsafe(${payloadAccessor})`);
544
+ }
545
+ const successCodesRaw = Array.from(operation.successSchemas.keys());
546
+ const successCodes = successCodesRaw.map(_ => JSON.stringify(_)).join(", ");
547
+ const singleSuccessCode = successCodesRaw.length === 1 && successCodesRaw[0].startsWith("2");
548
+ const errorCodes = operation.errorSchemas.size > 0 && Object.fromEntries(operation.errorSchemas.entries());
549
+ const configAccessor = resolveConfigAccessor(operation, "options", "config");
550
+ pipeline.push(`onRequest(${configAccessor})([${singleSuccessCode ? `"2xx"` : successCodes}]${errorCodes ? `, ${JSON.stringify(errorCodes)}` : ""})`);
551
+ return `"${operation.id}": (${params}) => ` + `HttpClientRequest.${httpClientMethodNames[operation.method]}(${operation.pathTemplate})` + `.pipe(\n ${pipeline.join(",\n ")}\n )`;
552
+ };
553
+ const operationToSseImpl = operation => {
554
+ const args = [...operation.pathIds];
555
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
556
+ if (hasOptions || operation.params || operation.payload) {
557
+ args.push("options");
558
+ }
559
+ const params = args.join(", ");
560
+ const pipeline = [];
561
+ if (operation.params) {
562
+ const paramsAccessor = resolveParamsAccessor(operation, "options", "params");
563
+ if (operation.urlParams.length > 0) {
564
+ const props = operation.urlParams.map(param => `"${param}": ${paramsAccessor}["${param}"] as any`);
565
+ pipeline.push(`HttpClientRequest.setUrlParams({ ${props.join(", ")} })`);
566
+ }
567
+ if (operation.headers.length > 0) {
568
+ const props = operation.headers.map(param => `"${param}": ${paramsAccessor}["${param}"] ?? undefined`);
569
+ pipeline.push(`HttpClientRequest.setHeaders({ ${props.join(", ")} })`);
570
+ }
571
+ }
572
+ if (operation.payloadFormData) {
573
+ pipeline.push(`HttpClientRequest.bodyFormDataRecord(options.payload as any)`);
574
+ } else if (operation.payload) {
575
+ pipeline.push(`HttpClientRequest.bodyJsonUnsafe(options.payload)`);
576
+ }
577
+ pipeline.push(`sseRequest`);
578
+ return `"${operation.id}Sse": (${params}) => ` + `HttpClientRequest.${httpClientMethodNames[operation.method]}(${operation.pathTemplate})` + `.pipe(\n ${pipeline.join(",\n ")}\n )`;
579
+ };
580
+ const operationToBinaryImpl = operation => {
581
+ const args = [...operation.pathIds];
582
+ const hasOptions = operation.params && !operation.paramsOptional || operation.payload;
583
+ if (hasOptions || operation.params || operation.payload) {
584
+ args.push("options");
585
+ }
586
+ const params = args.join(", ");
587
+ const pipeline = [];
588
+ if (operation.params) {
589
+ const paramsAccessor = resolveParamsAccessor(operation, "options", "params");
590
+ if (operation.urlParams.length > 0) {
591
+ const props = operation.urlParams.map(param => `"${param}": ${paramsAccessor}["${param}"] as any`);
592
+ pipeline.push(`HttpClientRequest.setUrlParams({ ${props.join(", ")} })`);
593
+ }
594
+ if (operation.headers.length > 0) {
595
+ const props = operation.headers.map(param => `"${param}": ${paramsAccessor}["${param}"] ?? undefined`);
596
+ pipeline.push(`HttpClientRequest.setHeaders({ ${props.join(", ")} })`);
597
+ }
598
+ }
599
+ if (operation.payloadFormData) {
600
+ pipeline.push(`HttpClientRequest.bodyFormDataRecord(options.payload as any)`);
601
+ } else if (operation.payload) {
602
+ pipeline.push(`HttpClientRequest.bodyJsonUnsafe(options.payload)`);
603
+ }
604
+ pipeline.push(`binaryRequest`);
605
+ return `"${operation.id}Stream": (${params}) => ` + `HttpClientRequest.${httpClientMethodNames[operation.method]}(${operation.pathTemplate})` + `.pipe(\n ${pipeline.join(",\n ")}\n )`;
606
+ };
607
+ return OpenApiTransformer.of({
608
+ imports: (_importName, operations) => {
609
+ const requirements = computeImportRequirements(operations);
610
+ const imports = [`import * as Data from "effect/Data"`, `import * as Effect from "effect/Effect"`];
611
+ if (requiresStreaming(requirements)) {
612
+ imports.push(`import * as Stream from "effect/Stream"`);
613
+ }
614
+ imports.push(`import type * as HttpClient from "effect/unstable/http/HttpClient"`, `import * as HttpClientError from "effect/unstable/http/HttpClientError"`, `import * as HttpClientRequest from "effect/unstable/http/HttpClientRequest"`, `import * as HttpClientResponse from "effect/unstable/http/HttpClientResponse"`);
615
+ return imports.join("\n");
616
+ },
617
+ toTypes: operationsToInterface,
618
+ toImplementation: operationsToImpl
619
+ });
620
+ };
621
+ export const layerTransformerTs = /*#__PURE__*/Layer.sync(OpenApiTransformer, makeTransformerTs);
622
+ const commonSource = `const unexpectedStatus = (response: HttpClientResponse.HttpClientResponse) =>
623
+ Effect.flatMap(
624
+ Effect.orElseSucceed(response.json, () => "Unexpected status code"),
625
+ (description) =>
626
+ Effect.fail(
627
+ new HttpClientError.HttpClientError({
628
+ reason: new HttpClientError.StatusCodeError({
629
+ request: response.request,
630
+ response,
631
+ description: typeof description === "string" ? description : JSON.stringify(description),
632
+ }),
633
+ }),
634
+ ),
635
+ )
636
+ const withResponse = <Config extends OperationConfig>(config: Config | undefined) => (
637
+ f: (response: HttpClientResponse.HttpClientResponse) => Effect.Effect<any, any>,
638
+ ): (request: HttpClientRequest.HttpClientRequest) => Effect.Effect<any, any> => {
639
+ const withOptionalResponse = (
640
+ config?.includeResponse
641
+ ? (response: HttpClientResponse.HttpClientResponse) => Effect.map(f(response), (a) => [a, response])
642
+ : (response: HttpClientResponse.HttpClientResponse) => f(response)
643
+ ) as any
644
+ return options?.transformClient
645
+ ? (request) =>
646
+ Effect.flatMap(
647
+ Effect.flatMap(options.transformClient!(httpClient), (client) => client.execute(request)),
648
+ withOptionalResponse
649
+ )
650
+ : (request) => Effect.flatMap(httpClient.execute(request), withOptionalResponse)
651
+ }`;
652
+ const sseRequestSource = _importName => `const sseRequest = <
653
+ Type,
654
+ DecodingServices
655
+ >(
656
+ schema: Schema.Decoder<Type, DecodingServices>
657
+ ) =>
658
+ (
659
+ request: HttpClientRequest.HttpClientRequest
660
+ ): Stream.Stream<
661
+ { readonly event: string; readonly id: string | undefined; readonly data: Type },
662
+ HttpClientError.HttpClientError | SchemaError | Sse.Retry,
663
+ DecodingServices
664
+ > =>
665
+ HttpClient.filterStatusOk(httpClient).execute(request).pipe(
666
+ Effect.map((response) => response.stream),
667
+ Stream.unwrap,
668
+ Stream.decodeText(),
669
+ Stream.pipeThroughChannel(Sse.decodeDataSchema(schema))
670
+ )`;
671
+ const binaryRequestSource = `const binaryRequest = (request: HttpClientRequest.HttpClientRequest): Stream.Stream<Uint8Array, HttpClientError.HttpClientError> =>
672
+ HttpClient.filterStatusOk(httpClient).execute(request).pipe(
673
+ Effect.map((response) => response.stream),
674
+ Stream.unwrap
675
+ )`;
676
+ // Type-only mode helpers (no schema decoding)
677
+ const sseRequestSourceTs = `const sseRequest = (request: HttpClientRequest.HttpClientRequest): Stream.Stream<unknown, HttpClientError.HttpClientError> =>
678
+ HttpClient.filterStatusOk(httpClient).execute(request).pipe(
679
+ Effect.map((response) => response.stream),
680
+ Stream.unwrap,
681
+ Stream.decodeText(),
682
+ Stream.splitLines,
683
+ Stream.filter((line) => line.startsWith("data: ")),
684
+ Stream.map((line) => JSON.parse(line.slice(6)))
685
+ )`;
686
+ const binaryRequestSourceTs = `const binaryRequest = (request: HttpClientRequest.HttpClientRequest): Stream.Stream<Uint8Array, HttpClientError.HttpClientError> =>
687
+ HttpClient.filterStatusOk(httpClient).execute(request).pipe(
688
+ Effect.map((response) => response.stream),
689
+ Stream.unwrap
690
+ )`;
691
+ const clientErrorSource = name => `export interface ${name}Error<Tag extends string, E> {
692
+ readonly _tag: Tag
693
+ readonly request: HttpClientRequest.HttpClientRequest
694
+ readonly response: HttpClientResponse.HttpClientResponse
695
+ readonly cause: E
696
+ }
697
+
698
+ class ${name}ErrorImpl extends Data.Error<{
699
+ _tag: string
700
+ cause: any
701
+ request: HttpClientRequest.HttpClientRequest
702
+ response: HttpClientResponse.HttpClientResponse
703
+ }> {}
704
+
705
+ export const ${name}Error = <Tag extends string, E>(
706
+ tag: Tag,
707
+ cause: E,
708
+ response: HttpClientResponse.HttpClientResponse,
709
+ ): ${name}Error<Tag, E> =>
710
+ new ${name}ErrorImpl({
711
+ _tag: tag,
712
+ cause,
713
+ response,
714
+ request: response.request,
715
+ }) as any`;
716
+ const resolveConfigAccessor = (operation, rootKey, configKey) => {
717
+ // If an operation payload is defined, then the root object must exist
718
+ if (Predicate.isNotUndefined(operation.payload)) {
719
+ return `${rootKey}.${configKey}`;
720
+ }
721
+ // If operation parameters are defined and non-optional, then the root object must exist
722
+ if (Predicate.isNotUndefined(operation.params) && !operation.paramsOptional) {
723
+ return `${rootKey}.${configKey}`;
724
+ }
725
+ // User-specified arguments are allowed but are not required, so the root object is optional
726
+ return `${rootKey}?.${configKey}`;
727
+ };
728
+ const resolveParamsAccessor = (operation, rootKey, paramsKey) => {
729
+ // If an operation payload is not defined and parameters are optional, then the
730
+ // root object may or may not exist and parameters must be marked as optional
731
+ if (Predicate.isUndefined(operation.payload) && operation.paramsOptional) {
732
+ return `${rootKey}?.${paramsKey}?.`;
733
+ }
734
+ // If parameters are optional, they must be marked as optional
735
+ if (operation.paramsOptional) {
736
+ return `${rootKey}.${paramsKey}?.`;
737
+ }
738
+ return `${rootKey}.${paramsKey}`;
739
+ };
740
+ //# sourceMappingURL=OpenApiTransformer.js.map