1
0
mirror of https://github.com/lancedikson/bowser synced 2026-03-02 03:40:27 +00:00

Replace .github/copilot-instructions.md with AGENTS.md

This commit is contained in:
naorpeled
2026-02-13 22:30:26 +02:00
parent 8ddb4f5a0d
commit f11700291a
2 changed files with 147 additions and 98 deletions

View File

@@ -1,124 +1,84 @@
# Copilot Instructions for Bowser # Copilot Instructions for Bowser
## Project Overview
Bowser is a small, fast, and rich-API browser/platform/engine detector for both browser and Node.js environments. It's designed to parse User-Agent strings and provide detailed information about browsers, operating systems, platforms, and rendering engines.
## Architecture ## Architecture
### Core Components Bowser parses User-Agent strings through a pipeline of four independent parser modules, each exporting an ordered array of descriptors:
- **`src/bowser.js`**: Main entry point and public API. Provides static methods `getParser()` and `parse()`. - `src/parser-browsers.js``src/parser-os.js``src/parser-platforms.js``src/parser-engines.js`
- **`src/parser.js`**: Core parsing engine that orchestrates all parsers and returns structured results.
- **`src/parser-browsers.js`**: Browser detection logic using regex patterns.
- **`src/parser-os.js`**: Operating system detection logic.
- **`src/parser-platforms.js`**: Platform type detection (desktop, tablet, mobile).
- **`src/parser-engines.js`**: Rendering engine detection (WebKit, Blink, Gecko, etc.).
- **`src/constants.js`**: Centralized constants including browser aliases and mappings.
- **`src/utils.js`**: Utility functions for string matching and manipulation.
### Build Output `src/parser.js` (the `Parser` class) orchestrates these by iterating each descriptor list using `Utils.find()`, testing via regex arrays or custom `test(parser)` functions, then calling `describe(ua)` on the first match. **Descriptor order matters** — more specific browsers (e.g., Opera, Edge) must come before generic ones (e.g., Chrome, Safari) to avoid false matches.
- **`es5.js`**: ES5 transpiled version (default export). `src/bowser.js` is a stateless facade exposing `Bowser.getParser(UA)` and `Bowser.parse(UA)`. `src/constants.js` holds bidirectional maps (`BROWSER_ALIASES_MAP``BROWSER_MAP`) used for `satisfies()` alias lookups. `src/utils.js` provides regex helpers (`getFirstMatch`, `getSecondMatch`) and version comparison logic.
- **`bundled.js`**: ES5 version with babel-polyfill included.
## Development Workflow Also supports User-Agent Client Hints via an optional `clientHints` parameter on the constructor.
### Setup ## Commands
```bash - **Build**: `npm run build` (Webpack + Babel → `es5.js`, `bundled.js`)
npm install - **Test**: `npm test` (AVA — runs both `test/unit/` and `test/acceptance/`)
- **Lint**: `npm run lint:check` / `npm run lint:fix`
## Adding Browser/Bot Support (5 files to touch)
1. **`src/parser-browsers.js`**: Add a descriptor with `test` (regex array) and `describe(ua)` returning `{ name, version }`. Place it above any generic browser it might conflict with.
2. **`src/parser-platforms.js`**: Add a platform descriptor if the new entry is a bot (return `{ type: PLATFORMS_MAP.bot, vendor }`) or needs specific device detection.
3. **`src/constants.js`**: Add entries to both `BROWSER_ALIASES_MAP` (display name → alias) and `BROWSER_MAP` (alias → display name). Alias convention: lowercase, underscores for spaces/dashes, drop "browser" suffix (e.g., `'UC Browser'``'uc'`, `'Opera Coast'``'opera_coast'`).
4. **`test/acceptance/useragentstrings.yml`**: Add real UA strings under the browser's display name with full `spec` (browser, os, platform, engine). The acceptance test iterates this YAML and asserts `Bowser.parse(ua)` deep-equals the spec. The top-level key **must match** the browser's `name` field exactly.
5. **`index.d.ts`**: Update TypeScript definitions if adding new API surface.
Run `npm test` to validate — acceptance tests auto-generate from the YAML file.
## Descriptor Pattern
Every parser file exports an array of descriptors following this pattern:
```js
{
test: [/someregex/i], // array of RegExp, OR:
// test(parser) { return parser.test(/regex/); }, // function form for complex logic
describe(ua) {
return {
name: 'BrowserName',
version: Utils.getFirstMatch(/someregex\/(\d+(\.\d+)+)/i, ua),
};
},
}
``` ```
### Key Commands Engine descriptors in `src/parser-engines.js` may use `test(parser)` to access `parser.getBrowserName()` for browser-dependent engine detection (e.g., Edge → Blink vs EdgeHTML).
- **Build**: `npm run build` - Compiles source files using Webpack and Babel. ## `satisfies()` Version DSL
- **Test**: `npm test` - Runs unit and acceptance tests using AVA.
- **Lint**: `npm run lint:check` - Checks code style using ESLint.
- **Lint Fix**: `npm run lint:fix` - Auto-fixes linting issues.
- **Watch Mode**: `npm run watch` - Builds on file changes.
- **Test Watch**: `npm run test:watch` - Runs tests on file changes.
### Testing `Parser.satisfies(checkTree)` accepts a nested object for conditional browser/OS/platform checks. Keys in the check tree are resolved via `BROWSER_ALIASES_MAP` (so both `'chrome'` and `'Chrome'` work). Version operators: `>`, `>=`, `<`, `<=`, `=`, `~` (loose/prefix match). Values can be a single string or an array of version strings (OR logic).
- Tests are located in `test/acceptance/` and `test/unit/`. ```js
- Acceptance tests use real User-Agent strings from `test/acceptance/useragentstrings.yml`. // Simple browser check
- Always update `useragentstrings.yml` when adding browser support. parser.satisfies({ chrome: '>118', firefox: '~100' })
- Test framework: AVA with Babel integration.
## Coding Standards // Nested by OS or platform
parser.satisfies({ windows: { chrome: '>118' }, macos: { safari: '>=15' } })
parser.satisfies({ desktop: { chrome: '>118' }, mobile: { safari: '>=15' } })
```
### Style Guide Returns `true`/`false` if the browser matches, or `undefined` if the browser isn't mentioned in the tree. The `~` operator does a loose prefix comparison (e.g., `~100` matches `100.x.x`).
- **ESLint Config**: Based on Airbnb Base style guide. ## Code Conventions
- **Parser**: Uses `babel-eslint`.
- **Exceptions**:
- Underscore-dangle allowed for private properties.
- `no-void` disabled.
- ES6 imports must include `.js` extension.
### Naming Conventions - ES6 modules with **explicit `.js` extensions** in imports: `import Utils from './utils.js'`
- Airbnb ESLint base; underscore-prefixed private properties (`_ua`, `_hints`)
- Use `void (0)` instead of `undefined` for comparisons (legacy convention)
- JSDoc on all public methods with `@param`, `@return`, `@example`
- Browser versions are always **strings**, never numbers
- **Browser Aliases**: Use lowercase letters, replace spaces/dashes with underscores, drop "browser" suffix. ## Testing Details
- Examples: `Opera Coast``opera_coast`, `UC Browser``uc`, `SeaMonkey``seamonkey`.
- **Private Properties**: Prefix with underscore (e.g., `_ua`).
- **Constants**: Use `UPPER_SNAKE_CASE` for constant maps and aliases.
### Code Patterns - **Acceptance tests** (`test/acceptance/test-list-of-ua.js`): Loads `useragentstrings.yml`, tests every UA against both `src/bowser.js` (ES6) and `es5.js` (built) — so **build before running tests** if you changed source.
- **Unit tests** (`test/unit/`): Test individual Parser/Bowser/Utils methods. Use `new Parser(UA, true)` (skipParsing=true) for isolated method testing.
- Test framework: AVA with Babel transpilation via `@babel/register`.
- Use ES6 modules with explicit `.js` extensions. ## Branching
- Prefer static methods in Bowser class.
- Use class-based structure for Parser.
- Regex patterns should be well-documented and tested.
- Keep parsers modular and focused on single responsibility.
## Adding Browser Support - **`master`** is the production branch.
When adding support for a new browser:
1. Add regex pattern to `src/parser-browsers.js`.
2. Add browser name to `BROWSER_ALIASES_MAP` in `src/constants.js`.
3. Add corresponding entry to `BROWSER_MAP`.
4. Add test cases to `test/acceptance/useragentstrings.yml`.
5. Run tests to verify: `npm test`.
6. Check for duplicates before adding aliases.
## Branching Strategy
- **`master`**: Development branch.
- **`production`**: Production branch.
- **New Features**: Branch from `master`, PR back to `master`.
- **Hot-fixes/Browser Support**: Branch from `production`, PR back to `production`.
## Important Files
- **`index.d.ts`**: TypeScript definitions.
- **`.babelrc`**: Babel configuration for ES5 transpilation.
- **`webpack.config.js`**: Build configuration.
- **`.eslintrc.yml`**: Linting rules.
- **`package.json`**: Dependencies and scripts.
## API Design Principles
- Keep the API simple and intuitive.
- Bowser class should be stateless and provide factory methods.
- Parser class handles instance-specific logic.
- Results should be structured and predictable.
- Support both immediate parsing and lazy parsing.
## Performance Considerations
- Parsers use lazy evaluation where possible.
- Regex patterns are optimized for common browsers first.
- Optional `skipParsing` parameter for delayed parsing.
- Minimal bundle size is a priority (~4.8kB gzipped).
## Documentation
- Use JSDoc comments for all public APIs.
- Document parameters, return types, and provide examples.
- Update README.md for API changes.
- Generate docs with: `npm run generate-docs`.
## Common Pitfalls ## Common Pitfalls

89
AGENTS.md Normal file
View File

@@ -0,0 +1,89 @@
# Agents Instructions for Bowser
## Architecture
Bowser parses User-Agent strings through a pipeline of four independent parser modules, each exporting an ordered array of descriptors:
- `src/parser-browsers.js``src/parser-os.js``src/parser-platforms.js``src/parser-engines.js`
`src/parser.js` (the `Parser` class) orchestrates these by iterating each descriptor list using `Utils.find()`, testing via regex arrays or custom `test(parser)` functions, then calling `describe(ua)` on the first match. **Descriptor order matters** — more specific browsers (e.g., Opera, Edge) must come before generic ones (e.g., Chrome, Safari) to avoid false matches.
`src/bowser.js` is a stateless facade exposing `Bowser.getParser(UA)` and `Bowser.parse(UA)`. `src/constants.js` holds bidirectional maps (`BROWSER_ALIASES_MAP``BROWSER_MAP`) used for `satisfies()` alias lookups. `src/utils.js` provides regex helpers (`getFirstMatch`, `getSecondMatch`) and version comparison logic.
Also supports User-Agent Client Hints via an optional `clientHints` parameter on the constructor.
## Commands
- **Build**: `npm run build` (Webpack + Babel → `es5.js`, `bundled.js`)
- **Test**: `npm test` (AVA — runs both `test/unit/` and `test/acceptance/`)
- **Lint**: `npm run lint:check` / `npm run lint:fix`
## Adding Browser/Bot Support (5 files to touch)
1. **`src/parser-browsers.js`**: Add a descriptor with `test` (regex array) and `describe(ua)` returning `{ name, version }`. Place it above any generic browser it might conflict with.
2. **`src/parser-platforms.js`**: Add a platform descriptor if the new entry is a bot (return `{ type: PLATFORMS_MAP.bot, vendor }`) or needs specific device detection.
3. **`src/constants.js`**: Add entries to both `BROWSER_ALIASES_MAP` (display name → alias) and `BROWSER_MAP` (alias → display name). Alias convention: lowercase, underscores for spaces/dashes, drop "browser" suffix (e.g., `'UC Browser'``'uc'`, `'Opera Coast'``'opera_coast'`).
4. **`test/acceptance/useragentstrings.yml`**: Add real UA strings under the browser's display name with full `spec` (browser, os, platform, engine). The acceptance test iterates this YAML and asserts `Bowser.parse(ua)` deep-equals the spec. The top-level key **must match** the browser's `name` field exactly.
5. **`index.d.ts`**: Update TypeScript definitions if adding new API surface.
Run `npm test` to validate — acceptance tests auto-generate from the YAML file.
## Descriptor Pattern
Every parser file exports an array of descriptors following this pattern:
```js
{
test: [/someregex/i], // array of RegExp, OR:
// test(parser) { return parser.test(/regex/); }, // function form for complex logic
describe(ua) {
return {
name: 'BrowserName',
version: Utils.getFirstMatch(/someregex\/(\d+(\.\d+)+)/i, ua),
};
},
}
```
Engine descriptors in `src/parser-engines.js` may use `test(parser)` to access `parser.getBrowserName()` for browser-dependent engine detection (e.g., Edge → Blink vs EdgeHTML).
## `satisfies()` Version DSL
`Parser.satisfies(checkTree)` accepts a nested object for conditional browser/OS/platform checks. Keys in the check tree are resolved via `BROWSER_ALIASES_MAP` (so both `'chrome'` and `'Chrome'` work). Version operators: `>`, `>=`, `<`, `<=`, `=`, `~` (loose/prefix match). Values can be a single string or an array of version strings (OR logic).
```js
// Simple browser check
parser.satisfies({ chrome: '>118', firefox: '~100' })
// Nested by OS or platform
parser.satisfies({ windows: { chrome: '>118' }, macos: { safari: '>=15' } })
parser.satisfies({ desktop: { chrome: '>118' }, mobile: { safari: '>=15' } })
```
Returns `true`/`false` if the browser matches, or `undefined` if the browser isn't mentioned in the tree. The `~` operator does a loose prefix comparison (e.g., `~100` matches `100.x.x`).
## Code Conventions
- ES6 modules with **explicit `.js` extensions** in imports: `import Utils from './utils.js'`
- Airbnb ESLint base; underscore-prefixed private properties (`_ua`, `_hints`)
- Use `void (0)` instead of `undefined` for comparisons (legacy convention)
- JSDoc on all public methods with `@param`, `@return`, `@example`
- Browser versions are always **strings**, never numbers
## Testing Details
- **Acceptance tests** (`test/acceptance/test-list-of-ua.js`): Loads `useragentstrings.yml`, tests every UA against both `src/bowser.js` (ES6) and `es5.js` (built) — so **build before running tests** if you changed source.
- **Unit tests** (`test/unit/`): Test individual Parser/Bowser/Utils methods. Use `new Parser(UA, true)` (skipParsing=true) for isolated method testing.
- Test framework: AVA with Babel transpilation via `@babel/register`.
## Branching
- **`master`** is the production branch.
## Common Pitfalls
- Always check `BROWSER_ALIASES_MAP` for existing aliases before adding new ones.
- User-Agent strings can be complex; test edge cases thoroughly.
- Remember to update both the alias map and the reverse map in constants.
- Browser versions should be treated as strings, not numbers.
- Keep regex patterns readable with comments explaining their purpose.