@pikku/openapi-parser 0.12.6 → 0.12.8
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/CHANGELOG.md +12 -0
- package/dist/codegen.d.ts +1 -0
- package/dist/codegen.js +45 -3
- package/package.json +1 -1
- package/src/codegen.ts +51 -3
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# @pikku/openapi-parser
|
|
2
2
|
|
|
3
|
+
## 0.12.8
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 94ceecd: Fix duplicate property error in generated code when body and path/query/header params share the same name. Skips the body property with a warning.
|
|
8
|
+
|
|
9
|
+
## 0.12.7
|
|
10
|
+
|
|
11
|
+
### Patch Changes
|
|
12
|
+
|
|
13
|
+
- 5dd1996: Fix credentials command crash when state.credentials is undefined, and add --credential flag to `pikku new addon` for per-user credential wiring (apikey, bearer, oauth2).
|
|
14
|
+
|
|
3
15
|
## 0.12.6
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/codegen.d.ts
CHANGED
|
@@ -14,6 +14,7 @@ interface AddonVars {
|
|
|
14
14
|
interface CodegenFlags {
|
|
15
15
|
oauth: boolean;
|
|
16
16
|
secret: boolean;
|
|
17
|
+
credential?: 'apikey' | 'bearer' | 'oauth2';
|
|
17
18
|
mcp?: boolean;
|
|
18
19
|
}
|
|
19
20
|
export declare function generateAddonFromOpenAPI(spec: ParsedSpec, vars: AddonVars, flags: CodegenFlags): Record<string, string>;
|
package/dist/codegen.js
CHANGED
|
@@ -179,11 +179,21 @@ function buildInputSchema(parsed, ctx) {
|
|
|
179
179
|
}
|
|
180
180
|
if (parsed.requestBody) {
|
|
181
181
|
if (parsed.requestBody.properties) {
|
|
182
|
+
const paramNames = new Set([
|
|
183
|
+
...parsed.pathParams.map((p) => p.name),
|
|
184
|
+
...parsed.queryParams.map((p) => p.name),
|
|
185
|
+
...parsed.headerParams.map((p) => p.name),
|
|
186
|
+
]);
|
|
182
187
|
const requiredSet = new Set(parsed.requestBody.required ?? []);
|
|
183
188
|
for (const [key, propSchema] of Object.entries(parsed.requestBody.properties)) {
|
|
184
189
|
// Skip readOnly properties from input
|
|
185
190
|
if (propSchema.readOnly)
|
|
186
191
|
continue;
|
|
192
|
+
// Skip body properties that collide with path/query/header params
|
|
193
|
+
if (paramNames.has(key)) {
|
|
194
|
+
console.warn(`[openapi] Skipping body property '${key}' — collides with path/query/header param`);
|
|
195
|
+
continue;
|
|
196
|
+
}
|
|
187
197
|
const isOptional = !requiredSet.has(key);
|
|
188
198
|
const zodCode = schemaToZod(propSchema, ctx, { optional: isOptional });
|
|
189
199
|
props.push(` ${safeKey(key)}: ${zodCode},`);
|
|
@@ -235,7 +245,10 @@ function generateServiceFile(spec, opPairs, vars, flags) {
|
|
|
235
245
|
const lines = [];
|
|
236
246
|
// Always import all error classes used in the switch statement
|
|
237
247
|
const allErrorClasses = new Set(Object.values(STATUS_TO_ERROR));
|
|
238
|
-
if (flags.
|
|
248
|
+
if (flags.credential && flags.credential !== 'oauth2') {
|
|
249
|
+
// Per-user credential: no special imports needed, creds passed via constructor
|
|
250
|
+
}
|
|
251
|
+
else if (flags.oauth || flags.credential === 'oauth2') {
|
|
239
252
|
lines.push("import { OAuth2Client } from '@pikku/core/oauth2'");
|
|
240
253
|
lines.push("import type { TypedSecretService } from '#pikku/secrets/pikku-secrets.gen.js'");
|
|
241
254
|
}
|
|
@@ -286,7 +299,14 @@ function generateServiceFile(spec, opPairs, vars, flags) {
|
|
|
286
299
|
// Class declaration
|
|
287
300
|
lines.push(`export class ${pascalName}Service {`);
|
|
288
301
|
lines.push(' private baseUrl: string');
|
|
289
|
-
if (flags.
|
|
302
|
+
if (flags.credential && flags.credential !== 'oauth2') {
|
|
303
|
+
const credField = flags.credential === 'bearer' ? 'token' : 'apiKey';
|
|
304
|
+
lines.push('');
|
|
305
|
+
lines.push(` constructor(private creds: { ${credField}: string }, variables: TypedVariablesService) {`);
|
|
306
|
+
lines.push(` this.baseUrl = variables.get('${screamingName}_BASE_URL') as string`);
|
|
307
|
+
lines.push(' }');
|
|
308
|
+
}
|
|
309
|
+
else if (flags.oauth || flags.credential === 'oauth2') {
|
|
290
310
|
lines.push(' private oauth: OAuth2Client');
|
|
291
311
|
lines.push('');
|
|
292
312
|
lines.push(` constructor(secrets: TypedSecretService, variables: TypedVariablesService) {`);
|
|
@@ -359,7 +379,29 @@ function generateServiceFile(spec, opPairs, vars, flags) {
|
|
|
359
379
|
lines.push(' url.searchParams.set(key, value)');
|
|
360
380
|
lines.push(' }');
|
|
361
381
|
lines.push('');
|
|
362
|
-
if (flags.
|
|
382
|
+
if (flags.credential && flags.credential !== 'oauth2') {
|
|
383
|
+
// Per-user credential: use creds from wire.getCredentials()
|
|
384
|
+
if (flags.credential === 'bearer') {
|
|
385
|
+
lines.push(' headers.Authorization = `Bearer ${this.creds.token}`');
|
|
386
|
+
}
|
|
387
|
+
else {
|
|
388
|
+
// apikey: check spec for custom header name
|
|
389
|
+
const apiKeyScheme = Object.values(spec.securitySchemes).find((s) => s.type === 'apiKey');
|
|
390
|
+
if (apiKeyScheme?.name && apiKeyScheme?.in === 'header') {
|
|
391
|
+
lines.push(` headers[${JSON.stringify(apiKeyScheme.name)}] = this.creds.apiKey`);
|
|
392
|
+
}
|
|
393
|
+
else {
|
|
394
|
+
lines.push(' headers.Authorization = `Bearer ${this.creds.apiKey}`');
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
lines.push('');
|
|
398
|
+
lines.push(' const response = await fetch(url.toString(), {');
|
|
399
|
+
lines.push(' method,');
|
|
400
|
+
lines.push(' headers,');
|
|
401
|
+
lines.push(' body: body ? JSON.stringify(body) : undefined,');
|
|
402
|
+
lines.push(' })');
|
|
403
|
+
}
|
|
404
|
+
else if (flags.oauth || flags.credential === 'oauth2') {
|
|
363
405
|
lines.push(' const response = await this.oauth.request(url.toString(), {');
|
|
364
406
|
lines.push(' method,');
|
|
365
407
|
lines.push(' headers,');
|
package/package.json
CHANGED
package/src/codegen.ts
CHANGED
|
@@ -32,6 +32,7 @@ interface AddonVars {
|
|
|
32
32
|
interface CodegenFlags {
|
|
33
33
|
oauth: boolean
|
|
34
34
|
secret: boolean
|
|
35
|
+
credential?: 'apikey' | 'bearer' | 'oauth2'
|
|
35
36
|
mcp?: boolean
|
|
36
37
|
}
|
|
37
38
|
|
|
@@ -296,12 +297,24 @@ function buildInputSchema(
|
|
|
296
297
|
|
|
297
298
|
if (parsed.requestBody) {
|
|
298
299
|
if (parsed.requestBody.properties) {
|
|
300
|
+
const paramNames = new Set([
|
|
301
|
+
...parsed.pathParams.map((p) => p.name),
|
|
302
|
+
...parsed.queryParams.map((p) => p.name),
|
|
303
|
+
...parsed.headerParams.map((p) => p.name),
|
|
304
|
+
])
|
|
299
305
|
const requiredSet = new Set(parsed.requestBody.required ?? [])
|
|
300
306
|
for (const [key, propSchema] of Object.entries(
|
|
301
307
|
parsed.requestBody.properties
|
|
302
308
|
)) {
|
|
303
309
|
// Skip readOnly properties from input
|
|
304
310
|
if (propSchema.readOnly) continue
|
|
311
|
+
// Skip body properties that collide with path/query/header params
|
|
312
|
+
if (paramNames.has(key)) {
|
|
313
|
+
console.warn(
|
|
314
|
+
`[openapi] Skipping body property '${key}' — collides with path/query/header param`
|
|
315
|
+
)
|
|
316
|
+
continue
|
|
317
|
+
}
|
|
305
318
|
const isOptional = !requiredSet.has(key)
|
|
306
319
|
const zodCode = schemaToZod(propSchema, ctx, { optional: isOptional })
|
|
307
320
|
props.push(` ${safeKey(key)}: ${zodCode},`)
|
|
@@ -386,7 +399,9 @@ function generateServiceFile(
|
|
|
386
399
|
// Always import all error classes used in the switch statement
|
|
387
400
|
const allErrorClasses = new Set<string>(Object.values(STATUS_TO_ERROR))
|
|
388
401
|
|
|
389
|
-
if (flags.
|
|
402
|
+
if (flags.credential && flags.credential !== 'oauth2') {
|
|
403
|
+
// Per-user credential: no special imports needed, creds passed via constructor
|
|
404
|
+
} else if (flags.oauth || flags.credential === 'oauth2') {
|
|
390
405
|
lines.push("import { OAuth2Client } from '@pikku/core/oauth2'")
|
|
391
406
|
lines.push(
|
|
392
407
|
"import type { TypedSecretService } from '#pikku/secrets/pikku-secrets.gen.js'"
|
|
@@ -458,7 +473,17 @@ function generateServiceFile(
|
|
|
458
473
|
lines.push(`export class ${pascalName}Service {`)
|
|
459
474
|
lines.push(' private baseUrl: string')
|
|
460
475
|
|
|
461
|
-
if (flags.
|
|
476
|
+
if (flags.credential && flags.credential !== 'oauth2') {
|
|
477
|
+
const credField = flags.credential === 'bearer' ? 'token' : 'apiKey'
|
|
478
|
+
lines.push('')
|
|
479
|
+
lines.push(
|
|
480
|
+
` constructor(private creds: { ${credField}: string }, variables: TypedVariablesService) {`
|
|
481
|
+
)
|
|
482
|
+
lines.push(
|
|
483
|
+
` this.baseUrl = variables.get('${screamingName}_BASE_URL') as string`
|
|
484
|
+
)
|
|
485
|
+
lines.push(' }')
|
|
486
|
+
} else if (flags.oauth || flags.credential === 'oauth2') {
|
|
462
487
|
lines.push(' private oauth: OAuth2Client')
|
|
463
488
|
lines.push('')
|
|
464
489
|
lines.push(
|
|
@@ -548,7 +573,30 @@ function generateServiceFile(
|
|
|
548
573
|
lines.push(' }')
|
|
549
574
|
lines.push('')
|
|
550
575
|
|
|
551
|
-
if (flags.
|
|
576
|
+
if (flags.credential && flags.credential !== 'oauth2') {
|
|
577
|
+
// Per-user credential: use creds from wire.getCredentials()
|
|
578
|
+
if (flags.credential === 'bearer') {
|
|
579
|
+
lines.push(' headers.Authorization = `Bearer ${this.creds.token}`')
|
|
580
|
+
} else {
|
|
581
|
+
// apikey: check spec for custom header name
|
|
582
|
+
const apiKeyScheme = Object.values(spec.securitySchemes).find(
|
|
583
|
+
(s) => s.type === 'apiKey'
|
|
584
|
+
)
|
|
585
|
+
if (apiKeyScheme?.name && apiKeyScheme?.in === 'header') {
|
|
586
|
+
lines.push(
|
|
587
|
+
` headers[${JSON.stringify(apiKeyScheme.name)}] = this.creds.apiKey`
|
|
588
|
+
)
|
|
589
|
+
} else {
|
|
590
|
+
lines.push(' headers.Authorization = `Bearer ${this.creds.apiKey}`')
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
lines.push('')
|
|
594
|
+
lines.push(' const response = await fetch(url.toString(), {')
|
|
595
|
+
lines.push(' method,')
|
|
596
|
+
lines.push(' headers,')
|
|
597
|
+
lines.push(' body: body ? JSON.stringify(body) : undefined,')
|
|
598
|
+
lines.push(' })')
|
|
599
|
+
} else if (flags.oauth || flags.credential === 'oauth2') {
|
|
552
600
|
lines.push(
|
|
553
601
|
' const response = await this.oauth.request(url.toString(), {'
|
|
554
602
|
)
|