Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 41 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ async function convertCollection() {
const result = await convertBrunoCollectionToOpenAPI('./path-to-your-bruno-collection-directory');

// Save as JSON file
fs.writeFileSync('openapi-spec.json', JSON.stringify(result.openapi, null, 2));
fs.writeFileSync('openapi-spec.json', JSON.stringify(result.spec, null, 2));
console.log('OpenAPI specification saved as openapi-spec.json');
}

Expand All @@ -50,7 +50,45 @@ async function convertCollection() {
const result = await convertBrunoCollectionToOpenAPI('./path-to-your-bruno-collection-directory');

// Convert to YAML and save
const yamlString = yaml.dump(result.openapi, { indent: 2 });
const yamlString = yaml.dump(result.spec, { indent: 2 });
fs.writeFileSync('openapi-spec.yaml', yamlString);
console.log('OpenAPI specification saved as openapi-spec.yaml');
}

convertCollection();
```

### JSON Export (Sync)

```javascript
import { convertBrunoCollectionToOpenAPISync } from '@gyeonghokim/bruno-to-openapi';
import fs from 'fs';

function convertCollection() {
// Convert Bruno collection directory to OpenAPI specification synchronously
const result = convertBrunoCollectionToOpenAPISync('./path-to-your-bruno-collection-directory');

// Save as JSON file
fs.writeFileSync('openapi-spec.json', JSON.stringify(result.spec, null, 2));
console.log('OpenAPI specification saved as openapi-spec.json');
}

convertCollection();
```

### YAML Export (Sync)

```javascript
import { convertBrunoCollectionToOpenAPISync } from '@gyeonghokim/bruno-to-openapi';
import yaml from 'js-yaml';
import fs from 'fs';

function convertCollection() {
// Convert Bruno collection directory to OpenAPI specification synchronously
const result = convertBrunoCollectionToOpenAPISync('./path-to-your-bruno-collection-directory');

// Convert to YAML and save
const yamlString = yaml.dump(result.spec, { indent: 2 });
fs.writeFileSync('openapi-spec.yaml', yamlString);
console.log('OpenAPI specification saved as openapi-spec.yaml');
}
Expand All @@ -74,7 +112,7 @@ convertCollection();
The library exports the following functions:

- `convertBrunoCollectionToOpenAPI(collectionPath)`: Asynchronously takes a Bruno collection directory path and returns a Promise resolving to the conversion result containing the OpenAPI specification.
- `convertBrunoCollectionToAPISync(collectionPath)`: Synchronously takes a Bruno collection directory path and returns the conversion result containing the OpenAPI specification (Note: The full synchronous implementation has limitations and it's recommended to use the async version).
- `convertBrunoCollectionToOpenAPISync(collectionPath)`: Synchronously takes a Bruno collection directory path and returns the conversion result containing the OpenAPI specification (Note: The full synchronous implementation has limitations and it's recommended to use the async version).
- `isValidBrunoCollection(collectionPath)`: Asynchronously validates if a given path contains a valid Bruno collection and returns a Promise resolving to true if valid.

## Contributing
Expand Down
12 changes: 11 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,9 @@
"typescript": "^5.9.3",
"vitest": "^1.0.0"
},
"dependencies": {
"openapi-types": "^12.1.3"
},
"engines": {
"node": ">=18.0.0"
}
Expand Down
4 changes: 1 addition & 3 deletions src/models/bruno-collection.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,8 +61,6 @@ export class BrunoCollectionModel implements BrunoCollection {
* Generates a unique identifier
*/
private generateUid(): string {
// In a real implementation, you might want to use nanoid or similar
// For now, we'll create a simple UID based on timestamp and random number
return Date.now().toString(36) + Math.random().toString(36).substr(2, 5)
return crypto.randomUUID()
}
}
58 changes: 58 additions & 0 deletions src/models/errors.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/**
* Custom error classes for Bruno collection parsing
*/

/**
* Error thrown when a collection path is invalid (does not exist or is not a directory)
*/
export class InvalidCollectionPathError extends Error {
constructor(collectionPath: string) {
super(`Collection path does not exist or is not a directory: ${collectionPath}`)
this.name = 'InvalidCollectionPathError'
// Maintains proper stack trace for where our error was thrown (only available on V8)
if (Error.captureStackTrace) {
Error.captureStackTrace(this, InvalidCollectionPathError)
}
}
}

/**
* Error thrown when JSON parsing fails
*/
export class InvalidJsonError extends Error {
constructor(filePath: string, parseError: unknown) {
const message = parseError instanceof Error ? parseError.message : 'Unknown error'
super(`Invalid JSON in ${filePath}: ${message}`)
this.name = 'InvalidJsonError'
if (Error.captureStackTrace) {
Error.captureStackTrace(this, InvalidJsonError)
}
}
}

/**
* Error thrown when bruno.json exists but does not contain a valid JSON object
*/
export class InvalidBrunoJsonError extends Error {
constructor(filePath: string) {
super(`bruno.json must contain a valid JSON object: ${filePath}`)
this.name = 'InvalidBrunoJsonError'
if (Error.captureStackTrace) {
Error.captureStackTrace(this, InvalidBrunoJsonError)
}
}
}

/**
* Error thrown when a .bru file fails to parse
*/
export class BruFileParseError extends Error {
constructor(bruFilePath: string, cause?: unknown) {
const causeMessage = cause instanceof Error ? cause.message : String(cause)
super(`Failed to parse .bru file ${bruFilePath}: ${causeMessage}`)
this.name = 'BruFileParseError'
if (Error.captureStackTrace) {
Error.captureStackTrace(this, BruFileParseError)
}
}
}
Loading