|
| 1 | +# Agent Guidelines for Bark EdgeOne |
| 2 | + |
| 3 | +This document provides coding guidelines and conventions for AI agents working on the Bark EdgeOne codebase. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +Bark EdgeOne is a push notification server implementation for EdgeOne Pages Edge Functions, providing APNs integration with global edge deployment. |
| 8 | + |
| 9 | +**Tech Stack:** TypeScript, EdgeOne Pages, Vitest, ESLint, Prettier |
| 10 | + |
| 11 | +## Build, Test, and Lint Commands |
| 12 | + |
| 13 | +### Build |
| 14 | +```bash |
| 15 | +npm run build # Compile TypeScript to dist/ |
| 16 | +npm run type-check # Type check without emitting files |
| 17 | +``` |
| 18 | + |
| 19 | +### Testing |
| 20 | +```bash |
| 21 | +npm test # Run all tests once |
| 22 | +npm run test:watch # Run tests in watch mode |
| 23 | +npm run test:coverage # Run tests with coverage report |
| 24 | + |
| 25 | +# Run a single test file |
| 26 | +npx vitest run src/utils/__tests__/string.test.ts |
| 27 | + |
| 28 | +# Run tests matching a pattern |
| 29 | +npx vitest run -t "isEmpty" |
| 30 | +``` |
| 31 | + |
| 32 | +### Linting and Formatting |
| 33 | +```bash |
| 34 | +npm run lint # Check for linting errors |
| 35 | +npm run lint:fix # Auto-fix linting errors |
| 36 | +npm run format # Format all files |
| 37 | +npm run format:check # Check formatting without changes |
| 38 | +``` |
| 39 | + |
| 40 | +### Local Development |
| 41 | +```bash |
| 42 | +npx edgeone pages dev # Start local dev server on http://localhost:8788 |
| 43 | +``` |
| 44 | + |
| 45 | +### Deploy to EdgeOne Pages |
| 46 | +```bash |
| 47 | +npx edgeone pages deploy # Start local dev server on http://localhost:8788 |
| 48 | + |
| 49 | +## Project Structure |
| 50 | + |
| 51 | +``` |
| 52 | +bark-edgeone/ |
| 53 | +├── src/ # Core source code |
| 54 | +│ ├── apns/ # APNs integration (JWT, payload, push) |
| 55 | +│ ├── handlers/ # Business logic handlers |
| 56 | +│ ├── types/ # TypeScript type definitions |
| 57 | +│ └── utils/ # Utility functions |
| 58 | +├── edge-functions/ # EdgeOne edge function endpoints |
| 59 | +│ └── api/ # API route handlers |
| 60 | +├── node-functions/ # Node.js serverless functions |
| 61 | +├── dist/ # Compiled output |
| 62 | +└── __tests__/ # Test files (co-located with source) |
| 63 | +``` |
| 64 | +
|
| 65 | +## Code Style Guidelines |
| 66 | +
|
| 67 | +### Imports |
| 68 | +- Use ES6 import syntax |
| 69 | +- Import types with `type` keyword: `import type { Foo } from './types'` |
| 70 | +- Group imports: external packages first, then internal modules |
| 71 | +- Use relative paths for local imports |
| 72 | +
|
| 73 | +```typescript |
| 74 | +import type { EventContext } from '../../src/types/common'; |
| 75 | +import { success, jsonResponse } from '../../src/utils/response'; |
| 76 | +``` |
| 77 | + |
| 78 | +### Formatting |
| 79 | +- **Indentation:** 2 spaces (no tabs) |
| 80 | +- **Line length:** 100 characters max |
| 81 | +- **Quotes:** Single quotes for strings (except to avoid escaping) |
| 82 | +- **Semicolons:** Always required |
| 83 | +- **Trailing commas:** ES5 style (objects, arrays) |
| 84 | +- **Arrow functions:** Always use parentheses around parameters |
| 85 | +- **Line endings:** LF (Unix style) |
| 86 | + |
| 87 | +### TypeScript |
| 88 | +- **Strict mode:** Enabled - all strict checks are enforced |
| 89 | +- **Target:** ES2020 |
| 90 | +- **Module:** CommonJS |
| 91 | +- **Type annotations:** Prefer explicit return types for exported functions |
| 92 | +- **Any usage:** Avoid `any`; use `unknown` or proper types. If `any` is necessary, add `/* eslint-disable @typescript-eslint/no-explicit-any */` with justification |
| 93 | +- **Unused vars:** Prefix with underscore if intentionally unused: `_context` |
| 94 | + |
| 95 | +```typescript |
| 96 | +// Good |
| 97 | +export async function getToken(keyId: string): Promise<string> { |
| 98 | + // ... |
| 99 | +} |
| 100 | + |
| 101 | +// Acceptable with comment |
| 102 | +/* eslint-disable @typescript-eslint/no-explicit-any */ |
| 103 | +export interface CommonResp { |
| 104 | + data?: any; // Dynamic response data |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +### Naming Conventions |
| 109 | +- **Files:** kebab-case (`push-handler.ts`, `string.test.ts`) |
| 110 | +- **Functions:** camelCase (`generateJWT`, `normalizeSound`) |
| 111 | +- **Classes:** PascalCase (if used) |
| 112 | +- **Interfaces/Types:** PascalCase (`EventContext`, `PushParams`) |
| 113 | +- **Constants:** UPPER_SNAKE_CASE (`APNS_KEY_ID`, `JWT_TOKEN_VALIDITY_MS`) |
| 114 | +- **Private/unused params:** Prefix with underscore (`_context`, `_unused`) |
| 115 | + |
| 116 | +### Functions and Documentation |
| 117 | +- Add JSDoc comments for exported functions and complex logic |
| 118 | +- Include parameter descriptions and return types |
| 119 | +- Document edge cases and important behavior |
| 120 | + |
| 121 | +```typescript |
| 122 | +/** |
| 123 | + * Generate JWT token for APNs authentication |
| 124 | + * |
| 125 | + * @param keyId - APNs Key ID (default from config) |
| 126 | + * @param teamId - Apple Developer Team ID (default from config) |
| 127 | + * @param privateKey - P8 private key in PEM format (default from config) |
| 128 | + * @returns JWT token string |
| 129 | + */ |
| 130 | +export async function generateJWT( |
| 131 | + keyId: string = APNS_KEY_ID, |
| 132 | + teamId: string = APNS_TEAM_ID, |
| 133 | + privateKey: string = APNS_PRIVATE_KEY |
| 134 | +): Promise<string> { |
| 135 | + // Implementation |
| 136 | +} |
| 137 | +``` |
| 138 | + |
| 139 | +### Error Handling |
| 140 | +- Use try-catch for async operations |
| 141 | +- Extract error messages with utility functions: `getErrorMessage(error)` |
| 142 | +- Return structured error responses using response utilities |
| 143 | +- Check error types before accessing properties |
| 144 | + |
| 145 | +```typescript |
| 146 | +try { |
| 147 | + await someAsyncOperation(); |
| 148 | +} catch (error) { |
| 149 | + const message = getErrorMessage(error); |
| 150 | + return jsonResponse(failure(message, 500)); |
| 151 | +} |
| 152 | +``` |
| 153 | + |
| 154 | +### Testing |
| 155 | +- Use Vitest for all tests |
| 156 | +- Place tests in `__tests__` directories next to source files |
| 157 | +- Name test files: `*.test.ts` |
| 158 | +- Use descriptive test names with `describe` and `it` |
| 159 | +- Test edge cases, error conditions, and happy paths |
| 160 | + |
| 161 | +```typescript |
| 162 | +import { describe, it, expect } from 'vitest'; |
| 163 | + |
| 164 | +describe('isEmpty', () => { |
| 165 | + it('should return true for undefined', () => { |
| 166 | + expect(isEmpty(undefined)).toBe(true); |
| 167 | + }); |
| 168 | + |
| 169 | + it('should return false for non-empty string', () => { |
| 170 | + expect(isEmpty('hello')).toBe(false); |
| 171 | + }); |
| 172 | +}); |
| 173 | +``` |
| 174 | + |
| 175 | +## EdgeOne Specifics |
| 176 | + |
| 177 | +### Edge Function Structure |
| 178 | +- Export `onRequest` function for edge function handlers |
| 179 | +- Accept `EventContext` parameter containing request, env, params |
| 180 | +- Return `Response` objects using response utilities |
| 181 | + |
| 182 | +```typescript |
| 183 | +export async function onRequest(context: EventContext): Promise<Response> { |
| 184 | + return jsonResponse(success()); |
| 185 | +} |
| 186 | +``` |
| 187 | + |
| 188 | +### Environment Variables |
| 189 | +- Access via `context.env` in edge functions |
| 190 | +- Define types in `src/types/environment.ts` |
| 191 | +- Never commit `.env` files |
| 192 | + |
| 193 | +## Common Patterns |
| 194 | + |
| 195 | +### Response Handling |
| 196 | +Use utility functions from `src/utils/response.ts`: |
| 197 | +- `success(data?, message?)` - Success response |
| 198 | +- `failure(message, code?)` - Error response |
| 199 | +- `jsonResponse(data, status?)` - JSON response wrapper |
| 200 | + |
| 201 | +### String Utilities |
| 202 | +- `isEmpty(str)` - Check for empty/whitespace strings |
| 203 | +- `safeDecodeURIComponent(str)` - Safe URI decoding |
| 204 | +- `normalizeSound(sound)` - Normalize sound file names |
| 205 | + |
| 206 | +## Important Notes |
| 207 | + |
| 208 | +- **No console restrictions:** `console.log` is allowed for debugging |
| 209 | +- **Explicit any:** Warn on `any` usage but don't block |
| 210 | +- **Function return types:** Not required but recommended for exports |
| 211 | +- **Coverage exclusions:** Types, config files, and dist/ are excluded from coverage |
0 commit comments