|
| 1 | +--- |
| 2 | +alwaysApply: false |
| 3 | +--- |
| 4 | +# Firebolt Node.js SDK Architecture Rules |
| 5 | + |
| 6 | +## Version Architecture (Critical) |
| 7 | + |
| 8 | +**V1 (Legacy)**: Username/password auth → `ConnectionV1`, `QueryFormatterV1`, `DatabaseServiceV1`, `EngineServiceV1` |
| 9 | +**V2 (Current)**: Service account auth (client_id/secret) → `ConnectionV2`, `QueryFormatterV2`, `DatabaseServiceV2`, `EngineServiceV2` |
| 10 | +**Core (Self-Hosted)**: `FireboltCore()` auth → `ConnectionCore`, `QueryFormatterV2`, no ResourceManager, no async queries, no transactions |
| 11 | + |
| 12 | +Version selected in `makeConnection()` based on auth type. **Always support all versions** unless explicitly version-specific feature. |
| 13 | + |
| 14 | +## Core Patterns |
| 15 | + |
| 16 | +**Dependency Injection**: Factory functions (`FireboltClient()`, `ResourceClient()`) accept `logger` and `httpClient`, return configured instances. Context object passes dependencies. |
| 17 | + |
| 18 | +**Abstract Base Classes**: `Connection` (base) → `ConnectionV1`/`ConnectionV2`. `QueryFormatter` (base) → `QueryFormatterV1`/`QueryFormatterV2`. Base classes contain shared logic. |
| 19 | + |
| 20 | +**Statement Types**: |
| 21 | +- `Statement`: `execute()` → `fetchResult()` (in-memory) or `streamResult()` (in-memory stream, not true streaming) |
| 22 | +- `StreamStatement`: `executeStream()` → true server-side streaming |
| 23 | +- `AsyncStatement`: `executeAsync()` → returns token, no immediate data |
| 24 | + |
| 25 | +## Query Execution Flow |
| 26 | + |
| 27 | +1. `prepareQuery()`: Format with `QueryFormatter` (handles `?`, `:name`, or `$1`/$2` for server-side) |
| 28 | +2. `getRequestUrl()`: Add query params and settings |
| 29 | +3. `executeQuery()`: POST to engine endpoint |
| 30 | +4. `processHeaders()`: Update session parameters from response headers |
| 31 | +5. Parse JSON, handle errors via `throwErrorIfErrorBody()` |
| 32 | +6. Return appropriate Statement type |
| 33 | + |
| 34 | +## Parameter Management |
| 35 | + |
| 36 | +Connection maintains session parameters: |
| 37 | +- **Immutable**: `database`, `account_id`, `output_format` (cannot be removed) |
| 38 | +- **Mutable**: Updated via `SET` statements or response headers |
| 39 | +- **Server headers**: `Firebolt-Update-Parameters`, `Firebolt-Update-Endpoint`, `Firebolt-Reset-Session`, `Firebolt-Remove-Parameters` |
| 40 | + |
| 41 | +## Authentication & Caching |
| 42 | + |
| 43 | +**Managed Firebolt**: `Authenticator` handles OAuth tokens with thread-safe caching (read/write locks via `rwlock`). Cache key: `{clientId, secret, apiEndpoint}`. Tokens expire at 50% of actual expiry for safety. Disable with `useCache: false`. |
| 44 | + |
| 45 | +**Firebolt Core**: `CoreAuthenticator` provides no-op authentication (no tokens, no caching). Core connections don't require authentication. |
| 46 | + |
| 47 | +## Engine Endpoint Resolution |
| 48 | + |
| 49 | +**V2**: Get system engine URL → connect → `USE DATABASE` → `USE ENGINE` (if specified) |
| 50 | +**V1**: Resolve account ID → get engine URL by database/engine → direct connection |
| 51 | +**Core**: `engineEndpoint` must be provided explicitly in connection options (no resolution needed) |
| 52 | + |
| 53 | +## Error Handling |
| 54 | + |
| 55 | +Use `CompositeError` for multiple errors. Custom errors: `AccountNotFoundError`, `AuthenticationError`, `ApiError` (from `src/common/errors.ts`). |
| 56 | + |
| 57 | +## Important Details |
| 58 | + |
| 59 | +- **Prepared statements**: `native` (default, client-side `?`/`:name`) vs `fb_numeric` (server-side `$1`/`$2`) |
| 60 | +- **Transactions**: `begin()`, `commit()`, `rollback()` on Connection (state per connection). |
| 61 | +- **Async queries**: `async: true` setting → token for `isAsyncQueryRunning()`, `isAsyncQuerySuccessful()`, `cancelAsyncQuery()`. **Not supported in Core** - methods throw errors. |
| 62 | +- **ResourceManager**: Available for V1/V2 managed connections. **Not available in Core** - accessing `resourceManager` throws error. |
| 63 | +- **Result hydration**: SQL types → JS types (dates, BigNumber for large ints, normalization) |
| 64 | +- **Connection cleanup**: `destroy()` aborts all active requests |
| 65 | +- **Response formats**: `JSON_COMPACT` (default), `JSON`, `JSON_LINES` |
| 66 | + |
| 67 | +## Development Rules |
| 68 | + |
| 69 | +1. Support V1, V2, and Core unless version-specific feature |
| 70 | +2. Use abstract base classes for shared functionality (`Connection`, `QueryFormatter`, `Authenticator`) |
| 71 | +3. Keep DI pattern for testability |
| 72 | +4. Use custom error types from `src/common/errors.ts` |
| 73 | +5. Maintain separate V1/V2/Core test suites |
| 74 | +6. V1 is legacy; prioritize V2 for new features |
| 75 | +7. **Core limitations**: No ResourceManager, no async queries, no transactions. Use `CoreAuthenticator` for no-op auth. `engineEndpoint` required. |
| 76 | +8. **Type guards**: Use `"type" in auth && auth.type === "firebolt-core"` to detect Core connections |
| 77 | + |
0 commit comments