apigen-ts 1.4.0 → 1.5.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.
package/dist/_template.ts CHANGED
@@ -54,11 +54,24 @@ export class ApiClient {
54
54
  return new URL(path, base)
55
55
  }
56
56
 
57
+ // same as "${v}" in path template, but keeps "/", "?", "#" inside the path segment
58
+ EncodePathParam(v: unknown) {
59
+ return encodeURIComponent(String(v))
60
+ }
61
+
62
+ // query params with explode: false, eg. Swagger 2 `collectionFormat: csv`
63
+ JoinQueryParam(v: unknown, sep: string) {
64
+ return Array.isArray(v) ? v.join(sep) : v
65
+ }
66
+
57
67
  async Fetch<T>(method: string, path: string, opts: ApigenRequest = {}): Promise<T> {
58
68
  const url = this.PrepareFetchUrl(path)
59
69
 
70
+ // skip omitted values; arrays repeat the key per item (OpenAPI default: form + explode)
60
71
  for (const [k, v] of Object.entries(opts?.search ?? {})) {
61
- url.searchParams.append(k, Array.isArray(v) ? v.join(",") : (v as string))
72
+ for (const x of Array.isArray(v) ? v : [v]) {
73
+ if (x !== undefined && x !== null) url.searchParams.append(k, x as string)
74
+ }
62
75
  }
63
76
 
64
77
  const configHeaders =
@@ -76,7 +89,7 @@ export class ApiClient {
76
89
  headers.delete("content-type")
77
90
  body = ct === "multipart/form-data" ? new FormData() : new URLSearchParams()
78
91
  for (const [k, v] of Object.entries(opts.body as Record<string, string>)) {
79
- body.append(k, v)
92
+ if (v !== undefined && v !== null) body.append(k, v)
80
93
  }
81
94
  }
82
95
 
package/dist/cli.js CHANGED
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- import { a as apigen, g as getCliConfig } from './main-BkWtFDlL.js';
2
+ import { a as apigen, g as getCliConfig } from './main-Cn0HtcUt.js';
3
3
  import 'fs/promises';
4
4
  import 'path';
5
5
  import 'url';
@@ -10,7 +10,7 @@ import ts from 'typescript';
10
10
  import path from 'node:path';
11
11
 
12
12
  var name = "apigen-ts";
13
- var version = "1.4.0";
13
+ var version = "1.5.0";
14
14
 
15
15
  const initCtx = (config) => {
16
16
  return {
@@ -455,13 +455,31 @@ const prepareUrl = (url, rename) => {
455
455
  const [name, ...rest] = x.split("}");
456
456
  const right = rest.join("}");
457
457
  return f$1.createTemplateSpan(
458
- f$1.createIdentifier(name),
459
- // no normalization required
458
+ f$1.createCallExpression(f$1.createIdentifier("this.EncodePathParam"), void 0, [
459
+ f$1.createIdentifier(name)
460
+ // no normalization required
461
+ ]),
460
462
  i === parts.length - 2 ? f$1.createTemplateTail(right) : f$1.createTemplateMiddle(right)
461
463
  );
462
464
  })
463
465
  );
464
466
  };
467
+ const prepareSearch = (params) => {
468
+ const separators = { form: ",", spaceDelimited: " ", pipeDelimited: "|" };
469
+ const joined = params.flatMap((x) => {
470
+ const style = x.style ?? "form";
471
+ if ((x.explode ?? style === "form") || !(style in separators)) return [];
472
+ const key = /^[a-zA-Z_$][\w$]*$/.test(x.name) ? f$1.createPropertyAccessExpression(f$1.createIdentifier("search"), x.name) : f$1.createElementAccessExpression(f$1.createIdentifier("search"), f$1.createStringLiteral(x.name));
473
+ const value = f$1.createCallExpression(f$1.createIdentifier("this.JoinQueryParam"), void 0, [
474
+ key,
475
+ f$1.createStringLiteral(separators[style])
476
+ ]);
477
+ return [f$1.createPropertyAssignment(f$1.createStringLiteral(x.name), value)];
478
+ });
479
+ if (!joined.length) return f$1.createShorthandPropertyAssignment("search");
480
+ const search = [f$1.createSpreadAssignment(f$1.createIdentifier("search")), ...joined];
481
+ return f$1.createPropertyAssignment("search", f$1.createObjectLiteralExpression(search));
482
+ };
465
483
  const prepareOp = (ctx, cfg, opName, httpMethod) => {
466
484
  cfg.parameters = cfg.parameters ?? [];
467
485
  const reqSchema = getReqSchema(ctx, cfg);
@@ -469,7 +487,7 @@ const prepareOp = (ctx, cfg, opName, httpMethod) => {
469
487
  const allParams = filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
470
488
  const params = uniqBy(allParams.filter((x) => x.in === "path"), "name");
471
489
  if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
472
- const search = allParams.filter((x) => x.in === "query");
490
+ const search = uniqBy(allParams.filter((x) => x.in === "query"), "name");
473
491
  allParams.filter((x) => x.in === "header");
474
492
  for (const [name, v] of Object.entries({ search })) {
475
493
  if (!v.length) continue;
@@ -496,7 +514,7 @@ const prepareOp = (ctx, cfg, opName, httpMethod) => {
496
514
  }
497
515
  const cbArgs = filterNullable([
498
516
  ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
499
- search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
517
+ search.length ? prepareSearch(search) : void 0,
500
518
  reqSchema && f$1.createShorthandPropertyAssignment("body"),
501
519
  reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
502
520
  "headers",
package/dist/main.cjs CHANGED
@@ -378,13 +378,31 @@ const prepareUrl = (url, rename) => {
378
378
  const [name, ...rest] = x.split("}");
379
379
  const right = rest.join("}");
380
380
  return f$1.createTemplateSpan(
381
- f$1.createIdentifier(name),
382
- // no normalization required
381
+ f$1.createCallExpression(f$1.createIdentifier("this.EncodePathParam"), void 0, [
382
+ f$1.createIdentifier(name)
383
+ // no normalization required
384
+ ]),
383
385
  i === parts.length - 2 ? f$1.createTemplateTail(right) : f$1.createTemplateMiddle(right)
384
386
  );
385
387
  })
386
388
  );
387
389
  };
390
+ const prepareSearch = (params) => {
391
+ const separators = { form: ",", spaceDelimited: " ", pipeDelimited: "|" };
392
+ const joined = params.flatMap((x) => {
393
+ const style = x.style ?? "form";
394
+ if ((x.explode ?? style === "form") || !(style in separators)) return [];
395
+ const key = /^[a-zA-Z_$][\w$]*$/.test(x.name) ? f$1.createPropertyAccessExpression(f$1.createIdentifier("search"), x.name) : f$1.createElementAccessExpression(f$1.createIdentifier("search"), f$1.createStringLiteral(x.name));
396
+ const value = f$1.createCallExpression(f$1.createIdentifier("this.JoinQueryParam"), void 0, [
397
+ key,
398
+ f$1.createStringLiteral(separators[style])
399
+ ]);
400
+ return [f$1.createPropertyAssignment(f$1.createStringLiteral(x.name), value)];
401
+ });
402
+ if (!joined.length) return f$1.createShorthandPropertyAssignment("search");
403
+ const search = [f$1.createSpreadAssignment(f$1.createIdentifier("search")), ...joined];
404
+ return f$1.createPropertyAssignment("search", f$1.createObjectLiteralExpression(search));
405
+ };
388
406
  const prepareOp = (ctx, cfg, opName, httpMethod) => {
389
407
  cfg.parameters = cfg.parameters ?? [];
390
408
  const reqSchema = getReqSchema(ctx, cfg);
@@ -392,7 +410,7 @@ const prepareOp = (ctx, cfg, opName, httpMethod) => {
392
410
  const allParams = arrayUtilsTs.filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
393
411
  const params = lodashEs.uniqBy(allParams.filter((x) => x.in === "path"), "name");
394
412
  if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
395
- const search = allParams.filter((x) => x.in === "query");
413
+ const search = lodashEs.uniqBy(allParams.filter((x) => x.in === "query"), "name");
396
414
  allParams.filter((x) => x.in === "header");
397
415
  for (const [name, v] of Object.entries({ search })) {
398
416
  if (!v.length) continue;
@@ -419,7 +437,7 @@ const prepareOp = (ctx, cfg, opName, httpMethod) => {
419
437
  }
420
438
  const cbArgs = arrayUtilsTs.filterNullable([
421
439
  ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
422
- search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
440
+ search.length ? prepareSearch(search) : void 0,
423
441
  reqSchema && f$1.createShorthandPropertyAssignment("body"),
424
442
  reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
425
443
  "headers",
package/dist/main.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import 'fs/promises';
2
2
  import 'path';
3
3
  import 'url';
4
- export { a as apigen } from './main-BkWtFDlL.js';
4
+ export { a as apigen } from './main-Cn0HtcUt.js';
5
5
  import 'cleye';
6
6
  import '@redocly/openapi-core';
7
7
  import 'array-utils-ts';
package/dist/main.mjs CHANGED
@@ -375,13 +375,31 @@ const prepareUrl = (url, rename) => {
375
375
  const [name, ...rest] = x.split("}");
376
376
  const right = rest.join("}");
377
377
  return f$1.createTemplateSpan(
378
- f$1.createIdentifier(name),
379
- // no normalization required
378
+ f$1.createCallExpression(f$1.createIdentifier("this.EncodePathParam"), void 0, [
379
+ f$1.createIdentifier(name)
380
+ // no normalization required
381
+ ]),
380
382
  i === parts.length - 2 ? f$1.createTemplateTail(right) : f$1.createTemplateMiddle(right)
381
383
  );
382
384
  })
383
385
  );
384
386
  };
387
+ const prepareSearch = (params) => {
388
+ const separators = { form: ",", spaceDelimited: " ", pipeDelimited: "|" };
389
+ const joined = params.flatMap((x) => {
390
+ const style = x.style ?? "form";
391
+ if ((x.explode ?? style === "form") || !(style in separators)) return [];
392
+ const key = /^[a-zA-Z_$][\w$]*$/.test(x.name) ? f$1.createPropertyAccessExpression(f$1.createIdentifier("search"), x.name) : f$1.createElementAccessExpression(f$1.createIdentifier("search"), f$1.createStringLiteral(x.name));
393
+ const value = f$1.createCallExpression(f$1.createIdentifier("this.JoinQueryParam"), void 0, [
394
+ key,
395
+ f$1.createStringLiteral(separators[style])
396
+ ]);
397
+ return [f$1.createPropertyAssignment(f$1.createStringLiteral(x.name), value)];
398
+ });
399
+ if (!joined.length) return f$1.createShorthandPropertyAssignment("search");
400
+ const search = [f$1.createSpreadAssignment(f$1.createIdentifier("search")), ...joined];
401
+ return f$1.createPropertyAssignment("search", f$1.createObjectLiteralExpression(search));
402
+ };
385
403
  const prepareOp = (ctx, cfg, opName, httpMethod) => {
386
404
  cfg.parameters = cfg.parameters ?? [];
387
405
  const reqSchema = getReqSchema(ctx, cfg);
@@ -389,7 +407,7 @@ const prepareOp = (ctx, cfg, opName, httpMethod) => {
389
407
  const allParams = filterNullable(cfg.parameters.map((x) => unref(ctx, x)));
390
408
  const params = uniqBy(allParams.filter((x) => x.in === "path"), "name");
391
409
  if (reqSchema) params.push({ name: "body", schema: reqSchema[1] });
392
- const search = allParams.filter((x) => x.in === "query");
410
+ const search = uniqBy(allParams.filter((x) => x.in === "query"), "name");
393
411
  allParams.filter((x) => x.in === "header");
394
412
  for (const [name, v] of Object.entries({ search })) {
395
413
  if (!v.length) continue;
@@ -416,7 +434,7 @@ const prepareOp = (ctx, cfg, opName, httpMethod) => {
416
434
  }
417
435
  const cbArgs = filterNullable([
418
436
  ctx.fetchOptions ? f$1.createSpreadAssignment(f$1.createIdentifier("opts")) : void 0,
419
- search.length ? f$1.createShorthandPropertyAssignment("search") : void 0,
437
+ search.length ? prepareSearch(search) : void 0,
420
438
  reqSchema && f$1.createShorthandPropertyAssignment("body"),
421
439
  reqSchema && reqSchema[0] !== "application/json" ? f$1.createPropertyAssignment(
422
440
  "headers",
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "apigen-ts",
4
- "version": "1.4.0",
4
+ "version": "1.5.0",
5
5
  "license": "MIT",
6
6
  "author": "vladkens <v.pronsky@gmail.com>",
7
7
  "repository": {
@@ -21,22 +21,22 @@
21
21
  "node": ">=22"
22
22
  },
23
23
  "dependencies": {
24
- "@redocly/openapi-core": "2.47.0",
24
+ "@redocly/openapi-core": "2.76.0",
25
25
  "@types/lodash-es": "4.17.12",
26
26
  "@types/swagger2openapi": "7.0.4",
27
27
  "array-utils-ts": "1.1.0",
28
- "cleye": "2.6.0",
28
+ "cleye": "2.7.0",
29
29
  "lodash-es": "4.18.1",
30
30
  "swagger2openapi": "7.0.8"
31
31
  },
32
32
  "devDependencies": {
33
- "@types/node": "26.2.0",
33
+ "@types/node": "26.6.6",
34
34
  "fetch-mock": "12.6.0",
35
- "npm-check-updates": "23.0.2",
36
- "pkgroll": "2.27.1",
37
- "prettier": "3.9.6",
35
+ "npm-check-updates": "23.1.1",
36
+ "pkgroll": "2.29.0",
37
+ "prettier": "3.9.10",
38
38
  "prettier-plugin-organize-imports": "4.3.0",
39
- "tsx": "4.23.12",
39
+ "tsx": "4.23.15",
40
40
  "typescript": "6.0.3"
41
41
  },
42
42
  "peerDependencies": {