ng-openapi 0.0.40 → 0.0.41-pr-8-feature-url-support-7d492ee.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 +191 -47
  2. package/index.d.ts +38 -32
  3. package/index.js +169 -34
  4. package/package.json +1 -1
package/cli.cjs CHANGED
@@ -33,14 +33,117 @@ 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, config) {
42
+ const generateClient = config.generateClientIf?.(spec) ?? true;
43
+ if (!generateClient) {
44
+ throw new Error("Client generation is disabled by configuration. Check your `generateClientIf` condition.");
45
+ }
46
+ this.spec = spec;
47
+ }
48
+ static async create(swaggerPathOrUrl, config) {
49
+ const swaggerContent = await _SwaggerParser.loadContent(swaggerPathOrUrl);
50
+ const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
51
+ return new _SwaggerParser(spec, config);
52
+ }
53
+ static async loadContent(pathOrUrl) {
54
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
55
+ return await _SwaggerParser.fetchUrlContent(pathOrUrl);
56
+ } else {
57
+ return fs.readFileSync(pathOrUrl, "utf8");
58
+ }
59
+ }
60
+ static isUrl(input) {
61
+ try {
62
+ new URL(input);
63
+ return true;
64
+ } catch {
65
+ return false;
66
+ }
67
+ }
68
+ static async fetchUrlContent(url) {
69
+ try {
70
+ const response = await fetch(url, {
71
+ method: "GET",
72
+ headers: {
73
+ "Accept": "application/json, application/yaml, text/yaml, text/plain, */*",
74
+ "User-Agent": "ng-openapi"
75
+ },
76
+ // 30 second timeout
77
+ signal: AbortSignal.timeout(3e4)
78
+ });
79
+ if (!response.ok) {
80
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
81
+ }
82
+ const content = await response.text();
83
+ if (!content || content.trim() === "") {
84
+ throw new Error(`Empty response from URL: ${url}`);
85
+ }
86
+ return content;
87
+ } catch (error) {
88
+ let errorMessage = `Failed to fetch content from URL: ${url}`;
89
+ if (error.name === "AbortError") {
90
+ errorMessage += " - Request timeout (30s)";
91
+ } else if (error.message) {
92
+ errorMessage += ` - ${error.message}`;
93
+ }
94
+ throw new Error(errorMessage);
95
+ }
96
+ }
97
+ static parseSpecContent(content, pathOrUrl) {
98
+ let format;
99
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
100
+ const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
101
+ if (urlPath.endsWith(".json")) {
102
+ format = "json";
103
+ } else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
104
+ format = "yaml";
105
+ } else {
106
+ format = _SwaggerParser.detectFormat(content);
107
+ }
108
+ } else {
109
+ const extension = path.extname(pathOrUrl).toLowerCase();
110
+ switch (extension) {
111
+ case ".json":
112
+ format = "json";
113
+ break;
114
+ case ".yaml":
115
+ format = "yaml";
116
+ break;
117
+ case ".yml":
118
+ format = "yml";
119
+ break;
120
+ default:
121
+ format = _SwaggerParser.detectFormat(content);
122
+ }
123
+ }
124
+ try {
125
+ switch (format) {
126
+ case "json":
127
+ return JSON.parse(content);
128
+ case "yaml":
129
+ case "yml":
130
+ return yaml.load(content);
131
+ default:
132
+ throw new Error(`Unable to determine format for: ${pathOrUrl}`);
133
+ }
134
+ } catch (error) {
135
+ throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
136
+ }
137
+ }
138
+ static detectFormat(content) {
139
+ const trimmed = content.trim();
140
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
141
+ return "json";
142
+ }
143
+ if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
144
+ return "yaml";
145
+ }
146
+ return "json";
44
147
  }
45
148
  getDefinitions() {
46
149
  return this.spec.definitions || this.spec.components?.schemas || {};
@@ -81,18 +184,6 @@ var SwaggerParser = class {
81
184
  }
82
185
  return null;
83
186
  }
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.`);
94
- }
95
- }
96
187
  };
97
188
 
98
189
  // src/lib/core/generator.ts
@@ -137,7 +228,7 @@ var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => def
137
228
  `, "BASE_INTERCEPTOR_HEADER_COMMENT");
138
229
 
139
230
  // src/lib/generators/type/type.generator.ts
140
- var TypeGenerator = class {
231
+ var TypeGenerator = class _TypeGenerator {
141
232
  static {
142
233
  __name(this, "TypeGenerator");
143
234
  }
@@ -146,7 +237,7 @@ var TypeGenerator = class {
146
237
  sourceFile;
147
238
  generatedTypes = /* @__PURE__ */ new Set();
148
239
  config;
149
- constructor(swaggerPath, outputRoot, config) {
240
+ constructor(parser, outputRoot, config) {
150
241
  this.config = config;
151
242
  const outputPath = outputRoot + "/models/index.ts";
152
243
  this.project = new import_ts_morph.Project({
@@ -158,15 +249,14 @@ var TypeGenerator = class {
158
249
  ...this.config.compilerOptions
159
250
  }
160
251
  });
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
- }
252
+ this.parser = parser;
253
+ this.sourceFile = this.project.createSourceFile(outputPath, "", {
254
+ overwrite: true
255
+ });
256
+ }
257
+ static async create(swaggerPathOrUrl, outputRoot, config) {
258
+ const parser = await SwaggerParser.create(swaggerPathOrUrl, config);
259
+ return new _TypeGenerator(parser, outputRoot, config);
170
260
  }
171
261
  generate() {
172
262
  try {
@@ -2008,7 +2098,7 @@ var ServiceMethodGenerator = class {
2008
2098
  };
2009
2099
 
2010
2100
  // src/lib/generators/service/service.generator.ts
2011
- var ServiceGenerator = class {
2101
+ var ServiceGenerator = class _ServiceGenerator {
2012
2102
  static {
2013
2103
  __name(this, "ServiceGenerator");
2014
2104
  }
@@ -2017,10 +2107,10 @@ var ServiceGenerator = class {
2017
2107
  spec;
2018
2108
  config;
2019
2109
  methodGenerator;
2020
- constructor(swaggerPath, project, config) {
2110
+ constructor(parser, project, config) {
2021
2111
  this.config = config;
2022
2112
  this.project = project;
2023
- this.parser = new SwaggerParser(swaggerPath);
2113
+ this.parser = parser;
2024
2114
  this.spec = this.parser.getSpec();
2025
2115
  if (!this.parser.isValidSpec()) {
2026
2116
  const versionInfo = this.parser.getSpecVersion();
@@ -2028,6 +2118,10 @@ var ServiceGenerator = class {
2028
2118
  }
2029
2119
  this.methodGenerator = new ServiceMethodGenerator(config);
2030
2120
  }
2121
+ static async create(swaggerPathOrUrl, project, config) {
2122
+ const parser = await SwaggerParser.create(swaggerPathOrUrl, config);
2123
+ return new _ServiceGenerator(parser, project, config);
2124
+ }
2031
2125
  generate(outputRoot) {
2032
2126
  const outputDir = path8.join(outputRoot, "services");
2033
2127
  const paths = this.extractPaths();
@@ -2335,12 +2429,36 @@ var ServiceIndexGenerator = class {
2335
2429
 
2336
2430
  // src/lib/core/generator.ts
2337
2431
  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}`);
2432
+ function isUrl(input) {
2433
+ try {
2434
+ new URL(input);
2435
+ return true;
2436
+ } catch {
2437
+ return false;
2438
+ }
2439
+ }
2440
+ __name(isUrl, "isUrl");
2441
+ function validateInput(input) {
2442
+ if (isUrl(input)) {
2443
+ const url = new URL(input);
2444
+ if (![
2445
+ "http:",
2446
+ "https:"
2447
+ ].includes(url.protocol)) {
2448
+ throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
2449
+ }
2450
+ } else {
2451
+ if (!fs3.existsSync(input)) {
2452
+ throw new Error(`Input file not found: ${input}`);
2453
+ }
2341
2454
  }
2455
+ }
2456
+ __name(validateInput, "validateInput");
2457
+ async function generateFromConfig(config) {
2458
+ validateInput(config.input);
2342
2459
  const outputPath = config.output;
2343
2460
  const generateServices = config.options.generateServices ?? true;
2461
+ const inputType = isUrl(config.input) ? "URL" : "file";
2344
2462
  if (!fs3.existsSync(outputPath)) {
2345
2463
  fs3.mkdirSync(outputPath, {
2346
2464
  recursive: true
@@ -2356,7 +2474,8 @@ async function generateFromConfig(config) {
2356
2474
  ...config.compilerOptions
2357
2475
  }
2358
2476
  });
2359
- const typeGenerator = new TypeGenerator(config.input, outputPath, config);
2477
+ console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
2478
+ const typeGenerator = await TypeGenerator.create(config.input, outputPath, config);
2360
2479
  typeGenerator.generate();
2361
2480
  console.log(`\u2705 TypeScript interfaces generated`);
2362
2481
  if (generateServices) {
@@ -2368,7 +2487,7 @@ async function generateFromConfig(config) {
2368
2487
  }
2369
2488
  const fileDownloadHelper = new FileDownloadGenerator(project);
2370
2489
  fileDownloadHelper.generate(outputPath);
2371
- const serviceGenerator = new ServiceGenerator(config.input, project, config);
2490
+ const serviceGenerator = await ServiceGenerator.create(config.input, project, config);
2372
2491
  serviceGenerator.generate(outputPath);
2373
2492
  const indexGenerator = new ServiceIndexGenerator(project);
2374
2493
  indexGenerator.generateIndex(outputPath);
@@ -2380,14 +2499,19 @@ async function generateFromConfig(config) {
2380
2499
  }
2381
2500
  const mainIndexGenerator = new MainIndexGenerator(project, config);
2382
2501
  mainIndexGenerator.generateMainIndex(outputPath);
2502
+ const sourceInfo = `from ${inputType}: ${config.input}`;
2383
2503
  if (config.clientName) {
2384
- console.log(`\u{1F389} ${config.clientName} Generation completed successfully at: ${outputPath}`);
2504
+ console.log(`\u{1F389} ${config.clientName} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2385
2505
  } else {
2386
- console.log("\u{1F389} Generation completed successfully at:", outputPath);
2506
+ console.log(`\u{1F389} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2387
2507
  }
2388
2508
  } catch (error) {
2389
2509
  if (error instanceof Error) {
2390
2510
  console.error("\u274C Error during generation:", error.message);
2511
+ if (error.message.includes("fetch") || error.message.includes("Failed to fetch")) {
2512
+ console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
2513
+ console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
2514
+ }
2391
2515
  } else {
2392
2516
  console.error("\u274C Unknown error during generation:", error);
2393
2517
  }
@@ -2397,7 +2521,7 @@ async function generateFromConfig(config) {
2397
2521
  __name(generateFromConfig, "generateFromConfig");
2398
2522
 
2399
2523
  // package.json
2400
- var version = "0.0.39";
2524
+ var version = "0.0.40";
2401
2525
 
2402
2526
  // src/lib/cli.ts
2403
2527
  var program = new import_commander.Command();
@@ -2422,7 +2546,26 @@ async function loadConfigFile(configPath) {
2422
2546
  }
2423
2547
  }
2424
2548
  __name(loadConfigFile, "loadConfigFile");
2425
- function validateInputFile(inputPath) {
2549
+ function isUrl2(input) {
2550
+ try {
2551
+ new URL(input);
2552
+ return true;
2553
+ } catch {
2554
+ return false;
2555
+ }
2556
+ }
2557
+ __name(isUrl2, "isUrl");
2558
+ function validateInput2(inputPath) {
2559
+ if (isUrl2(inputPath)) {
2560
+ const url = new URL(inputPath);
2561
+ if (![
2562
+ "http:",
2563
+ "https:"
2564
+ ].includes(url.protocol)) {
2565
+ throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
2566
+ }
2567
+ return;
2568
+ }
2426
2569
  if (!fs4.existsSync(inputPath)) {
2427
2570
  throw new Error(`Input file not found: ${inputPath}`);
2428
2571
  }
@@ -2436,18 +2579,17 @@ function validateInputFile(inputPath) {
2436
2579
  throw new Error(`Failed to parse ${extension || "specification"}. Supported formats are .json, .yaml, and .yml.`);
2437
2580
  }
2438
2581
  }
2439
- __name(validateInputFile, "validateInputFile");
2582
+ __name(validateInput2, "validateInput");
2440
2583
  async function generateFromOptions(options) {
2441
2584
  try {
2442
2585
  if (options.config) {
2443
2586
  const config = await loadConfigFile(options.config);
2444
- validateInputFile(path10.resolve(config.input));
2587
+ validateInput2(config.input);
2445
2588
  await generateFromConfig(config);
2446
2589
  } else if (options.input) {
2447
- const inputPath = path10.resolve(options.input);
2448
- validateInputFile(inputPath);
2590
+ validateInput2(options.input);
2449
2591
  const config = {
2450
- input: inputPath,
2592
+ input: options.input,
2451
2593
  output: options.output || "./src/generated",
2452
2594
  options: {
2453
2595
  dateType: options.dateType || "Date",
@@ -2469,10 +2611,10 @@ async function generateFromOptions(options) {
2469
2611
  }
2470
2612
  }
2471
2613
  __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) => {
2614
+ 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
2615
  await generateFromOptions(options);
2474
2616
  });
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) => {
2617
+ 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
2618
  await generateFromOptions(options);
2477
2619
  });
2478
2620
  program.on("--help", () => {
@@ -2482,8 +2624,10 @@ program.on("--help", () => {
2482
2624
  console.log(" $ ng-openapi -i ./swagger.json -o ./src/api");
2483
2625
  console.log(" $ ng-openapi -i ./openapi.yaml -o ./src/api");
2484
2626
  console.log(" $ ng-openapi -i ./api-spec.yml -o ./src/api");
2627
+ console.log(" $ ng-openapi -i https://api.example.com/openapi.json -o ./src/api");
2628
+ console.log(" $ ng-openapi -i https://petstore.swagger.io/v2/swagger.json -o ./src/api");
2485
2629
  console.log(" $ ng-openapi generate -c ./openapi.config.ts");
2486
- console.log(" $ ng-openapi generate -i ./api.yaml --types-only");
2630
+ console.log(" $ ng-openapi generate -i https://api.example.com/swagger.yaml --types-only");
2487
2631
  });
2488
2632
  program.parse();
2489
2633
  //# sourceMappingURL=cli.cjs.map
package/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { ParameterType, XML, ExternalDocs, Info, Path, BodyParameter, QueryParameter, Security, Tag } from 'swagger-schema-official';
2
1
  import { ScriptTarget, ModuleKind } from 'ts-morph';
3
2
  import { HttpInterceptor } from '@angular/common/http';
3
+ import { Info, ExternalDocs, Path, ParameterType, XML, BodyParameter, QueryParameter, Security, Tag } from 'swagger-schema-official';
4
4
 
5
5
  interface MethodGenerationContext {
6
6
  pathParams: Array<{
@@ -26,35 +26,6 @@ interface TypeSchema {
26
26
  [key: string]: any;
27
27
  }
28
28
 
29
- interface GeneratorConfig {
30
- input: string;
31
- output: string;
32
- clientName?: string;
33
- options: {
34
- dateType: "string" | "Date";
35
- enumStyle: "enum" | "union";
36
- generateServices?: boolean;
37
- generateEnumBasedOnDescription?: boolean;
38
- customHeaders?: Record<string, string>;
39
- responseTypeMapping?: {
40
- [contentType: string]: "json" | "blob" | "arraybuffer" | "text";
41
- };
42
- customizeMethodName?: (operationId: string) => string;
43
- };
44
- compilerOptions?: {
45
- declaration?: boolean;
46
- target?: ScriptTarget;
47
- module?: ModuleKind;
48
- strict?: boolean;
49
- };
50
- }
51
- interface NgOpenapiClientConfig {
52
- clientName: string;
53
- basePath: string;
54
- enableDateTransform?: boolean;
55
- interceptors?: (new (...args: HttpInterceptor[]) => HttpInterceptor)[];
56
- }
57
-
58
29
  interface Parameter {
59
30
  name: string;
60
31
  in: "query" | "path" | "header" | "cookie";
@@ -162,9 +133,45 @@ type EnumValueObject = {
162
133
  Value: number;
163
134
  };
164
135
 
136
+ interface GeneratorConfig {
137
+ input: string;
138
+ output: string;
139
+ clientName?: string;
140
+ generateClientIf?: (swaggerSpec: SwaggerSpec) => boolean;
141
+ options: {
142
+ dateType: "string" | "Date";
143
+ enumStyle: "enum" | "union";
144
+ generateServices?: boolean;
145
+ generateEnumBasedOnDescription?: boolean;
146
+ customHeaders?: Record<string, string>;
147
+ responseTypeMapping?: {
148
+ [contentType: string]: "json" | "blob" | "arraybuffer" | "text";
149
+ };
150
+ customizeMethodName?: (operationId: string) => string;
151
+ };
152
+ compilerOptions?: {
153
+ declaration?: boolean;
154
+ target?: ScriptTarget;
155
+ module?: ModuleKind;
156
+ strict?: boolean;
157
+ };
158
+ }
159
+ interface NgOpenapiClientConfig {
160
+ clientName: string;
161
+ basePath: string;
162
+ enableDateTransform?: boolean;
163
+ interceptors?: (new (...args: HttpInterceptor[]) => HttpInterceptor)[];
164
+ }
165
+
165
166
  declare class SwaggerParser {
166
167
  private readonly spec;
167
- constructor(swaggerPath: string);
168
+ private constructor();
169
+ static create(swaggerPathOrUrl: string, config: GeneratorConfig): Promise<SwaggerParser>;
170
+ private static loadContent;
171
+ private static isUrl;
172
+ private static fetchUrlContent;
173
+ private static parseSpecContent;
174
+ private static detectFormat;
168
175
  getDefinitions(): Record<string, SwaggerDefinition>;
169
176
  getDefinition(name: string): SwaggerDefinition | undefined;
170
177
  resolveReference(ref: string): SwaggerDefinition | undefined;
@@ -176,7 +183,6 @@ declare class SwaggerParser {
176
183
  type: "swagger" | "openapi";
177
184
  version: string;
178
185
  } | null;
179
- private parseSpecFile;
180
186
  }
181
187
 
182
188
  /**
package/index.js CHANGED
@@ -83,10 +83,120 @@ 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, config) {
87
87
  __publicField(this, "spec");
88
- const swaggerContent = fs.readFileSync(swaggerPath, "utf8");
89
- this.spec = this.parseSpecFile(swaggerContent, swaggerPath);
88
+ var _a, _b;
89
+ const generateClient = (_b = (_a = config.generateClientIf) == null ? void 0 : _a.call(config, spec)) != null ? _b : true;
90
+ if (!generateClient) {
91
+ throw new Error("Client generation is disabled by configuration. Check your `generateClientIf` condition.");
92
+ }
93
+ this.spec = spec;
94
+ }
95
+ static create(swaggerPathOrUrl, config) {
96
+ return __async(this, null, function* () {
97
+ const swaggerContent = yield _SwaggerParser.loadContent(swaggerPathOrUrl);
98
+ const spec = _SwaggerParser.parseSpecContent(swaggerContent, swaggerPathOrUrl);
99
+ return new _SwaggerParser(spec, config);
100
+ });
101
+ }
102
+ static loadContent(pathOrUrl) {
103
+ return __async(this, null, function* () {
104
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
105
+ return yield _SwaggerParser.fetchUrlContent(pathOrUrl);
106
+ } else {
107
+ return fs.readFileSync(pathOrUrl, "utf8");
108
+ }
109
+ });
110
+ }
111
+ static isUrl(input) {
112
+ try {
113
+ new URL(input);
114
+ return true;
115
+ } catch (e) {
116
+ return false;
117
+ }
118
+ }
119
+ static fetchUrlContent(url) {
120
+ return __async(this, null, function* () {
121
+ try {
122
+ const response = yield fetch(url, {
123
+ method: "GET",
124
+ headers: {
125
+ "Accept": "application/json, application/yaml, text/yaml, text/plain, */*",
126
+ "User-Agent": "ng-openapi"
127
+ },
128
+ // 30 second timeout
129
+ signal: AbortSignal.timeout(3e4)
130
+ });
131
+ if (!response.ok) {
132
+ throw new Error(`HTTP ${response.status}: ${response.statusText}`);
133
+ }
134
+ const content = yield response.text();
135
+ if (!content || content.trim() === "") {
136
+ throw new Error(`Empty response from URL: ${url}`);
137
+ }
138
+ return content;
139
+ } catch (error) {
140
+ let errorMessage = `Failed to fetch content from URL: ${url}`;
141
+ if (error.name === "AbortError") {
142
+ errorMessage += " - Request timeout (30s)";
143
+ } else if (error.message) {
144
+ errorMessage += ` - ${error.message}`;
145
+ }
146
+ throw new Error(errorMessage);
147
+ }
148
+ });
149
+ }
150
+ static parseSpecContent(content, pathOrUrl) {
151
+ let format;
152
+ if (_SwaggerParser.isUrl(pathOrUrl)) {
153
+ const urlPath = new URL(pathOrUrl).pathname.toLowerCase();
154
+ if (urlPath.endsWith(".json")) {
155
+ format = "json";
156
+ } else if (urlPath.endsWith(".yaml") || urlPath.endsWith(".yml")) {
157
+ format = "yaml";
158
+ } else {
159
+ format = _SwaggerParser.detectFormat(content);
160
+ }
161
+ } else {
162
+ const extension = path.extname(pathOrUrl).toLowerCase();
163
+ switch (extension) {
164
+ case ".json":
165
+ format = "json";
166
+ break;
167
+ case ".yaml":
168
+ format = "yaml";
169
+ break;
170
+ case ".yml":
171
+ format = "yml";
172
+ break;
173
+ default:
174
+ format = _SwaggerParser.detectFormat(content);
175
+ }
176
+ }
177
+ try {
178
+ switch (format) {
179
+ case "json":
180
+ return JSON.parse(content);
181
+ case "yaml":
182
+ case "yml":
183
+ return yaml.load(content);
184
+ default:
185
+ throw new Error(`Unable to determine format for: ${pathOrUrl}`);
186
+ }
187
+ } catch (error) {
188
+ throw new Error(`Failed to parse ${format.toUpperCase()} content from: ${pathOrUrl}. Error: ${error instanceof Error ? error.message : error}`);
189
+ }
190
+ }
191
+ static detectFormat(content) {
192
+ const trimmed = content.trim();
193
+ if (trimmed.startsWith("{") || trimmed.startsWith("[")) {
194
+ return "json";
195
+ }
196
+ if (trimmed.includes("openapi:") || trimmed.includes("swagger:") || trimmed.includes("---") || /^[a-zA-Z][a-zA-Z0-9_]*\s*:/.test(trimmed)) {
197
+ return "yaml";
198
+ }
199
+ return "json";
90
200
  }
91
201
  getDefinitions() {
92
202
  var _a;
@@ -128,18 +238,6 @@ var _SwaggerParser = class _SwaggerParser {
128
238
  }
129
239
  return null;
130
240
  }
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.`);
141
- }
142
- }
143
241
  };
144
242
  __name(_SwaggerParser, "SwaggerParser");
145
243
  var SwaggerParser = _SwaggerParser;
@@ -187,7 +285,7 @@ var BASE_INTERCEPTOR_HEADER_COMMENT = /* @__PURE__ */ __name((clientName) => def
187
285
 
188
286
  // src/lib/generators/type/type.generator.ts
189
287
  var _TypeGenerator = class _TypeGenerator {
190
- constructor(swaggerPath, outputRoot, config) {
288
+ constructor(parser, outputRoot, config) {
191
289
  __publicField(this, "project");
192
290
  __publicField(this, "parser");
193
291
  __publicField(this, "sourceFile");
@@ -203,15 +301,16 @@ var _TypeGenerator = class _TypeGenerator {
203
301
  strict: true
204
302
  }, this.config.compilerOptions)
205
303
  });
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
- }
304
+ this.parser = parser;
305
+ this.sourceFile = this.project.createSourceFile(outputPath, "", {
306
+ overwrite: true
307
+ });
308
+ }
309
+ static create(swaggerPathOrUrl, outputRoot, config) {
310
+ return __async(this, null, function* () {
311
+ const parser = yield SwaggerParser.create(swaggerPathOrUrl, config);
312
+ return new _TypeGenerator(parser, outputRoot, config);
313
+ });
215
314
  }
216
315
  generate() {
217
316
  try {
@@ -2063,7 +2162,7 @@ var ServiceMethodGenerator = _ServiceMethodGenerator;
2063
2162
 
2064
2163
  // src/lib/generators/service/service.generator.ts
2065
2164
  var _ServiceGenerator = class _ServiceGenerator {
2066
- constructor(swaggerPath, project, config) {
2165
+ constructor(parser, project, config) {
2067
2166
  __publicField(this, "project");
2068
2167
  __publicField(this, "parser");
2069
2168
  __publicField(this, "spec");
@@ -2071,7 +2170,7 @@ var _ServiceGenerator = class _ServiceGenerator {
2071
2170
  __publicField(this, "methodGenerator");
2072
2171
  this.config = config;
2073
2172
  this.project = project;
2074
- this.parser = new SwaggerParser(swaggerPath);
2173
+ this.parser = parser;
2075
2174
  this.spec = this.parser.getSpec();
2076
2175
  if (!this.parser.isValidSpec()) {
2077
2176
  const versionInfo = this.parser.getSpecVersion();
@@ -2079,6 +2178,12 @@ var _ServiceGenerator = class _ServiceGenerator {
2079
2178
  }
2080
2179
  this.methodGenerator = new ServiceMethodGenerator(config);
2081
2180
  }
2181
+ static create(swaggerPathOrUrl, project, config) {
2182
+ return __async(this, null, function* () {
2183
+ const parser = yield SwaggerParser.create(swaggerPathOrUrl, config);
2184
+ return new _ServiceGenerator(parser, project, config);
2185
+ });
2186
+ }
2082
2187
  generate(outputRoot) {
2083
2188
  const outputDir = path8.join(outputRoot, "services");
2084
2189
  const paths = this.extractPaths();
@@ -2388,14 +2493,38 @@ var ServiceIndexGenerator = _ServiceIndexGenerator;
2388
2493
 
2389
2494
  // src/lib/core/generator.ts
2390
2495
  var fs3 = __toESM(require("fs"));
2496
+ function isUrl(input) {
2497
+ try {
2498
+ new URL(input);
2499
+ return true;
2500
+ } catch (e) {
2501
+ return false;
2502
+ }
2503
+ }
2504
+ __name(isUrl, "isUrl");
2505
+ function validateInput(input) {
2506
+ if (isUrl(input)) {
2507
+ const url = new URL(input);
2508
+ if (![
2509
+ "http:",
2510
+ "https:"
2511
+ ].includes(url.protocol)) {
2512
+ throw new Error(`Unsupported URL protocol: ${url.protocol}. Only HTTP and HTTPS are supported.`);
2513
+ }
2514
+ } else {
2515
+ if (!fs3.existsSync(input)) {
2516
+ throw new Error(`Input file not found: ${input}`);
2517
+ }
2518
+ }
2519
+ }
2520
+ __name(validateInput, "validateInput");
2391
2521
  function generateFromConfig(config) {
2392
2522
  return __async(this, null, function* () {
2393
2523
  var _a;
2394
- if (!fs3.existsSync(config.input)) {
2395
- throw new Error(`Input file not found: ${config.input}`);
2396
- }
2524
+ validateInput(config.input);
2397
2525
  const outputPath = config.output;
2398
2526
  const generateServices = (_a = config.options.generateServices) != null ? _a : true;
2527
+ const inputType = isUrl(config.input) ? "URL" : "file";
2399
2528
  if (!fs3.existsSync(outputPath)) {
2400
2529
  fs3.mkdirSync(outputPath, {
2401
2530
  recursive: true
@@ -2410,7 +2539,8 @@ function generateFromConfig(config) {
2410
2539
  strict: true
2411
2540
  }, config.compilerOptions)
2412
2541
  });
2413
- const typeGenerator = new TypeGenerator(config.input, outputPath, config);
2542
+ console.log(`\u{1F4E1} Processing OpenAPI specification from ${inputType}: ${config.input}`);
2543
+ const typeGenerator = yield TypeGenerator.create(config.input, outputPath, config);
2414
2544
  typeGenerator.generate();
2415
2545
  console.log(`\u2705 TypeScript interfaces generated`);
2416
2546
  if (generateServices) {
@@ -2422,7 +2552,7 @@ function generateFromConfig(config) {
2422
2552
  }
2423
2553
  const fileDownloadHelper = new FileDownloadGenerator(project);
2424
2554
  fileDownloadHelper.generate(outputPath);
2425
- const serviceGenerator = new ServiceGenerator(config.input, project, config);
2555
+ const serviceGenerator = yield ServiceGenerator.create(config.input, project, config);
2426
2556
  serviceGenerator.generate(outputPath);
2427
2557
  const indexGenerator = new ServiceIndexGenerator(project);
2428
2558
  indexGenerator.generateIndex(outputPath);
@@ -2434,14 +2564,19 @@ function generateFromConfig(config) {
2434
2564
  }
2435
2565
  const mainIndexGenerator = new MainIndexGenerator(project, config);
2436
2566
  mainIndexGenerator.generateMainIndex(outputPath);
2567
+ const sourceInfo = `from ${inputType}: ${config.input}`;
2437
2568
  if (config.clientName) {
2438
- console.log(`\u{1F389} ${config.clientName} Generation completed successfully at: ${outputPath}`);
2569
+ console.log(`\u{1F389} ${config.clientName} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2439
2570
  } else {
2440
- console.log("\u{1F389} Generation completed successfully at:", outputPath);
2571
+ console.log(`\u{1F389} Generation completed successfully ${sourceInfo} -> ${outputPath}`);
2441
2572
  }
2442
2573
  } catch (error) {
2443
2574
  if (error instanceof Error) {
2444
2575
  console.error("\u274C Error during generation:", error.message);
2576
+ if (error.message.includes("fetch") || error.message.includes("Failed to fetch")) {
2577
+ console.error("\u{1F4A1} Tip: Make sure the URL is accessible and returns a valid OpenAPI/Swagger specification");
2578
+ console.error("\u{1F4A1} Alternative: Download the specification file locally and use the file path instead");
2579
+ }
2445
2580
  } else {
2446
2581
  console.error("\u274C Unknown error during generation:", error);
2447
2582
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ng-openapi",
3
- "version": "0.0.40",
3
+ "version": "0.0.41-pr-8-feature-url-support-7d492ee.0",
4
4
  "description": "Generate Angular services and TypeScript types from OpenAPI/Swagger specifications",
5
5
  "keywords": [
6
6
  "angular",