norn-cli 1.3.16 → 1.3.18

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.
@@ -0,0 +1,339 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.getCachedSwaggerSpec = getCachedSwaggerSpec;
7
+ exports.clearSwaggerCache = clearSwaggerCache;
8
+ exports.invalidateSwaggerCache = invalidateSwaggerCache;
9
+ exports.parseSwaggerSpec = parseSwaggerSpec;
10
+ exports.generateNornapiContent = generateNornapiContent;
11
+ exports.extractResponseSchemas = extractResponseSchemas;
12
+ exports.generateSchemaFilename = generateSchemaFilename;
13
+ const axios_1 = __importDefault(require("axios"));
14
+ /**
15
+ * Cache for parsed swagger specs (cleared when extension deactivates)
16
+ */
17
+ const swaggerCache = new Map();
18
+ /**
19
+ * Get a cached swagger spec, or fetch and parse if not cached
20
+ */
21
+ async function getCachedSwaggerSpec(url) {
22
+ if (swaggerCache.has(url)) {
23
+ return swaggerCache.get(url);
24
+ }
25
+ const spec = await parseSwaggerSpec(url);
26
+ swaggerCache.set(url, spec);
27
+ return spec;
28
+ }
29
+ /**
30
+ * Clear the swagger cache (call on extension deactivation)
31
+ */
32
+ function clearSwaggerCache() {
33
+ swaggerCache.clear();
34
+ }
35
+ /**
36
+ * Invalidate a specific URL from the cache (for manual refresh)
37
+ */
38
+ function invalidateSwaggerCache(url) {
39
+ if (url) {
40
+ swaggerCache.delete(url);
41
+ }
42
+ else {
43
+ swaggerCache.clear();
44
+ }
45
+ }
46
+ /**
47
+ * Fetches and parses an OpenAPI/Swagger specification from a URL.
48
+ */
49
+ async function parseSwaggerSpec(url) {
50
+ // Fetch the spec
51
+ const response = await axios_1.default.get(url, {
52
+ headers: {
53
+ 'Accept': 'application/json'
54
+ },
55
+ timeout: 30000
56
+ });
57
+ const spec = response.data;
58
+ // Determine if it's OpenAPI 3.x or Swagger 2.0
59
+ const isOpenAPI3 = spec.openapi && spec.openapi.startsWith('3.');
60
+ // Extract base URL
61
+ let baseUrl = '';
62
+ if (isOpenAPI3) {
63
+ // OpenAPI 3.x uses servers array
64
+ if (spec.servers && spec.servers.length > 0) {
65
+ baseUrl = spec.servers[0].url;
66
+ }
67
+ }
68
+ else {
69
+ // Swagger 2.0 uses host + basePath + schemes
70
+ const scheme = spec.schemes?.[0] || 'https';
71
+ const host = spec.host || '';
72
+ const basePath = spec.basePath || '';
73
+ if (host) {
74
+ baseUrl = `${scheme}://${host}${basePath}`;
75
+ }
76
+ }
77
+ // Extract title and version
78
+ const title = spec.info?.title || 'API';
79
+ const version = spec.info?.version || '1.0.0';
80
+ // Group endpoints by tag
81
+ const sectionMap = new Map();
82
+ // Parse paths
83
+ const paths = spec.paths || {};
84
+ for (const [path, pathItem] of Object.entries(paths)) {
85
+ const pathObj = pathItem;
86
+ for (const method of ['get', 'post', 'put', 'delete', 'patch', 'head', 'options']) {
87
+ const operation = pathObj[method];
88
+ if (!operation)
89
+ continue;
90
+ // Get operation details
91
+ const operationId = operation.operationId || generateOperationId(method, path);
92
+ const tags = operation.tags || ['default'];
93
+ const summary = operation.summary;
94
+ // Extract path parameters
95
+ const parameters = [];
96
+ const pathParams = path.match(/\{([^}]+)\}/g);
97
+ if (pathParams) {
98
+ for (const param of pathParams) {
99
+ parameters.push(param.replace(/[{}]/g, ''));
100
+ }
101
+ }
102
+ // Also check query parameters that are required
103
+ const paramDefs = operation.parameters;
104
+ if (paramDefs) {
105
+ for (const param of paramDefs) {
106
+ if (param.in === 'query' && param.required) {
107
+ parameters.push(param.name);
108
+ }
109
+ }
110
+ }
111
+ // Extract response codes from operation.responses
112
+ const responseCodes = [];
113
+ const responses = operation.responses;
114
+ if (responses) {
115
+ for (const code of Object.keys(responses)) {
116
+ // Include numeric status codes (skip 'default')
117
+ if (/^\d{3}$/.test(code)) {
118
+ responseCodes.push(code);
119
+ }
120
+ }
121
+ // Sort numerically
122
+ responseCodes.sort((a, b) => parseInt(a) - parseInt(b));
123
+ }
124
+ const endpoint = {
125
+ name: sanitizeOperationId(operationId),
126
+ method: method.toUpperCase(),
127
+ path,
128
+ parameters,
129
+ tag: tags[0] || 'default',
130
+ summary,
131
+ responseCodes: responseCodes.length > 0 ? responseCodes : ['200'] // Default to 200 if no responses defined
132
+ };
133
+ // Add to section
134
+ const tag = endpoint.tag;
135
+ if (!sectionMap.has(tag)) {
136
+ sectionMap.set(tag, []);
137
+ }
138
+ sectionMap.get(tag).push(endpoint);
139
+ }
140
+ }
141
+ // Convert to sections array
142
+ const sections = [];
143
+ // Get tag descriptions if available
144
+ const tagDescriptions = new Map();
145
+ if (spec.tags) {
146
+ for (const tag of spec.tags) {
147
+ if (tag.description) {
148
+ tagDescriptions.set(tag.name, tag.description);
149
+ }
150
+ }
151
+ }
152
+ for (const [name, endpoints] of sectionMap) {
153
+ sections.push({
154
+ name,
155
+ description: tagDescriptions.get(name),
156
+ endpoints
157
+ });
158
+ }
159
+ // Sort sections alphabetically
160
+ sections.sort((a, b) => a.name.localeCompare(b.name));
161
+ return {
162
+ title,
163
+ version,
164
+ baseUrl,
165
+ sections
166
+ };
167
+ }
168
+ /**
169
+ * Generate an operation ID from method and path if not provided
170
+ */
171
+ function generateOperationId(method, path) {
172
+ // Convert /pet/{petId} to getPetByPetId
173
+ const parts = path.split('/').filter(p => p && !p.startsWith('{'));
174
+ const pathName = parts.map(p => p.charAt(0).toUpperCase() + p.slice(1)).join('');
175
+ return method.toLowerCase() + pathName;
176
+ }
177
+ /**
178
+ * Sanitize operation ID to be a valid identifier
179
+ */
180
+ function sanitizeOperationId(operationId) {
181
+ // Remove invalid characters and ensure it starts with a letter
182
+ let sanitized = operationId
183
+ .replace(/[^a-zA-Z0-9_]/g, '_')
184
+ .replace(/_+/g, '_')
185
+ .replace(/^_+|_+$/g, '');
186
+ // Ensure it starts with a capital letter
187
+ if (sanitized && /^[a-z]/.test(sanitized)) {
188
+ sanitized = sanitized.charAt(0).toUpperCase() + sanitized.slice(1);
189
+ }
190
+ return sanitized || 'Endpoint';
191
+ }
192
+ /**
193
+ * Generate .nornapi content for selected sections
194
+ */
195
+ function generateNornapiContent(spec, selectedSections, customBaseUrl) {
196
+ const lines = [];
197
+ const baseUrl = customBaseUrl || spec.baseUrl;
198
+ // Add endpoints section
199
+ lines.push('endpoints');
200
+ const sectionsToInclude = selectedSections.includes('All')
201
+ ? spec.sections
202
+ : spec.sections.filter(s => selectedSections.includes(s.name));
203
+ for (const section of sectionsToInclude) {
204
+ // Add section comment
205
+ lines.push(` # ${section.name}${section.description ? ` - ${section.description}` : ''}`);
206
+ for (const endpoint of section.endpoints) {
207
+ // Build the endpoint line with explicit method for full parser/editor support
208
+ const path = endpoint.path;
209
+ lines.push(` ${endpoint.name}: ${endpoint.method} ${baseUrl}${path}`);
210
+ }
211
+ lines.push('');
212
+ }
213
+ lines.push('end endpoints');
214
+ return lines.join('\n');
215
+ }
216
+ /**
217
+ * Extract response schemas from an OpenAPI/Swagger spec
218
+ * Resolves $ref references to inline schemas
219
+ * @param url URL of the OpenAPI spec
220
+ * @returns Array of response schemas
221
+ */
222
+ async function extractResponseSchemas(url) {
223
+ const response = await axios_1.default.get(url, {
224
+ headers: { 'Accept': 'application/json' },
225
+ timeout: 30000
226
+ });
227
+ const spec = response.data;
228
+ const schemas = [];
229
+ const isOpenAPI3 = spec.openapi && spec.openapi.startsWith('3.');
230
+ // Helper to resolve $ref references
231
+ const resolveRef = (ref, root) => {
232
+ // $ref format: "#/components/schemas/Pet" or "#/definitions/Pet"
233
+ if (!ref.startsWith('#/')) {
234
+ return undefined;
235
+ }
236
+ const parts = ref.slice(2).split('/');
237
+ let current = root;
238
+ for (const part of parts) {
239
+ if (current === undefined)
240
+ return undefined;
241
+ current = current[part];
242
+ }
243
+ return current;
244
+ };
245
+ // Recursively resolve all $ref in a schema
246
+ const resolveSchemaRefs = (schema, root, visited = new Set()) => {
247
+ if (!schema || typeof schema !== 'object') {
248
+ return schema;
249
+ }
250
+ // Handle $ref
251
+ if (schema.$ref && typeof schema.$ref === 'string') {
252
+ if (visited.has(schema.$ref)) {
253
+ // Circular reference - return simple object type
254
+ return { type: 'object' };
255
+ }
256
+ visited.add(schema.$ref);
257
+ const resolved = resolveRef(schema.$ref, root);
258
+ if (resolved) {
259
+ return resolveSchemaRefs(resolved, root, visited);
260
+ }
261
+ return schema;
262
+ }
263
+ // Recursively resolve properties, items, etc.
264
+ const result = Array.isArray(schema) ? [] : {};
265
+ for (const key of Object.keys(schema)) {
266
+ if (key === '$ref')
267
+ continue;
268
+ const value = schema[key];
269
+ if (typeof value === 'object' && value !== null) {
270
+ result[key] = resolveSchemaRefs(value, root, new Set(visited));
271
+ }
272
+ else {
273
+ result[key] = value;
274
+ }
275
+ }
276
+ return result;
277
+ };
278
+ // Parse paths
279
+ const paths = spec.paths || {};
280
+ for (const [pathStr, pathItem] of Object.entries(paths)) {
281
+ const pathObj = pathItem;
282
+ for (const method of ['get', 'post', 'put', 'delete', 'patch', 'head', 'options']) {
283
+ const operation = pathObj[method];
284
+ if (!operation)
285
+ continue;
286
+ const operationId = operation.operationId || generateOperationId(method, pathStr);
287
+ const responses = operation.responses;
288
+ if (!responses)
289
+ continue;
290
+ for (const [statusCode, responseObj] of Object.entries(responses)) {
291
+ // Skip non-numeric status codes like 'default'
292
+ if (!/^\d{3}$/.test(statusCode))
293
+ continue;
294
+ let schema = null;
295
+ if (isOpenAPI3) {
296
+ // OpenAPI 3.x: responses[code].content["application/json"].schema
297
+ const content = responseObj.content;
298
+ if (content) {
299
+ const jsonContent = content['application/json'] || content['*/*'];
300
+ if (jsonContent?.schema) {
301
+ schema = jsonContent.schema;
302
+ }
303
+ }
304
+ }
305
+ else {
306
+ // Swagger 2.0: responses[code].schema
307
+ if (responseObj.schema) {
308
+ schema = responseObj.schema;
309
+ }
310
+ }
311
+ if (schema) {
312
+ // Resolve all $ref references
313
+ const resolvedSchema = resolveSchemaRefs(schema, spec);
314
+ // Add JSON Schema meta properties
315
+ const jsonSchema = {
316
+ '$schema': 'http://json-schema.org/draft-07/schema#',
317
+ title: `${sanitizeOperationId(operationId)} ${statusCode} Response`,
318
+ ...resolvedSchema
319
+ };
320
+ schemas.push({
321
+ operationId: sanitizeOperationId(operationId),
322
+ method: method.toUpperCase(),
323
+ path: pathStr,
324
+ statusCode,
325
+ schema: jsonSchema
326
+ });
327
+ }
328
+ }
329
+ }
330
+ }
331
+ return schemas;
332
+ }
333
+ /**
334
+ * Generate schema filename from operation details
335
+ */
336
+ function generateSchemaFilename(operationId, statusCode) {
337
+ return `${operationId}-${statusCode}.schema.json`;
338
+ }
339
+ //# sourceMappingURL=swaggerParser.js.map
@@ -0,0 +1,48 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ const assert = __importStar(require("assert"));
37
+ // You can import and use all API from the 'vscode' module
38
+ // as well as import your extension to test it
39
+ const vscode = __importStar(require("vscode"));
40
+ // import * as myExtension from '../../extension';
41
+ suite('Extension Test Suite', () => {
42
+ vscode.window.showInformationMessage('Start all tests.');
43
+ test('Sample test', () => {
44
+ assert.strictEqual(-1, [1, 2, 3].indexOf(5));
45
+ assert.strictEqual(-1, [1, 2, 3].indexOf(0));
46
+ });
47
+ });
48
+ //# sourceMappingURL=extension.test.js.map