@blimu/codegen 0.4.1 → 0.5.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 CHANGED
@@ -55,19 +55,19 @@ Create `chunkflow-codegen.config.mjs` in your project root:
55
55
 
56
56
  ```javascript
57
57
  // @ts-check
58
- import { defineConfig } from "@blimu/codegen";
58
+ import { defineConfig } from '@blimu/codegen';
59
59
 
60
60
  export default defineConfig({
61
- spec: "http://localhost:3020/docs/backend-api/json",
61
+ spec: 'http://localhost:3020/docs/backend-api/json',
62
62
  clients: [
63
63
  {
64
- type: "typescript",
65
- outDir: "./my-sdk",
66
- packageName: "my-sdk",
67
- name: "MyClient",
64
+ type: 'typescript',
65
+ outDir: './my-sdk',
66
+ packageName: 'my-sdk',
67
+ name: 'MyClient',
68
68
  operationIdParser: (operationId, method, path) => {
69
69
  // Custom transform logic
70
- return operationId.replace(/Controller/g, "");
70
+ return operationId.replace(/Controller/g, '');
71
71
  },
72
72
  },
73
73
  ],
@@ -83,32 +83,32 @@ See `examples/chunkflow-codegen.config.mjs.example` for a complete example with
83
83
  Use the codegen library programmatically in your TypeScript code:
84
84
 
85
85
  ```typescript
86
- import { generate, loadConfig, defineConfig } from "@blimu/codegen";
86
+ import { generate, loadConfig, defineConfig } from '@blimu/codegen';
87
87
 
88
88
  // Generate from config object
89
89
  await generate({
90
- spec: "http://localhost:3020/docs/backend-api/json",
90
+ spec: 'http://localhost:3020/docs/backend-api/json',
91
91
  clients: [
92
92
  {
93
- type: "typescript",
94
- outDir: "./my-sdk",
95
- packageName: "my-sdk",
96
- name: "MyClient",
93
+ type: 'typescript',
94
+ outDir: './my-sdk',
95
+ packageName: 'my-sdk',
96
+ name: 'MyClient',
97
97
  operationIdParser: (operationId, method, path) => {
98
- return operationId.replace(/Controller/g, "");
98
+ return operationId.replace(/Controller/g, '');
99
99
  },
100
100
  },
101
101
  ],
102
102
  });
103
103
 
104
104
  // Generate from config file path
105
- await generate("./chunkflow-codegen.config.mjs");
105
+ await generate('./chunkflow-codegen.config.mjs');
106
106
 
107
107
  // Generate only a specific client
108
- await generate("./chunkflow-codegen.config.mjs", { client: "MyClient" });
108
+ await generate('./chunkflow-codegen.config.mjs', { client: 'MyClient' });
109
109
 
110
110
  // Load config programmatically
111
- const config = await loadConfig("./chunkflow-codegen.config.mjs");
111
+ const config = await loadConfig('./chunkflow-codegen.config.mjs');
112
112
  ```
113
113
 
114
114
  ## Configuration Options
@@ -2,7 +2,7 @@ import type { AuthStrategy } from '@blimu/fetch';
2
2
  import type { ClientOption } from './client';
3
3
 
4
4
  export function buildAuthStrategies(cfg: ClientOption): AuthStrategy[] {
5
- const authStrategies: AuthStrategy[] = [...(cfg?.authStrategies || [])];
5
+ const authStrategies: AuthStrategy[] = [...(cfg?.authStrategies ?? [])];
6
6
 
7
7
  {{#each IR.securitySchemes~}}
8
8
  {{~#if (eq this.type "http")~}}
@@ -52,12 +52,12 @@ export class {{Client.name}} {
52
52
  {{/each}}
53
53
 
54
54
  constructor(options?: ClientOption) {
55
- const restCfg = { ...(options || {}) };
55
+ const restCfg = { ...(options ?? {}) };
56
56
  {{#each IR.securitySchemes}}
57
57
  delete restCfg.{{camel this.key}};
58
58
  {{/each}}
59
59
 
60
- const authStrategies = buildAuthStrategies(options || {});
60
+ const authStrategies = buildAuthStrategies(options ?? {});
61
61
 
62
62
  const core = new FetchClient({
63
63
  ...restCfg,
@@ -2,7 +2,8 @@
2
2
  "name": "{{Client.packageName}}",
3
3
  "version": "0.1.0",
4
4
  "description": "TypeScript SDK for {{Client.name}} API (auto-generated)",
5
- "main": "dist/index.js",
5
+ "main": "dist/index.cjs",
6
+ "type": "module",
6
7
  "module": "dist/index.mjs",
7
8
  "types": "dist/index.d.ts",
8
9
  "files": ["dist/**"],
@@ -10,40 +11,40 @@
10
11
  ".": {
11
12
  "types": "./dist/index.d.ts",
12
13
  "import": "./dist/index.mjs",
13
- "require": "./dist/index.js",
14
- "default": "./dist/index.js"
14
+ "require": "./dist/index.cjs",
15
+ "default": "./dist/index.cjs"
15
16
  },
16
17
  "./services/*": {
17
18
  "types": "./dist/services/*.d.ts",
18
19
  "import": "./dist/services/*.mjs",
19
- "require": "./dist/services/*.js",
20
- "default": "./dist/services/*.js"
20
+ "require": "./dist/services/*.cjs",
21
+ "default": "./dist/services/*.cjs"
21
22
  },
22
23
  "./schema": {
23
24
  "types": "./dist/schema.d.ts",
24
25
  "import": "./dist/schema.mjs",
25
- "require": "./dist/schema.js",
26
- "default": "./dist/schema.js"
26
+ "require": "./dist/schema.cjs",
27
+ "default": "./dist/schema.cjs"
27
28
  },
28
29
  "./client": {
29
30
  "types": "./dist/client.d.ts",
30
31
  "import": "./dist/client.mjs",
31
- "require": "./dist/client.js",
32
- "default": "./dist/client.js"
32
+ "require": "./dist/client.cjs",
33
+ "default": "./dist/client.cjs"
33
34
  },
34
35
  "./utils": {
35
36
  "types": "./dist/utils.d.ts",
36
37
  "import": "./dist/utils.mjs",
37
- "require": "./dist/utils.js",
38
- "default": "./dist/utils.js"
38
+ "require": "./dist/utils.cjs",
39
+ "default": "./dist/utils.cjs"
39
40
  }
40
41
  },
41
42
  "scripts": {
42
43
  "build": "tsup",
43
44
  "typecheck": "tsc -p tsconfig.json --noEmit",
44
45
  "lint": "eslint .",
45
- "format": "eslint --fix . && prettier --write .",
46
- "prepublishOnly": "npm run build && npm run typecheck || true"
46
+ "format": "eslint --fix .",
47
+ "prepublishOnly": "npm run build && npm run typecheck"
47
48
  },
48
49
  "dependencies": {
49
50
  {{#each (getAllDependencies Client)}}
@@ -1,10 +1,12 @@
1
- import { FetchClient } from '@blimu/fetch';
1
+ import type { FetchClient } from '@blimu/fetch';
2
2
  {{#if (serviceUsesSchema Service)}}
3
- import * as Schema from "../schema";
3
+ import type * as Schema from "../schema";
4
4
  {{/if}}
5
5
  {{#if Client.includeQueryKeys}}
6
+ {{#if (hasAnyOptionalQueryKeyParams Service)}}
6
7
  import { isNotUndefined } from "../utils";
7
8
  {{/if}}
9
+ {{/if}}
8
10
  {{! Generate import statements for predefined types used in this specific service }}
9
11
  {{#if (getServicePredefinedTypes Service)}}
10
12
  {{#each (groupByPackage (getServicePredefinedTypes Service))}}
@@ -47,7 +49,7 @@ export class {{serviceName Service.tag}} {
47
49
  {{/if}}
48
50
  contentType: "{{response.contentType}}",
49
51
  streamingFormat: "{{response.streamingFormat}}",
50
- ...(init || {}),
52
+ ...(init ?? {}),
51
53
  });
52
54
  }
53
55
  {{else}}
@@ -65,7 +67,7 @@ export class {{serviceName Service.tag}} {
65
67
  {{#if requestBody}}
66
68
  body,
67
69
  {{/if}}
68
- ...(init || {}),
70
+ ...(init ?? {}),
69
71
  });
70
72
  }
71
73
  {{/if}}
@@ -78,9 +80,13 @@ export class {{serviceName Service.tag}} {
78
80
  {{~#each (methodSignatureNoInit this) as |param|~}}
79
81
  {{param}}{{#unless @last}},{{/unless}}
80
82
  {{~/each~}}
81
- ) {
83
+ ): {{#if (hasOptionalQueryKeyParams this)}}readonly string[]{{else}}{{queryKeyReturnType this}}{{/if}} {
84
+ {{#if (hasOptionalQueryKeyParams this)}}
82
85
  const keys = [{{queryKeyBase this}}{{#each (queryKeyArgs this)}}, {{this}}{{/each}}] as const;
83
- return isNotUndefined(keys);
86
+ return isNotUndefined(keys) as readonly string[];
87
+ {{else}}
88
+ return [{{queryKeyBase this}}{{#each (queryKeyArgs this)}}, {{this}}{{/each}}] as const;
89
+ {{/if}}
84
90
  }
85
91
  {{/if}}
86
92
  {{/each}}
@@ -21,7 +21,8 @@
21
21
  "noFallthroughCasesInSwitch": true,
22
22
  "noUnusedLocals": true,
23
23
  "noUnusedParameters": true,
24
- "useDefineForClassFields": true
24
+ "useDefineForClassFields": true,
25
+ "verbatimModuleSyntax": true
25
26
  },
26
27
  "include": ["{{Client.srcDir}}/**/*"],
27
28
  "exclude": ["node_modules", "dist"]
@@ -1,7 +1,13 @@
1
1
  import { defineConfig } from "tsup";
2
2
 
3
3
  export default defineConfig({
4
- entry: ["{{Client.srcDir}}/**/*.ts"],
4
+ entry: [
5
+ "{{Client.srcDir}}/index.ts",
6
+ "{{Client.srcDir}}/services/*.ts",
7
+ "{{Client.srcDir}}/schema.ts",
8
+ "{{Client.srcDir}}/client.ts",
9
+ "{{Client.srcDir}}/utils.ts",
10
+ ],
5
11
  format: ["cjs", "esm"],
6
12
  dts: true,
7
13
  splitting: false,
@@ -9,6 +15,9 @@ export default defineConfig({
9
15
  clean: true,
10
16
  outDir: "dist",
11
17
  tsconfig: "./tsconfig.json",
18
+ outExtension({ format }) {
19
+ return format === 'esm' ? { js: '.mjs' } : { js: '.cjs' };
20
+ },
12
21
  // External dependencies should not be bundled
13
22
  // This ensures proper type resolution and smaller bundle sizes
14
23
  external: [],
@@ -3,19 +3,19 @@ import { parseSSEStream, parseNDJSONStream } from "@blimu/fetch";
3
3
  export type PaginableQuery = { limit?: number; offset?: number } & Record<string, unknown>;
4
4
 
5
5
  export async function* paginate<T>(
6
- fetchPage: (query?: any, init?: Omit<RequestInit, 'method' | 'body'>) => Promise<{ data?: T[]; hasMore?: boolean; limit?: number; offset?: number }>,
6
+ fetchPage: (query?: PaginableQuery, init?: Omit<RequestInit, 'method' | 'body'>) => Promise<{ data?: T[]; hasMore?: boolean; limit?: number; offset?: number }>,
7
7
  initialQuery: PaginableQuery = {},
8
8
  pageSize = 100,
9
9
  ): AsyncGenerator<T, void, unknown> {
10
10
  let offset = Number(initialQuery.offset ?? 0);
11
11
  const limit = Number(initialQuery.limit ?? pageSize);
12
12
  // shallow copy to avoid mutating caller
13
- const baseQuery: any = { ...initialQuery };
13
+ const baseQuery: PaginableQuery = { ...initialQuery };
14
14
  while (true) {
15
15
  const page = await fetchPage({ ...baseQuery, limit, offset });
16
16
  const items = page.data ?? [];
17
17
  for (const item of items) {
18
- yield item as T;
18
+ yield item;
19
19
  }
20
20
  if (!page.hasMore || items.length < limit) break;
21
21
  offset += limit;
@@ -23,7 +23,7 @@ export async function* paginate<T>(
23
23
  }
24
24
 
25
25
  export async function listAll<T>(
26
- fetchPage: (query?: any, init?: Omit<RequestInit, 'method' | 'body'>) => Promise<{ data?: T[]; hasMore?: boolean; limit?: number; offset?: number }>,
26
+ fetchPage: (query?: PaginableQuery, init?: Omit<RequestInit, 'method' | 'body'>) => Promise<{ data?: T[]; hasMore?: boolean; limit?: number; offset?: number }>,
27
27
  query: PaginableQuery = {},
28
28
  pageSize = 100,
29
29
  ): Promise<T[]> {
@@ -35,9 +35,14 @@ export async function listAll<T>(
35
35
  /**
36
36
  * Filters out undefined values from an array while preserving type safety
37
37
  * Useful for query keys where optional parameters might be undefined
38
+ * Preserves tuple types when no undefined values are present
38
39
  */
39
- export function isNotUndefined<T>(arr: readonly (T | undefined)[]): T[] {
40
- return arr.filter((item): item is T => item !== undefined);
40
+ export function isNotUndefined<T extends readonly unknown[]>(arr: T): T {
41
+ // Type assertion: when no undefined values exist, tuple is preserved
42
+ // When undefined values exist, they are filtered but type system can't infer exact tuple shape
43
+ return arr.filter(
44
+ (item): item is Exclude<T[number], undefined> => item !== undefined
45
+ ) as unknown as T;
41
46
  }
42
47
 
43
48
  // Re-export streaming parsers from @blimu/fetch
package/dist/index.d.mts CHANGED
@@ -151,7 +151,7 @@ interface IR {
151
151
  models: IRModel[];
152
152
  securitySchemes: IRSecurityScheme[];
153
153
  modelDefs: IRModelDef[];
154
- openApiDocument?: any;
154
+ openApiDocument?: unknown;
155
155
  }
156
156
  interface IRParam {
157
157
  name: string;
@@ -171,7 +171,7 @@ interface IRResponse {
171
171
  description: string;
172
172
  isStreaming: boolean;
173
173
  contentType: string;
174
- streamingFormat?: "sse" | "ndjson" | "chunked";
174
+ streamingFormat?: 'sse' | 'ndjson' | 'chunked' | undefined;
175
175
  }
176
176
  interface IRModel {
177
177
  name: string;
@@ -183,30 +183,30 @@ interface IRModelDef {
183
183
  annotations: IRAnnotations;
184
184
  }
185
185
  interface IRAnnotations {
186
- title?: string;
187
- description?: string;
188
- deprecated?: boolean;
189
- readOnly?: boolean;
190
- writeOnly?: boolean;
191
- default?: any;
192
- examples?: any[];
186
+ title?: string | undefined;
187
+ description?: string | undefined;
188
+ deprecated?: boolean | undefined;
189
+ readOnly?: boolean | undefined;
190
+ writeOnly?: boolean | undefined;
191
+ default?: unknown | undefined;
192
+ examples?: unknown[] | undefined;
193
193
  }
194
194
  interface IRSchema {
195
195
  kind: IRSchemaKind;
196
196
  nullable: boolean;
197
- format?: string;
198
- properties?: IRField[];
199
- additionalProperties?: IRSchema;
200
- items?: IRSchema;
201
- enumValues?: string[];
202
- enumRaw?: any[];
203
- enumBase?: IRSchemaKind;
197
+ format?: string | undefined;
198
+ properties?: IRField[] | undefined;
199
+ additionalProperties?: IRSchema | undefined;
200
+ items?: IRSchema | undefined;
201
+ enumValues?: string[] | undefined;
202
+ enumRaw?: unknown[] | undefined;
203
+ enumBase?: IRSchemaKind | undefined;
204
204
  ref?: string;
205
- oneOf?: IRSchema[];
206
- anyOf?: IRSchema[];
207
- allOf?: IRSchema[];
208
- not?: IRSchema;
209
- discriminator?: IRDiscriminator;
205
+ oneOf?: IRSchema[] | undefined;
206
+ anyOf?: IRSchema[] | undefined;
207
+ allOf?: IRSchema[] | undefined;
208
+ not?: IRSchema | undefined;
209
+ discriminator?: IRDiscriminator | undefined;
210
210
  }
211
211
  interface IRField {
212
212
  name: string;
@@ -216,15 +216,15 @@ interface IRField {
216
216
  }
217
217
  interface IRDiscriminator {
218
218
  propertyName: string;
219
- mapping?: Record<string, string>;
219
+ mapping?: Record<string, string> | undefined;
220
220
  }
221
221
  interface IRSecurityScheme {
222
222
  key: string;
223
223
  type: string;
224
224
  scheme?: string;
225
- in?: string;
226
- name?: string;
227
- bearerFormat?: string;
225
+ in?: string | undefined;
226
+ name?: string | undefined;
227
+ bearerFormat?: string | undefined;
228
228
  }
229
229
 
230
230
  interface Generator<TClient extends Client = Client> {
package/dist/index.d.ts CHANGED
@@ -151,7 +151,7 @@ interface IR {
151
151
  models: IRModel[];
152
152
  securitySchemes: IRSecurityScheme[];
153
153
  modelDefs: IRModelDef[];
154
- openApiDocument?: any;
154
+ openApiDocument?: unknown;
155
155
  }
156
156
  interface IRParam {
157
157
  name: string;
@@ -171,7 +171,7 @@ interface IRResponse {
171
171
  description: string;
172
172
  isStreaming: boolean;
173
173
  contentType: string;
174
- streamingFormat?: "sse" | "ndjson" | "chunked";
174
+ streamingFormat?: 'sse' | 'ndjson' | 'chunked' | undefined;
175
175
  }
176
176
  interface IRModel {
177
177
  name: string;
@@ -183,30 +183,30 @@ interface IRModelDef {
183
183
  annotations: IRAnnotations;
184
184
  }
185
185
  interface IRAnnotations {
186
- title?: string;
187
- description?: string;
188
- deprecated?: boolean;
189
- readOnly?: boolean;
190
- writeOnly?: boolean;
191
- default?: any;
192
- examples?: any[];
186
+ title?: string | undefined;
187
+ description?: string | undefined;
188
+ deprecated?: boolean | undefined;
189
+ readOnly?: boolean | undefined;
190
+ writeOnly?: boolean | undefined;
191
+ default?: unknown | undefined;
192
+ examples?: unknown[] | undefined;
193
193
  }
194
194
  interface IRSchema {
195
195
  kind: IRSchemaKind;
196
196
  nullable: boolean;
197
- format?: string;
198
- properties?: IRField[];
199
- additionalProperties?: IRSchema;
200
- items?: IRSchema;
201
- enumValues?: string[];
202
- enumRaw?: any[];
203
- enumBase?: IRSchemaKind;
197
+ format?: string | undefined;
198
+ properties?: IRField[] | undefined;
199
+ additionalProperties?: IRSchema | undefined;
200
+ items?: IRSchema | undefined;
201
+ enumValues?: string[] | undefined;
202
+ enumRaw?: unknown[] | undefined;
203
+ enumBase?: IRSchemaKind | undefined;
204
204
  ref?: string;
205
- oneOf?: IRSchema[];
206
- anyOf?: IRSchema[];
207
- allOf?: IRSchema[];
208
- not?: IRSchema;
209
- discriminator?: IRDiscriminator;
205
+ oneOf?: IRSchema[] | undefined;
206
+ anyOf?: IRSchema[] | undefined;
207
+ allOf?: IRSchema[] | undefined;
208
+ not?: IRSchema | undefined;
209
+ discriminator?: IRDiscriminator | undefined;
210
210
  }
211
211
  interface IRField {
212
212
  name: string;
@@ -216,15 +216,15 @@ interface IRField {
216
216
  }
217
217
  interface IRDiscriminator {
218
218
  propertyName: string;
219
- mapping?: Record<string, string>;
219
+ mapping?: Record<string, string> | undefined;
220
220
  }
221
221
  interface IRSecurityScheme {
222
222
  key: string;
223
223
  type: string;
224
224
  scheme?: string;
225
- in?: string;
226
- name?: string;
227
- bearerFormat?: string;
225
+ in?: string | undefined;
226
+ name?: string | undefined;
227
+ bearerFormat?: string | undefined;
228
228
  }
229
229
 
230
230
  interface Generator<TClient extends Client = Client> {