ng-openapi 0.0.39 → 0.0.40-pr-8-feature-url-support.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.
Files changed (4) hide show
  1. package/cli.cjs +185 -45
  2. package/index.d.ts +7 -2
  3. package/index.js +162 -32
  4. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -33,14 +33,18 @@ var fs4 = __toESM(require("fs"));
33
33
  var fs = __toESM(require("fs"));
34
34
  var path = __toESM(require("path"));
35
35
  var yaml = __toESM(require("js-yaml"));
36
- var SwaggerParser = class {
36
+ var SwaggerParser = class _SwaggerParser {
37
37
  static {
38
38
  __name(this, "SwaggerParser");
39
39
  }
40
40
  spec;
41
- constructor(swaggerPath) {
42
- const swaggerContent = fs.readFileSync(swaggerPath, "utf8");
43
- this.spec = this.parseSpecFile(swaggerContent, swaggerPath);
41
+ constructor(spec) {
42
+ this.spec = spec;
43
+ }
44
+ static async create(swaggerPathOrUrl) {
45
+ const swaggerContent = await _SwaggerParser.loadContent(swaggerPathOrUrl);
46
+ const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
47
+ return new _SwaggerParser(spec);
44
48
  }
45
49
  getDefinitions() {
46
50
  return this.spec.definitions || this.spec.components?.schemas || {};
@@ -81,17 +85,100 @@ var SwaggerParser = class {
81
85
  }
82
86
  return null;
83
87
  }
84
- parseSpecFile(content, filePath) {
85
- const extension = path.extname(filePath).toLowerCase();
86
- switch (extension) {
87
- case ".json":
88
- return JSON.parse(content);
89
- case ".yaml":
90
- case ".yml":
91
- return yaml.load(content);
92
- default:
93
- throw new Error(`Failed to parse ${extension || "specification"} file: ${filePath}. Supported formats are .json, .yaml, and .yml.`);
88
+ static async loadContent(pathOrUrl) {
89
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
90
+ return await _SwaggerParser.fetchUrlContent(pathOrUrl);
91
+ } else {
92
+ return fs.readFileSync(pathOrUrl, "utf8");
93
+ }
94
+ }
95
+ static isUrl(input) {
96
+ try {
97
+ new URL(input);
98
+ return true;
99
+ } catch {
100
+ return false;
101
+ }
102
+ }
103
+ static async fetchUrlContent(url) {
104
+ try {
105
+ const response = await fetch(url, {
106
+ method: "GET",
107
+ headers: {
108
+ "Accept": "application/json, application/yaml, text/yaml, text/plain, */*",
109
+ "User-Agent": "ng-openapi"
110
+ },
111
+ // 30 second timeout
112
+ signal: AbortSignal.timeout(3e4)
113
+ });
114
+ if (!response.ok) {
115
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
116
+ }
117
+ const content = await response.text();
118
+ if (!content || content.trim() === "") {
119
+ throw new Error(`Empty response from URL: ${url}`);
120
+ }
121
+ return content;
122
+ } catch (error) {
123
+ let errorMessage = `Failed to fetch content from URL: ${url}`;
124
+ if (error.name === "AbortError") {
125
+ errorMessage += " - Request timeout (30s)";
126
+ } else if (error.message) {
127
+ errorMessage += ` - ${error.message}`;
128
+ }
129
+ throw new Error(errorMessage);
130
+ }
131
+ }
132
+ static parseSpecContent(content, pathOrUrl) {
133
+ let format;
134
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
135
+ const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
136
+ if (urlPath.endsWith(".json")) {
137
+ format = "json";
138
+ } else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
139
+ format = "yaml";
140
+ } else {
141
+ format = _SwaggerParser.detectFormat(content);
142
+ }
143
+ } else {
144
+ const extension = path.extname(pathOrUrl).toLowerCase();
145
+ switch (extension) {
146
+ case ".json":
147
+ format = "json";
148
+ break;
149
+ case ".yaml":
150
+ format = "yaml";
151
+ break;
152
+ case ".yml":
153
+ format = "yml";
154
+ break;
155
+ default:
156
+ format = _SwaggerParser.detectFormat(content);
157
+ }
158
+ }
159
+ try {
160
+ switch (format) {
161
+ case "json":
162
+ return JSON.parse(content);
163
+ case "yaml":
164
+ case "yml":
165
+ return yaml.load(content);
166
+ default:
167
+ throw new Error(`Unable to determine format for: ${pathOrUrl}`);
168
+ }
169
+ } catch (error) {
170
+ throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
171
+ }
172
+ }
173
+ static detectFormat(content) {
174
+ const trimmed = content.trim();
175
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
176
+ return "json";
94
177
  }
178
+ if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
179
+ return "yaml";
180
+ }
181
+ return "json";
95
182
  }
96
183
  };
97
184
 
@@ -137,7 +224,7 @@ var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => def
137
224
  `, "BASE_INTERCEPTOR_HEADER_COMMENT");
138
225
 
139
226
  // src/lib/generators/type/type.generator.ts
140
- var TypeGenerator = class {
227
+ var TypeGenerator = class _TypeGenerator {
141
228
  static {
142
229
  __name(this, "TypeGenerator");
143
230
  }
@@ -146,7 +233,7 @@ var TypeGenerator = class {
146
233
  sourceFile;
147
234
  generatedTypes = /* @__PURE__ */ new Set();
148
235
  config;
149
- constructor(swaggerPath, outputRoot, config) {
236
+ constructor(parser, outputRoot, config) {
150
237
  this.config = config;
151
238
  const outputPath = outputRoot + "/models/index.ts";
152
239
  this.project = new import_ts_morph.Project({
@@ -158,15 +245,14 @@ var TypeGenerator = class {
158
245
  ...this.config.compilerOptions
159
246
  }
160
247
  });
161
- try {
162
- this.parser = new SwaggerParser(swaggerPath);
163
- this.sourceFile = this.project.createSourceFile(outputPath, "", {
164
- overwrite: true
165
- });
166
- } catch (error) {
167
- console.error("Error initializing TypeGenerator:", error);
168
- throw error;
169
- }
248
+ this.parser = parser;
249
+ this.sourceFile = this.project.createSourceFile(outputPath, "", {
250
+ overwrite: true
251
+ });
252
+ }
253
+ static async create(swaggerPathOrUrl, outputRoot, config) {
254
+ const parser = await SwaggerParser.create(swaggerPathOrUrl);
255
+ return new _TypeGenerator(parser, outputRoot, config);
170
256
  }
171
257
  generate() {
172
258
  try {
@@ -2008,7 +2094,7 @@ var ServiceMethodGenerator = class {
2008
2094
  };
2009
2095
 
2010
2096
  // src/lib/generators/service/service.generator.ts
2011
- var ServiceGenerator = class {
2097
+ var ServiceGenerator = class _ServiceGenerator {
2012
2098
  static {
2013
2099
  __name(this, "ServiceGenerator");
2014
2100
  }
@@ -2017,10 +2103,10 @@ var ServiceGenerator = class {
2017
2103
  spec;
2018
2104
  config;
2019
2105
  methodGenerator;
2020
- constructor(swaggerPath, project, config) {
2106
+ constructor(parser, project, config) {
2021
2107
  this.config = config;
2022
2108
  this.project = project;
2023
- this.parser = new SwaggerParser(swaggerPath);
2109
+ this.parser = parser;
2024
2110
  this.spec = this.parser.getSpec();
2025
2111
  if (!this.parser.isValidSpec()) {
2026
2112
  const versionInfo = this.parser.getSpecVersion();
@@ -2028,6 +2114,10 @@ var ServiceGenerator = class {
2028
2114
  }
2029
2115
  this.methodGenerator = new ServiceMethodGenerator(config);
2030
2116
  }
2117
+ static async create(swaggerPathOrUrl, project, config) {
2118
+ const parser = await SwaggerParser.create(swaggerPathOrUrl);
2119
+ return new _ServiceGenerator(parser, project, config);
2120
+ }
2031
2121
  generate(outputRoot) {
2032
2122
  const outputDir = path8.join(outputRoot, "services");
2033
2123
  const paths = this.extractPaths();
@@ -2335,12 +2425,36 @@ var ServiceIndexGenerator = class {
2335
2425
 
2336
2426
  // src/lib/core/generator.ts
2337
2427
  var fs3 = __toESM(require("fs"));
2338
- async function generateFromConfig(config) {
2339
- if (!fs3.existsSync(config.input)) {
2340
- throw new Error(`Input file not found: ${config.input}`);
2428
+ function isUrl(input) {
2429
+ try {
2430
+ new URL(input);
2431
+ return true;
2432
+ } catch {
2433
+ return false;
2341
2434
  }
2435
+ }
2436
+ __name(isUrl, "isUrl");
2437
+ function validateInput(input) {
2438
+ if (isUrl(input)) {
2439
+ const url = new URL(input);
2440
+ if (![
2441
+ "http:",
2442
+ "https:"
2443
+ ].includes(url.protocol)) {
2444
+ throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
2445
+ }
2446
+ } else {
2447
+ if (!fs3.existsSync(input)) {
2448
+ throw new Error(`Input file not found: ${input}`);
2449
+ }
2450
+ }
2451
+ }
2452
+ __name(validateInput, "validateInput");
2453
+ async function generateFromConfig(config) {
2454
+ validateInput(config.input);
2342
2455
  const outputPath = config.output;
2343
2456
  const generateServices = config.options.generateServices ?? true;
2457
+ const inputType = isUrl(config.input) ? "URL" : "file";
2344
2458
  if (!fs3.existsSync(outputPath)) {
2345
2459
  fs3.mkdirSync(outputPath, {
2346
2460
  recursive: true
@@ -2356,7 +2470,8 @@ async function generateFromConfig(config) {
2356
2470
  ...config.compilerOptions
2357
2471
  }
2358
2472
  });
2359
- const typeGenerator = new TypeGenerator(config.input, outputPath, config);
2473
+ console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
2474
+ const typeGenerator = await TypeGenerator.create(config.input, outputPath, config);
2360
2475
  typeGenerator.generate();
2361
2476
  console.log(`\u2705 TypeScript interfaces generated`);
2362
2477
  if (generateServices) {
@@ -2368,7 +2483,7 @@ async function generateFromConfig(config) {
2368
2483
  }
2369
2484
  const fileDownloadHelper = new FileDownloadGenerator(project);
2370
2485
  fileDownloadHelper.generate(outputPath);
2371
- const serviceGenerator = new ServiceGenerator(config.input, project, config);
2486
+ const serviceGenerator = await ServiceGenerator.create(config.input, project, config);
2372
2487
  serviceGenerator.generate(outputPath);
2373
2488
  const indexGenerator = new ServiceIndexGenerator(project);
2374
2489
  indexGenerator.generateIndex(outputPath);
@@ -2380,14 +2495,19 @@ async function generateFromConfig(config) {
2380
2495
  }
2381
2496
  const mainIndexGenerator = new MainIndexGenerator(project, config);
2382
2497
  mainIndexGenerator.generateMainIndex(outputPath);
2498
+ const sourceInfo = `from ${inputType}: ${config.input}`;
2383
2499
  if (config.clientName) {
2384
- console.log(`\u{1F389} ${config.clientName} Generation completed successfully at: ${outputPath}`);
2500
+ console.log(`\u{1F389} ${config.clientName} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2385
2501
  } else {
2386
- console.log("\u{1F389} Generation completed successfully at:", outputPath);
2502
+ console.log(`\u{1F389} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2387
2503
  }
2388
2504
  } catch (error) {
2389
2505
  if (error instanceof Error) {
2390
2506
  console.error("\u274C Error during generation:", error.message);
2507
+ if (error.message.includes("fetch") || error.message.includes("Failed to fetch")) {
2508
+ console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
2509
+ console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
2510
+ }
2391
2511
  } else {
2392
2512
  console.error("\u274C Unknown error during generation:", error);
2393
2513
  }
@@ -2397,7 +2517,7 @@ async function generateFromConfig(config) {
2397
2517
  __name(generateFromConfig, "generateFromConfig");
2398
2518
 
2399
2519
  // package.json
2400
- var version = "0.0.38";
2520
+ var version = "0.0.39";
2401
2521
 
2402
2522
  // src/lib/cli.ts
2403
2523
  var program = new import_commander.Command();
@@ -2422,7 +2542,26 @@ async function loadConfigFile(configPath) {
2422
2542
  }
2423
2543
  }
2424
2544
  __name(loadConfigFile, "loadConfigFile");
2425
- function validateInputFile(inputPath) {
2545
+ function isUrl2(input) {
2546
+ try {
2547
+ new URL(input);
2548
+ return true;
2549
+ } catch {
2550
+ return false;
2551
+ }
2552
+ }
2553
+ __name(isUrl2, "isUrl");
2554
+ function validateInput2(inputPath) {
2555
+ if (isUrl2(inputPath)) {
2556
+ const url = new URL(inputPath);
2557
+ if (![
2558
+ "http:",
2559
+ "https:"
2560
+ ].includes(url.protocol)) {
2561
+ throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
2562
+ }
2563
+ return;
2564
+ }
2426
2565
  if (!fs4.existsSync(inputPath)) {
2427
2566
  throw new Error(`Input file not found: ${inputPath}`);
2428
2567
  }
@@ -2436,18 +2575,17 @@ function validateInputFile(inputPath) {
2436
2575
  throw new Error(`Failed to parse ${extension || "specification"}. Supported formats are .json, .yaml, and .yml.`);
2437
2576
  }
2438
2577
  }
2439
- __name(validateInputFile, "validateInputFile");
2578
+ __name(validateInput2, "validateInput");
2440
2579
  async function generateFromOptions(options) {
2441
2580
  try {
2442
2581
  if (options.config) {
2443
2582
  const config = await loadConfigFile(options.config);
2444
- validateInputFile(path10.resolve(config.input));
2583
+ validateInput2(config.input);
2445
2584
  await generateFromConfig(config);
2446
2585
  } else if (options.input) {
2447
- const inputPath = path10.resolve(options.input);
2448
- validateInputFile(inputPath);
2586
+ validateInput2(options.input);
2449
2587
  const config = {
2450
- input: inputPath,
2588
+ input: options.input,
2451
2589
  output: options.output || "./src/generated",
2452
2590
  options: {
2453
2591
  dateType: options.dateType || "Date",
@@ -2469,10 +2607,10 @@ async function generateFromOptions(options) {
2469
2607
  }
2470
2608
  }
2471
2609
  __name(generateFromOptions, "generateFromOptions");
2472
- program.name("ng-openapi").description("Generate Angular services and types from OpenAPI/Swagger specifications (JSON, YAML, YML)").version(version).option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path to OpenAPI/Swagger specification file (.json, .yaml, .yml)").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
2610
+ program.name("ng-openapi").description("Generate Angular services and types from OpenAPI/Swagger specifications (JSON, YAML, YML) from files or URLs").version(version).option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path or URL to OpenAPI/Swagger specification (.json, .yaml, .yml)").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
2473
2611
  await generateFromOptions(options);
2474
2612
  });
2475
- program.command("generate").alias("gen").description("Generate code from OpenAPI/Swagger specification").option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path to OpenAPI/Swagger specification file (.json, .yaml, .yml)").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
2613
+ program.command("generate").alias("gen").description("Generate code from OpenAPI/Swagger specification").option("-c, --config <path>", "Path to configuration file").option("-i, --input <path>", "Path or URL to OpenAPI/Swagger specification (.json, .yaml, .yml)").option("-o, --output <path>", "Output directory", "./src/generated").option("--types-only", "Generate only TypeScript interfaces").option("--date-type <type>", "Date type to use (string | Date)", "Date").action(async (options) => {
2476
2614
  await generateFromOptions(options);
2477
2615
  });
2478
2616
  program.on("--help", () => {
@@ -2482,8 +2620,10 @@ program.on("--help", () => {
2482
2620
  console.log(" $ ng-openapi -i ./swagger.json -o ./src/api");
2483
2621
  console.log(" $ ng-openapi -i ./openapi.yaml -o ./src/api");
2484
2622
  console.log(" $ ng-openapi -i ./api-spec.yml -o ./src/api");
2623
+ console.log(" $ ng-openapi -i https://api.example.com/openapi.json -o ./src/api");
2624
+ console.log(" $ ng-openapi -i https://petstore.swagger.io/v2/swagger.json -o ./src/api");
2485
2625
  console.log(" $ ng-openapi generate -c ./openapi.config.ts");
2486
- console.log(" $ ng-openapi generate -i ./api.yaml --types-only");
2626
+ console.log(" $ ng-openapi generate -i https://api.example.com/swagger.yaml --types-only");
2487
2627
  });
2488
2628
  program.parse();
2489
2629
  //# sourceMappingURL=cli.cjs.map
package/index.d.ts CHANGED
@@ -164,7 +164,8 @@ type EnumValueObject = {
164
164
 
165
165
  declare class SwaggerParser {
166
166
  private readonly spec;
167
- constructor(swaggerPath: string);
167
+ private constructor();
168
+ static create(swaggerPathOrUrl: string): Promise<SwaggerParser>;
168
169
  getDefinitions(): Record<string, SwaggerDefinition>;
169
170
  getDefinition(name: string): SwaggerDefinition | undefined;
170
171
  resolveReference(ref: string): SwaggerDefinition | undefined;
@@ -176,7 +177,11 @@ declare class SwaggerParser {
176
177
  type: "swagger" | "openapi";
177
178
  version: string;
178
179
  } | null;
179
- private parseSpecFile;
180
+ private static loadContent;
181
+ private static isUrl;
182
+ private static fetchUrlContent;
183
+ private static parseSpecContent;
184
+ private static detectFormat;
180
185
  }
181
186
 
182
187
  /**
package/index.js CHANGED
@@ -83,10 +83,16 @@ var fs = __toESM(require("fs"));
83
83
  var path = __toESM(require("path"));
84
84
  var yaml = __toESM(require("js-yaml"));
85
85
  var _SwaggerParser = class _SwaggerParser {
86
- constructor(swaggerPath) {
86
+ constructor(spec) {
87
87
  __publicField(this, "spec");
88
- const swaggerContent = fs.readFileSync(swaggerPath, "utf8");
89
- this.spec = this.parseSpecFile(swaggerContent, swaggerPath);
88
+ this.spec = spec;
89
+ }
90
+ static create(swaggerPathOrUrl) {
91
+ return __async(this, null, function* () {
92
+ const swaggerContent = yield _SwaggerParser.loadContent(swaggerPathOrUrl);
93
+ const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
94
+ return new _SwaggerParser(spec);
95
+ });
90
96
  }
91
97
  getDefinitions() {
92
98
  var _a;
@@ -128,18 +134,105 @@ var _SwaggerParser = class _SwaggerParser {
128
134
  }
129
135
  return null;
130
136
  }
131
- parseSpecFile(content, filePath) {
132
- const extension = path.extname(filePath).toLowerCase();
133
- switch (extension) {
134
- case ".json":
135
- return JSON.parse(content);
136
- case ".yaml":
137
- case ".yml":
138
- return yaml.load(content);
139
- default:
140
- throw new Error(`Failed to parse ${extension || "specification"} file: ${filePath}. Supported formats are .json, .yaml, and .yml.`);
137
+ static loadContent(pathOrUrl) {
138
+ return __async(this, null, function* () {
139
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
140
+ return yield _SwaggerParser.fetchUrlContent(pathOrUrl);
141
+ } else {
142
+ return fs.readFileSync(pathOrUrl, "utf8");
143
+ }
144
+ });
145
+ }
146
+ static isUrl(input) {
147
+ try {
148
+ new URL(input);
149
+ return true;
150
+ } catch (e) {
151
+ return false;
152
+ }
153
+ }
154
+ static fetchUrlContent(url) {
155
+ return __async(this, null, function* () {
156
+ try {
157
+ const response = yield fetch(url, {
158
+ method: "GET",
159
+ headers: {
160
+ "Accept": "application/json, application/yaml, text/yaml, text/plain, */*",
161
+ "User-Agent": "ng-openapi"
162
+ },
163
+ // 30 second timeout
164
+ signal: AbortSignal.timeout(3e4)
165
+ });
166
+ if (!response.ok) {
167
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
168
+ }
169
+ const content = yield response.text();
170
+ if (!content || content.trim() === "") {
171
+ throw new Error(`Empty response from URL: ${url}`);
172
+ }
173
+ return content;
174
+ } catch (error) {
175
+ let errorMessage = `Failed to fetch content from URL: ${url}`;
176
+ if (error.name === "AbortError") {
177
+ errorMessage += " - Request timeout (30s)";
178
+ } else if (error.message) {
179
+ errorMessage += ` - ${error.message}`;
180
+ }
181
+ throw new Error(errorMessage);
182
+ }
183
+ });
184
+ }
185
+ static parseSpecContent(content, pathOrUrl) {
186
+ let format;
187
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
188
+ const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
189
+ if (urlPath.endsWith(".json")) {
190
+ format = "json";
191
+ } else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
192
+ format = "yaml";
193
+ } else {
194
+ format = _SwaggerParser.detectFormat(content);
195
+ }
196
+ } else {
197
+ const extension = path.extname(pathOrUrl).toLowerCase();
198
+ switch (extension) {
199
+ case ".json":
200
+ format = "json";
201
+ break;
202
+ case ".yaml":
203
+ format = "yaml";
204
+ break;
205
+ case ".yml":
206
+ format = "yml";
207
+ break;
208
+ default:
209
+ format = _SwaggerParser.detectFormat(content);
210
+ }
211
+ }
212
+ try {
213
+ switch (format) {
214
+ case "json":
215
+ return JSON.parse(content);
216
+ case "yaml":
217
+ case "yml":
218
+ return yaml.load(content);
219
+ default:
220
+ throw new Error(`Unable to determine format for: ${pathOrUrl}`);
221
+ }
222
+ } catch (error) {
223
+ throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
141
224
  }
142
225
  }
226
+ static detectFormat(content) {
227
+ const trimmed = content.trim();
228
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
229
+ return "json";
230
+ }
231
+ if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
232
+ return "yaml";
233
+ }
234
+ return "json";
235
+ }
143
236
  };
144
237
  __name(_SwaggerParser, "SwaggerParser");
145
238
  var SwaggerParser = _SwaggerParser;
@@ -187,7 +280,7 @@ var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => def
187
280
 
188
281
  // src/lib/generators/type/type.generator.ts
189
282
  var _TypeGenerator = class _TypeGenerator {
190
- constructor(swaggerPath, outputRoot, config) {
283
+ constructor(parser, outputRoot, config) {
191
284
  __publicField(this, "project");
192
285
  __publicField(this, "parser");
193
286
  __publicField(this, "sourceFile");
@@ -203,15 +296,16 @@ var _TypeGenerator = class _TypeGenerator {
203
296
  strict: true
204
297
  }, this.config.compilerOptions)
205
298
  });
206
- try {
207
- this.parser = new SwaggerParser(swaggerPath);
208
- this.sourceFile = this.project.createSourceFile(outputPath, "", {
209
- overwrite: true
210
- });
211
- } catch (error) {
212
- console.error("Error initializing TypeGenerator:", error);
213
- throw error;
214
- }
299
+ this.parser = parser;
300
+ this.sourceFile = this.project.createSourceFile(outputPath, "", {
301
+ overwrite: true
302
+ });
303
+ }
304
+ static create(swaggerPathOrUrl, outputRoot, config) {
305
+ return __async(this, null, function* () {
306
+ const parser = yield SwaggerParser.create(swaggerPathOrUrl);
307
+ return new _TypeGenerator(parser, outputRoot, config);
308
+ });
215
309
  }
216
310
  generate() {
217
311
  try {
@@ -2063,7 +2157,7 @@ var ServiceMethodGenerator = _ServiceMethodGenerator;
2063
2157
 
2064
2158
  // src/lib/generators/service/service.generator.ts
2065
2159
  var _ServiceGenerator = class _ServiceGenerator {
2066
- constructor(swaggerPath, project, config) {
2160
+ constructor(parser, project, config) {
2067
2161
  __publicField(this, "project");
2068
2162
  __publicField(this, "parser");
2069
2163
  __publicField(this, "spec");
@@ -2071,7 +2165,7 @@ var _ServiceGenerator = class _ServiceGenerator {
2071
2165
  __publicField(this, "methodGenerator");
2072
2166
  this.config = config;
2073
2167
  this.project = project;
2074
- this.parser = new SwaggerParser(swaggerPath);
2168
+ this.parser = parser;
2075
2169
  this.spec = this.parser.getSpec();
2076
2170
  if (!this.parser.isValidSpec()) {
2077
2171
  const versionInfo = this.parser.getSpecVersion();
@@ -2079,6 +2173,12 @@ var _ServiceGenerator = class _ServiceGenerator {
2079
2173
  }
2080
2174
  this.methodGenerator = new ServiceMethodGenerator(config);
2081
2175
  }
2176
+ static create(swaggerPathOrUrl, project, config) {
2177
+ return __async(this, null, function* () {
2178
+ const parser = yield SwaggerParser.create(swaggerPathOrUrl);
2179
+ return new _ServiceGenerator(parser, project, config);
2180
+ });
2181
+ }
2082
2182
  generate(outputRoot) {
2083
2183
  const outputDir = path8.join(outputRoot, "services");
2084
2184
  const paths = this.extractPaths();
@@ -2388,14 +2488,38 @@ var ServiceIndexGenerator = _ServiceIndexGenerator;
2388
2488
 
2389
2489
  // src/lib/core/generator.ts
2390
2490
  var fs3 = __toESM(require("fs"));
2491
+ function isUrl(input) {
2492
+ try {
2493
+ new URL(input);
2494
+ return true;
2495
+ } catch (e) {
2496
+ return false;
2497
+ }
2498
+ }
2499
+ __name(isUrl, "isUrl");
2500
+ function validateInput(input) {
2501
+ if (isUrl(input)) {
2502
+ const url = new URL(input);
2503
+ if (![
2504
+ "http:",
2505
+ "https:"
2506
+ ].includes(url.protocol)) {
2507
+ throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
2508
+ }
2509
+ } else {
2510
+ if (!fs3.existsSync(input)) {
2511
+ throw new Error(`Input file not found: ${input}`);
2512
+ }
2513
+ }
2514
+ }
2515
+ __name(validateInput, "validateInput");
2391
2516
  function generateFromConfig(config) {
2392
2517
  return __async(this, null, function* () {
2393
2518
  var _a;
2394
- if (!fs3.existsSync(config.input)) {
2395
- throw new Error(`Input file not found: ${config.input}`);
2396
- }
2519
+ validateInput(config.input);
2397
2520
  const outputPath = config.output;
2398
2521
  const generateServices = (_a = config.options.generateServices) != null ? _a : true;
2522
+ const inputType = isUrl(config.input) ? "URL" : "file";
2399
2523
  if (!fs3.existsSync(outputPath)) {
2400
2524
  fs3.mkdirSync(outputPath, {
2401
2525
  recursive: true
@@ -2410,7 +2534,8 @@ function generateFromConfig(config) {
2410
2534
  strict: true
2411
2535
  }, config.compilerOptions)
2412
2536
  });
2413
- const typeGenerator = new TypeGenerator(config.input, outputPath, config);
2537
+ console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
2538
+ const typeGenerator = yield TypeGenerator.create(config.input, outputPath, config);
2414
2539
  typeGenerator.generate();
2415
2540
  console.log(`\u2705 TypeScript interfaces generated`);
2416
2541
  if (generateServices) {
@@ -2422,7 +2547,7 @@ function generateFromConfig(config) {
2422
2547
  }
2423
2548
  const fileDownloadHelper = new FileDownloadGenerator(project);
2424
2549
  fileDownloadHelper.generate(outputPath);
2425
- const serviceGenerator = new ServiceGenerator(config.input, project, config);
2550
+ const serviceGenerator = yield ServiceGenerator.create(config.input, project, config);
2426
2551
  serviceGenerator.generate(outputPath);
2427
2552
  const indexGenerator = new ServiceIndexGenerator(project);
2428
2553
  indexGenerator.generateIndex(outputPath);
@@ -2434,14 +2559,19 @@ function generateFromConfig(config) {
2434
2559
  }
2435
2560
  const mainIndexGenerator = new MainIndexGenerator(project, config);
2436
2561
  mainIndexGenerator.generateMainIndex(outputPath);
2562
+ const sourceInfo = `from ${inputType}: ${config.input}`;
2437
2563
  if (config.clientName) {
2438
- console.log(`\u{1F389} ${config.clientName} Generation completed successfully at: ${outputPath}`);
2564
+ console.log(`\u{1F389} ${config.clientName} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2439
2565
  } else {
2440
- console.log("\u{1F389} Generation completed successfully at:", outputPath);
2566
+ console.log(`\u{1F389} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2441
2567
  }
2442
2568
  } catch (error) {
2443
2569
  if (error instanceof Error) {
2444
2570
  console.error("\u274C Error during generation:", error.message);
2571
+ if (error.message.includes("fetch") || error.message.includes("Failed to fetch")) {
2572
+ console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
2573
+ console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
2574
+ }
2445
2575
  } else {
2446
2576
  console.error("\u274C Unknown error during generation:", error);
2447
2577
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-openapi",
3
- "version": "0.0.39",
3
+ "version": "0.0.40-pr-8-feature-url-support.0",
4
4
  "description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
5
5
  "keywords": [
6
6
  "angular",