@pikku/cli 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/cli.schema.json +1 -1
- package/console-app/assets/{index-AX4YS8AA.js → index-DOk9fqHB.js} +49 -49
- package/console-app/index.html +1 -1
- package/dist/.pikku/agent/pikku-agent-types.gen.d.ts +6 -6
- package/dist/.pikku/agent/pikku-agent-wirings-meta.gen.js +1 -1
- package/dist/.pikku/agent/pikku-agent-wirings.gen.d.ts +1 -1
- package/dist/.pikku/agent/pikku-agent-wirings.gen.js +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channel-types.gen.js +1 -1
- package/dist/.pikku/channel/pikku-channels-meta.gen.js +1 -1
- package/dist/.pikku/channel/pikku-channels.gen.d.ts +1 -1
- package/dist/.pikku/channel/pikku-channels.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-channel.js +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-client.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-types.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings-meta.gen.json +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli-wirings.gen.js +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.d.ts +1 -1
- package/dist/.pikku/cli/pikku-cli.gen.js +1 -1
- package/dist/.pikku/console/pikku-node-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.d.ts +1 -1
- package/dist/.pikku/function/pikku-function-types.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.js +1 -1
- package/dist/.pikku/function/pikku-functions-meta.gen.json +160 -160
- package/dist/.pikku/function/pikku-functions.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-types.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings-meta.gen.js +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.d.ts +1 -1
- package/dist/.pikku/http/pikku-http-wirings.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-types.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings-meta.gen.js +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings.gen.d.ts +1 -1
- package/dist/.pikku/mcp/pikku-mcp-wirings.gen.js +1 -1
- package/dist/.pikku/pikku-bootstrap.gen.js +1 -1
- package/dist/.pikku/pikku-services.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.d.ts +1 -1
- package/dist/.pikku/pikku-types.gen.js +1 -1
- package/dist/.pikku/pikku-websocket.gen.d.ts +1 -1
- package/dist/.pikku/pikku-websocket.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-types.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/queue/pikku-queue-workers-wirings.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-remote-rpc-workers.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.js +1 -1
- package/dist/.pikku/rpc/pikku-rpc-wirings-meta.internal.gen.json +2 -2
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-scheduler-types.gen.js +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings-meta.gen.js +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.d.ts +1 -1
- package/dist/.pikku/scheduler/pikku-schedulers-wirings.gen.js +1 -1
- package/dist/.pikku/schemas/register.gen.js +9 -9
- package/dist/.pikku/schemas/schemas/PikkuCLIConfig.schema.json +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secret-types.gen.js +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.d.ts +1 -1
- package/dist/.pikku/secrets/pikku-secrets.gen.js +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.d.ts +1 -1
- package/dist/.pikku/trigger/pikku-trigger-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variable-types.gen.js +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.d.ts +1 -1
- package/dist/.pikku/variables/pikku-variables.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-types.gen.d.ts +35 -5
- package/dist/.pikku/workflow/pikku-workflow-types.gen.js +5 -5
- package/dist/.pikku/workflow/pikku-workflow-wirings-meta.gen.js +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.d.ts +1 -1
- package/dist/.pikku/workflow/pikku-workflow-wirings.gen.js +1 -1
- package/dist/src/cli.wiring.js +1 -1
- package/dist/src/functions/commands/all.js +16 -1
- package/dist/src/functions/commands/new-addon.js +20 -6
- package/dist/src/functions/commands/versions-check.js +4 -4
- package/dist/src/functions/commands/versions-init.js +1 -1
- package/dist/src/functions/commands/versions-update.js +1 -1
- package/dist/src/functions/runtimes/nextjs/pikku-command-nextjs.js +2 -2
- package/dist/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.d.ts +1 -1
- package/dist/src/functions/runtimes/nextjs/serialize-nextjs-backend-wrapper.js +2 -2
- package/dist/src/functions/runtimes/nextjs/serialize-nextjs-http-wrapper.d.ts +1 -1
- package/dist/src/functions/runtimes/nextjs/serialize-nextjs-http-wrapper.js +2 -2
- package/dist/src/functions/wirings/ai-agent/pikku-command-public-agent.js +1 -1
- package/dist/src/functions/wirings/ai-agent/serialize-ai-agent-types.js +12 -6
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.d.ts +1 -1
- package/dist/src/functions/wirings/ai-agent/serialize-public-agent.js +5 -5
- package/dist/src/functions/wirings/cli/pikku-command-cli-entry.js +1 -1
- package/dist/src/functions/wirings/cli/serialize-channel-cli.d.ts +1 -1
- package/dist/src/functions/wirings/cli/serialize-channel-cli.js +2 -2
- package/dist/src/functions/wirings/console/pikku-command-console-functions.js +1 -1
- package/dist/src/functions/wirings/console/serialize-console-functions.d.ts +1 -1
- package/dist/src/functions/wirings/console/serialize-console-functions.js +2 -2
- package/dist/src/functions/wirings/middleware/serialize-middleware-imports.js +22 -0
- package/dist/src/functions/wirings/permissions/serialize-permissions-imports.js +22 -0
- package/dist/src/functions/wirings/rpc/pikku-command-public-rpc.js +1 -1
- package/dist/src/functions/wirings/rpc/pikku-command-rpc-client.js +1 -1
- package/dist/src/functions/wirings/rpc/serialize-public-rpc.d.ts +1 -1
- package/dist/src/functions/wirings/rpc/serialize-public-rpc.js +4 -4
- package/dist/src/functions/wirings/rpc/serialize-rpc-wrapper.d.ts +1 -1
- package/dist/src/functions/wirings/rpc/serialize-rpc-wrapper.js +6 -6
- package/dist/src/functions/wirings/workflow/serialize-workflow-types.js +64 -8
- package/dist/src/services.js +5 -1
- package/dist/src/utils/pikku-cli-config.js +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +6 -5
- package/dist/src/utils/openapi/codegen.d.ts +0 -20
- package/dist/src/utils/openapi/codegen.js +0 -442
- package/dist/src/utils/openapi/naming.d.ts +0 -30
- package/dist/src/utils/openapi/naming.js +0 -167
- package/dist/src/utils/openapi/parse-openapi.d.ts +0 -61
- package/dist/src/utils/openapi/parse-openapi.js +0 -306
- package/dist/src/utils/openapi/zod-codegen.d.ts +0 -1
- package/dist/src/utils/openapi/zod-codegen.js +0 -1
|
@@ -1,167 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Derives function/method names from HTTP method + path.
|
|
3
|
-
* Uses operationId when available, otherwise generates from path segments.
|
|
4
|
-
*/
|
|
5
|
-
const IRREGULAR_PLURALS = {
|
|
6
|
-
addresses: 'address',
|
|
7
|
-
statuses: 'status',
|
|
8
|
-
indices: 'index',
|
|
9
|
-
analyses: 'analysis',
|
|
10
|
-
quizzes: 'quiz',
|
|
11
|
-
matrices: 'matrix',
|
|
12
|
-
vertices: 'vertex',
|
|
13
|
-
aliases: 'alias',
|
|
14
|
-
buses: 'bus',
|
|
15
|
-
};
|
|
16
|
-
export function singularize(word) {
|
|
17
|
-
const lower = word.toLowerCase();
|
|
18
|
-
if (IRREGULAR_PLURALS[lower]) {
|
|
19
|
-
const singular = IRREGULAR_PLURALS[lower];
|
|
20
|
-
return word[0] === word[0].toUpperCase()
|
|
21
|
-
? singular.charAt(0).toUpperCase() + singular.slice(1)
|
|
22
|
-
: singular;
|
|
23
|
-
}
|
|
24
|
-
if (lower.endsWith('ies') && lower.length > 4) {
|
|
25
|
-
return word.slice(0, -3) + 'y';
|
|
26
|
-
}
|
|
27
|
-
// -shes, -ches, -xes, -zes, -sses → drop "es"
|
|
28
|
-
if (lower.endsWith('shes') || lower.endsWith('ches') || lower.endsWith('xes') || lower.endsWith('zes') || lower.endsWith('sses')) {
|
|
29
|
-
return word.slice(0, -2);
|
|
30
|
-
}
|
|
31
|
-
// General: drop trailing "s" (covers courses→course, products→product, etc.)
|
|
32
|
-
if (lower.endsWith('s') && !lower.endsWith('ss') && !lower.endsWith('us') && lower.length > 2) {
|
|
33
|
-
return word.slice(0, -1);
|
|
34
|
-
}
|
|
35
|
-
return word;
|
|
36
|
-
}
|
|
37
|
-
export function toCamelCase(str) {
|
|
38
|
-
return str
|
|
39
|
-
.replace(/[^a-zA-Z0-9]+(.)/g, (_, c) => c.toUpperCase())
|
|
40
|
-
.replace(/^[A-Z]/, (c) => c.toLowerCase());
|
|
41
|
-
}
|
|
42
|
-
export function toPascalCase(str) {
|
|
43
|
-
const camel = toCamelCase(str);
|
|
44
|
-
return camel.charAt(0).toUpperCase() + camel.slice(1);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Strip common API prefix from paths (e.g. /api/v2/).
|
|
48
|
-
* Uses majority voting — a prefix shared by >75% of paths is stripped,
|
|
49
|
-
* so a few outlier paths (e.g. /oauth/...) don't break detection.
|
|
50
|
-
*/
|
|
51
|
-
export function detectCommonPrefix(paths) {
|
|
52
|
-
if (paths.length === 0)
|
|
53
|
-
return '';
|
|
54
|
-
const segments = paths.map((p) => p.replace(/^\//, '').split('/'));
|
|
55
|
-
const threshold = Math.ceil(paths.length * 0.75);
|
|
56
|
-
// Find the longest prefix shared by at least `threshold` paths
|
|
57
|
-
let prefixLen = 0;
|
|
58
|
-
for (let i = 0; i < 10; i++) {
|
|
59
|
-
// Count how many paths have the same segment at position i as the first path
|
|
60
|
-
const candidateSeg = segments[0][i];
|
|
61
|
-
if (!candidateSeg || candidateSeg.startsWith('{'))
|
|
62
|
-
break;
|
|
63
|
-
const count = segments.filter((s) => s.length > i + 1 && s[i] === candidateSeg).length;
|
|
64
|
-
if (count >= threshold) {
|
|
65
|
-
prefixLen = i + 1;
|
|
66
|
-
}
|
|
67
|
-
else {
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
}
|
|
71
|
-
if (prefixLen === 0)
|
|
72
|
-
return '';
|
|
73
|
-
return '/' + segments[0].slice(0, prefixLen).join('/') + '/';
|
|
74
|
-
}
|
|
75
|
-
/** Convert operationId to camelCase function name */
|
|
76
|
-
function fromOperationId(operationId) {
|
|
77
|
-
// operationId might be snake_case, camelCase, PascalCase, or kebab-case
|
|
78
|
-
return toCamelCase(operationId
|
|
79
|
-
.replace(/[^a-zA-Z0-9_-]/g, '_')
|
|
80
|
-
.replace(/_+/g, '_')
|
|
81
|
-
.replace(/^_|_$/g, ''));
|
|
82
|
-
}
|
|
83
|
-
const METHOD_PREFIXES = {
|
|
84
|
-
get: 'get',
|
|
85
|
-
post: 'create',
|
|
86
|
-
put: 'update',
|
|
87
|
-
patch: 'update',
|
|
88
|
-
delete: 'delete',
|
|
89
|
-
};
|
|
90
|
-
/**
|
|
91
|
-
* Generate function names for a list of operations.
|
|
92
|
-
* Handles collision detection and auto-deduplication.
|
|
93
|
-
*/
|
|
94
|
-
export function generateOperationNames(operations, commonPrefix) {
|
|
95
|
-
const results = [];
|
|
96
|
-
const usedNames = new Map();
|
|
97
|
-
for (const op of operations) {
|
|
98
|
-
let name;
|
|
99
|
-
if (op.operationId) {
|
|
100
|
-
name = fromOperationId(op.operationId);
|
|
101
|
-
}
|
|
102
|
-
else {
|
|
103
|
-
name = deriveNameFromPath(op.method, op.path, commonPrefix);
|
|
104
|
-
}
|
|
105
|
-
// Handle collisions
|
|
106
|
-
const existing = usedNames.get(name) ?? 0;
|
|
107
|
-
if (existing > 0) {
|
|
108
|
-
usedNames.set(name, existing + 1);
|
|
109
|
-
name = `${name}${existing + 1}`;
|
|
110
|
-
}
|
|
111
|
-
usedNames.set(name, (usedNames.get(name) ?? 0) || 1);
|
|
112
|
-
results.push({
|
|
113
|
-
method: op.method,
|
|
114
|
-
path: op.path,
|
|
115
|
-
functionName: name,
|
|
116
|
-
methodName: name,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
return results;
|
|
120
|
-
}
|
|
121
|
-
function deriveNameFromPath(method, path, commonPrefix) {
|
|
122
|
-
// Strip common prefix
|
|
123
|
-
let cleanPath = path;
|
|
124
|
-
if (commonPrefix && cleanPath.startsWith(commonPrefix)) {
|
|
125
|
-
cleanPath = '/' + cleanPath.slice(commonPrefix.length);
|
|
126
|
-
}
|
|
127
|
-
const segments = cleanPath
|
|
128
|
-
.replace(/^\//, '')
|
|
129
|
-
.split('/')
|
|
130
|
-
.filter(Boolean);
|
|
131
|
-
const methodLower = method.toLowerCase();
|
|
132
|
-
const prefix = METHOD_PREFIXES[methodLower] || methodLower;
|
|
133
|
-
// Separate param and non-param segments
|
|
134
|
-
const nonParams = [];
|
|
135
|
-
let hasTrailingParam = false;
|
|
136
|
-
for (let i = 0; i < segments.length; i++) {
|
|
137
|
-
if (segments[i].startsWith('{')) {
|
|
138
|
-
if (i === segments.length - 1) {
|
|
139
|
-
hasTrailingParam = true;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
nonParams.push(segments[i]);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
if (nonParams.length === 0) {
|
|
147
|
-
return prefix;
|
|
148
|
-
}
|
|
149
|
-
// For GET with trailing param: singularize the last non-param segment
|
|
150
|
-
// For GET without trailing param: keep plural (list)
|
|
151
|
-
// For POST without trailing param: use prefix (create)
|
|
152
|
-
const parts = nonParams.map((seg, i) => {
|
|
153
|
-
const isLast = i === nonParams.length - 1;
|
|
154
|
-
let word = seg;
|
|
155
|
-
if (isLast && hasTrailingParam) {
|
|
156
|
-
word = singularize(word);
|
|
157
|
-
}
|
|
158
|
-
// For GET on collection (no trailing param), use "list" prefix instead of "get"
|
|
159
|
-
return toPascalCase(word);
|
|
160
|
-
});
|
|
161
|
-
// For GET on collection, use "list" instead of "get"
|
|
162
|
-
let finalPrefix = prefix;
|
|
163
|
-
if (methodLower === 'get' && !hasTrailingParam) {
|
|
164
|
-
finalPrefix = 'list';
|
|
165
|
-
}
|
|
166
|
-
return finalPrefix + parts.join('');
|
|
167
|
-
}
|
|
@@ -1,61 +0,0 @@
|
|
|
1
|
-
import type { OpenAPISchema } from './zod-codegen.js';
|
|
2
|
-
export interface ErrorResponse {
|
|
3
|
-
statusCode: number;
|
|
4
|
-
description: string;
|
|
5
|
-
}
|
|
6
|
-
export interface SecuritySchemeInfo {
|
|
7
|
-
type: 'oauth2' | 'http' | 'apiKey';
|
|
8
|
-
scheme?: string;
|
|
9
|
-
bearerFormat?: string;
|
|
10
|
-
name?: string;
|
|
11
|
-
in?: string;
|
|
12
|
-
flows?: {
|
|
13
|
-
authorizationUrl?: string;
|
|
14
|
-
tokenUrl?: string;
|
|
15
|
-
scopes?: Record<string, string>;
|
|
16
|
-
};
|
|
17
|
-
}
|
|
18
|
-
export interface ParsedSpec {
|
|
19
|
-
info: {
|
|
20
|
-
title: string;
|
|
21
|
-
version: string;
|
|
22
|
-
description?: string;
|
|
23
|
-
};
|
|
24
|
-
baseUrl: string;
|
|
25
|
-
serverUrls: string[];
|
|
26
|
-
authType: 'bearer' | 'oauth2' | 'apiKey' | 'none';
|
|
27
|
-
operations: ParsedOperation[];
|
|
28
|
-
componentSchemas: Record<string, OpenAPISchema>;
|
|
29
|
-
securitySchemes: Record<string, SecuritySchemeInfo>;
|
|
30
|
-
tagDescriptions: Record<string, string>;
|
|
31
|
-
}
|
|
32
|
-
export interface ParsedOperation {
|
|
33
|
-
operationId?: string;
|
|
34
|
-
method: string;
|
|
35
|
-
path: string;
|
|
36
|
-
summary?: string;
|
|
37
|
-
description?: string;
|
|
38
|
-
tags: string[];
|
|
39
|
-
pathParams: ParsedParam[];
|
|
40
|
-
queryParams: ParsedParam[];
|
|
41
|
-
headerParams: ParsedParam[];
|
|
42
|
-
requestBody?: OpenAPISchema;
|
|
43
|
-
requestBodyDescription?: string;
|
|
44
|
-
requestBodyRequired?: boolean;
|
|
45
|
-
responseSchema?: OpenAPISchema;
|
|
46
|
-
responseDescription?: string;
|
|
47
|
-
errorResponses: ErrorResponse[];
|
|
48
|
-
deprecated: boolean;
|
|
49
|
-
}
|
|
50
|
-
export interface ParsedParam {
|
|
51
|
-
name: string;
|
|
52
|
-
required: boolean;
|
|
53
|
-
schema: OpenAPISchema;
|
|
54
|
-
description?: string;
|
|
55
|
-
example?: unknown;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Read and parse an OpenAPI spec from a file path.
|
|
59
|
-
* Supports both YAML (.yaml, .yml) and JSON (.json) files.
|
|
60
|
-
*/
|
|
61
|
-
export declare function parseOpenAPISpec(filePath: string): Promise<ParsedSpec>;
|
|
@@ -1,306 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parses OpenAPI YAML/JSON specs, resolves $ref pointers, and produces a normalized IR.
|
|
3
|
-
*/
|
|
4
|
-
import { readFile } from 'fs/promises';
|
|
5
|
-
import { parse as parseYAML } from 'yaml';
|
|
6
|
-
/**
|
|
7
|
-
* Read and parse an OpenAPI spec from a file path.
|
|
8
|
-
* Supports both YAML (.yaml, .yml) and JSON (.json) files.
|
|
9
|
-
*/
|
|
10
|
-
export async function parseOpenAPISpec(filePath) {
|
|
11
|
-
const content = await readFile(filePath, 'utf-8');
|
|
12
|
-
let doc;
|
|
13
|
-
if (filePath.endsWith('.json')) {
|
|
14
|
-
doc = JSON.parse(content);
|
|
15
|
-
}
|
|
16
|
-
else {
|
|
17
|
-
doc = parseYAML(content);
|
|
18
|
-
}
|
|
19
|
-
// Validate spec version
|
|
20
|
-
const specVersion = doc.openapi ?? doc.swagger;
|
|
21
|
-
if (!specVersion) {
|
|
22
|
-
throw new Error('Not a valid OpenAPI/Swagger spec: missing "openapi" or "swagger" version field.');
|
|
23
|
-
}
|
|
24
|
-
const major = String(specVersion).split('.')[0];
|
|
25
|
-
if (major !== '2' && major !== '3') {
|
|
26
|
-
throw new Error(`Unsupported spec version "${specVersion}". Only OpenAPI 3.x and Swagger 2.x are supported.`);
|
|
27
|
-
}
|
|
28
|
-
// Resolve all $ref pointers in-place
|
|
29
|
-
resolveRefs(doc, doc);
|
|
30
|
-
const info = {
|
|
31
|
-
title: doc.info?.title ?? 'Unknown API',
|
|
32
|
-
version: doc.info?.version ?? '1.0.0',
|
|
33
|
-
description: doc.info?.description,
|
|
34
|
-
};
|
|
35
|
-
const serverUrls = extractServerUrls(doc);
|
|
36
|
-
const baseUrl = serverUrls[0] ?? '';
|
|
37
|
-
const authType = detectAuthType(doc);
|
|
38
|
-
const securitySchemes = extractSecuritySchemes(doc);
|
|
39
|
-
const tagDescriptions = extractTagDescriptions(doc);
|
|
40
|
-
// Extract component schemas
|
|
41
|
-
const componentSchemas = {};
|
|
42
|
-
if (doc.components?.schemas) {
|
|
43
|
-
for (const [name, schema] of Object.entries(doc.components.schemas)) {
|
|
44
|
-
componentSchemas[name] = schema;
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// Extract operations (skip deprecated)
|
|
48
|
-
const operations = [];
|
|
49
|
-
if (doc.paths) {
|
|
50
|
-
for (const [path, pathItem] of Object.entries(doc.paths)) {
|
|
51
|
-
// Shared parameters at the path level
|
|
52
|
-
const sharedParams = pathItem.parameters ?? [];
|
|
53
|
-
for (const method of ['get', 'post', 'put', 'patch', 'delete']) {
|
|
54
|
-
const op = pathItem[method];
|
|
55
|
-
if (!op)
|
|
56
|
-
continue;
|
|
57
|
-
// Skip deprecated operations
|
|
58
|
-
if (op.deprecated === true)
|
|
59
|
-
continue;
|
|
60
|
-
const allParams = [...sharedParams, ...(op.parameters ?? [])];
|
|
61
|
-
const body = op.requestBody;
|
|
62
|
-
operations.push({
|
|
63
|
-
operationId: op.operationId,
|
|
64
|
-
method,
|
|
65
|
-
path,
|
|
66
|
-
summary: op.summary,
|
|
67
|
-
description: op.description,
|
|
68
|
-
tags: op.tags ?? [],
|
|
69
|
-
pathParams: extractParams(allParams, 'path'),
|
|
70
|
-
queryParams: extractParams(allParams, 'query'),
|
|
71
|
-
headerParams: extractParams(allParams, 'header'),
|
|
72
|
-
requestBody: extractRequestBody(op),
|
|
73
|
-
requestBodyDescription: body?.description,
|
|
74
|
-
requestBodyRequired: body?.required,
|
|
75
|
-
responseSchema: extractResponseSchema(op),
|
|
76
|
-
responseDescription: extractResponseDescription(op),
|
|
77
|
-
errorResponses: extractErrorResponses(op),
|
|
78
|
-
deprecated: false,
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
return {
|
|
84
|
-
info,
|
|
85
|
-
baseUrl,
|
|
86
|
-
serverUrls,
|
|
87
|
-
authType,
|
|
88
|
-
operations,
|
|
89
|
-
componentSchemas,
|
|
90
|
-
securitySchemes,
|
|
91
|
-
tagDescriptions,
|
|
92
|
-
};
|
|
93
|
-
}
|
|
94
|
-
/** Recursively resolve $ref pointers in-place */
|
|
95
|
-
function resolveRefs(node, root) {
|
|
96
|
-
if (node === null || typeof node !== 'object')
|
|
97
|
-
return node;
|
|
98
|
-
if (Array.isArray(node)) {
|
|
99
|
-
for (let i = 0; i < node.length; i++) {
|
|
100
|
-
node[i] = resolveRefs(node[i], root);
|
|
101
|
-
}
|
|
102
|
-
return node;
|
|
103
|
-
}
|
|
104
|
-
if (typeof node.$ref === 'string') {
|
|
105
|
-
const resolved = resolveRefPath(node.$ref, root);
|
|
106
|
-
if (resolved && typeof resolved === 'object') {
|
|
107
|
-
// Merge any sibling properties (like description overrides)
|
|
108
|
-
const { $ref, ...siblings } = node;
|
|
109
|
-
const result = { ...resolved, ...siblings };
|
|
110
|
-
// Don't recurse infinitely — mark as resolved
|
|
111
|
-
return result;
|
|
112
|
-
}
|
|
113
|
-
return node;
|
|
114
|
-
}
|
|
115
|
-
for (const key of Object.keys(node)) {
|
|
116
|
-
node[key] = resolveRefs(node[key], root);
|
|
117
|
-
}
|
|
118
|
-
return node;
|
|
119
|
-
}
|
|
120
|
-
function resolveRefPath(ref, root) {
|
|
121
|
-
if (!ref.startsWith('#/'))
|
|
122
|
-
return undefined;
|
|
123
|
-
const parts = ref.slice(2).split('/');
|
|
124
|
-
let current = root;
|
|
125
|
-
for (const part of parts) {
|
|
126
|
-
const decoded = part.replace(/~1/g, '/').replace(/~0/g, '~');
|
|
127
|
-
if (current == null || typeof current !== 'object')
|
|
128
|
-
return undefined;
|
|
129
|
-
current = current[decoded];
|
|
130
|
-
}
|
|
131
|
-
return current;
|
|
132
|
-
}
|
|
133
|
-
function extractServerUrls(doc) {
|
|
134
|
-
// OpenAPI 3.x
|
|
135
|
-
if (doc.servers && doc.servers.length > 0) {
|
|
136
|
-
return doc.servers.map((s) => s.url).filter((url) => !!url);
|
|
137
|
-
}
|
|
138
|
-
// Swagger 2.x
|
|
139
|
-
if (doc.host) {
|
|
140
|
-
const schemes = doc.schemes?.length ? doc.schemes : ['https'];
|
|
141
|
-
const basePath = doc.basePath ?? '';
|
|
142
|
-
return schemes.map((scheme) => `${scheme}://${doc.host}${basePath}`);
|
|
143
|
-
}
|
|
144
|
-
return [];
|
|
145
|
-
}
|
|
146
|
-
function detectAuthType(doc) {
|
|
147
|
-
const securitySchemes = doc.components?.securitySchemes ?? doc.securityDefinitions ?? {};
|
|
148
|
-
for (const scheme of Object.values(securitySchemes)) {
|
|
149
|
-
if (scheme.type === 'oauth2')
|
|
150
|
-
return 'oauth2';
|
|
151
|
-
if (scheme.type === 'http' && scheme.scheme === 'bearer')
|
|
152
|
-
return 'bearer';
|
|
153
|
-
if (scheme.type === 'apiKey')
|
|
154
|
-
return 'apiKey';
|
|
155
|
-
}
|
|
156
|
-
return 'none';
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Build an OpenAPI schema object from v2-style parameter-level properties
|
|
160
|
-
* (type, enum, items, format, default) that aren't nested under `schema`.
|
|
161
|
-
*/
|
|
162
|
-
function paramToSchema(p) {
|
|
163
|
-
const schema = { type: p.type ?? 'string' };
|
|
164
|
-
if (p.enum)
|
|
165
|
-
schema.enum = p.enum;
|
|
166
|
-
if (p.items)
|
|
167
|
-
schema.items = p.items;
|
|
168
|
-
if (p.format)
|
|
169
|
-
schema.format = p.format;
|
|
170
|
-
if (p.default !== undefined)
|
|
171
|
-
schema.default = p.default;
|
|
172
|
-
return schema;
|
|
173
|
-
}
|
|
174
|
-
function extractParams(params, location) {
|
|
175
|
-
return params
|
|
176
|
-
.filter((p) => p.in === location)
|
|
177
|
-
.map((p) => ({
|
|
178
|
-
name: p.name,
|
|
179
|
-
required: p.required ?? location === 'path',
|
|
180
|
-
schema: (p.schema ?? paramToSchema(p)),
|
|
181
|
-
description: p.description,
|
|
182
|
-
example: p.example,
|
|
183
|
-
}));
|
|
184
|
-
}
|
|
185
|
-
function extractRequestBody(op) {
|
|
186
|
-
const body = op.requestBody;
|
|
187
|
-
if (!body)
|
|
188
|
-
return undefined;
|
|
189
|
-
const content = body.content;
|
|
190
|
-
if (!content)
|
|
191
|
-
return undefined;
|
|
192
|
-
// Prefer JSON
|
|
193
|
-
const jsonContent = content['application/json'];
|
|
194
|
-
if (jsonContent?.schema)
|
|
195
|
-
return jsonContent.schema;
|
|
196
|
-
// Fallback to first content type
|
|
197
|
-
const firstKey = Object.keys(content)[0];
|
|
198
|
-
if (firstKey && content[firstKey]?.schema) {
|
|
199
|
-
return content[firstKey].schema;
|
|
200
|
-
}
|
|
201
|
-
return undefined;
|
|
202
|
-
}
|
|
203
|
-
function extractResponseDescription(op) {
|
|
204
|
-
const responses = op.responses;
|
|
205
|
-
if (!responses)
|
|
206
|
-
return undefined;
|
|
207
|
-
for (const code of ['200', '201', '202']) {
|
|
208
|
-
const resp = responses[code];
|
|
209
|
-
if (resp?.description)
|
|
210
|
-
return resp.description;
|
|
211
|
-
}
|
|
212
|
-
return undefined;
|
|
213
|
-
}
|
|
214
|
-
function extractResponseSchema(op) {
|
|
215
|
-
const responses = op.responses;
|
|
216
|
-
if (!responses)
|
|
217
|
-
return undefined;
|
|
218
|
-
// Look for 2xx responses in order of preference
|
|
219
|
-
for (const code of ['200', '201', '202', '204']) {
|
|
220
|
-
const resp = responses[code];
|
|
221
|
-
if (!resp)
|
|
222
|
-
continue;
|
|
223
|
-
const content = resp.content;
|
|
224
|
-
if (!content)
|
|
225
|
-
continue;
|
|
226
|
-
const jsonContent = content['application/json'];
|
|
227
|
-
if (jsonContent?.schema)
|
|
228
|
-
return jsonContent.schema;
|
|
229
|
-
const firstKey = Object.keys(content)[0];
|
|
230
|
-
if (firstKey && content[firstKey]?.schema) {
|
|
231
|
-
return content[firstKey].schema;
|
|
232
|
-
}
|
|
233
|
-
}
|
|
234
|
-
// Fallback: any 2xx
|
|
235
|
-
for (const [code, resp] of Object.entries(responses)) {
|
|
236
|
-
if (code.startsWith('2') && resp.content) {
|
|
237
|
-
const jsonContent = resp.content['application/json'];
|
|
238
|
-
if (jsonContent?.schema)
|
|
239
|
-
return jsonContent.schema;
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
return undefined;
|
|
243
|
-
}
|
|
244
|
-
function extractErrorResponses(op) {
|
|
245
|
-
const responses = op.responses;
|
|
246
|
-
if (!responses)
|
|
247
|
-
return [];
|
|
248
|
-
const errors = [];
|
|
249
|
-
for (const [code, resp] of Object.entries(responses)) {
|
|
250
|
-
const statusCode = parseInt(code, 10);
|
|
251
|
-
if (isNaN(statusCode))
|
|
252
|
-
continue;
|
|
253
|
-
if (statusCode >= 400) {
|
|
254
|
-
errors.push({
|
|
255
|
-
statusCode,
|
|
256
|
-
description: resp.description ?? `Error ${statusCode}`,
|
|
257
|
-
});
|
|
258
|
-
}
|
|
259
|
-
}
|
|
260
|
-
return errors;
|
|
261
|
-
}
|
|
262
|
-
function extractSecuritySchemes(doc) {
|
|
263
|
-
const raw = doc.components?.securitySchemes ?? doc.securityDefinitions ?? {};
|
|
264
|
-
const result = {};
|
|
265
|
-
for (const [name, scheme] of Object.entries(raw)) {
|
|
266
|
-
const info = {
|
|
267
|
-
type: scheme.type === 'http' ? 'http' : scheme.type,
|
|
268
|
-
};
|
|
269
|
-
if (scheme.type === 'http') {
|
|
270
|
-
info.scheme = scheme.scheme;
|
|
271
|
-
info.bearerFormat = scheme.bearerFormat;
|
|
272
|
-
}
|
|
273
|
-
if (scheme.type === 'apiKey') {
|
|
274
|
-
info.name = scheme.name;
|
|
275
|
-
info.in = scheme.in;
|
|
276
|
-
}
|
|
277
|
-
if (scheme.type === 'oauth2') {
|
|
278
|
-
// Extract flows — prefer authorizationCode, then implicit, then clientCredentials
|
|
279
|
-
const flows = scheme.flows ?? {};
|
|
280
|
-
const flow = flows.authorizationCode ??
|
|
281
|
-
flows.implicit ??
|
|
282
|
-
flows.clientCredentials ??
|
|
283
|
-
flows.password;
|
|
284
|
-
if (flow) {
|
|
285
|
-
info.flows = {
|
|
286
|
-
authorizationUrl: flow.authorizationUrl,
|
|
287
|
-
tokenUrl: flow.tokenUrl,
|
|
288
|
-
scopes: flow.scopes,
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
}
|
|
292
|
-
result[name] = info;
|
|
293
|
-
}
|
|
294
|
-
return result;
|
|
295
|
-
}
|
|
296
|
-
function extractTagDescriptions(doc) {
|
|
297
|
-
const result = {};
|
|
298
|
-
if (Array.isArray(doc.tags)) {
|
|
299
|
-
for (const tag of doc.tags) {
|
|
300
|
-
if (tag.name && tag.description) {
|
|
301
|
-
result[tag.name] = tag.description;
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
return result;
|
|
306
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { type OpenAPISchema, type ZodCodegenContext, createContext, schemaToZod, schemaVarName, sanitizeTypeName, } from '@pikku/openapi-to-zod-schema';
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export { createContext, schemaToZod, schemaVarName, sanitizeTypeName, } from '@pikku/openapi-to-zod-schema';
|