{"owner":"sidorares","repo":"node-mysql2","hasSkills":true,"totalSkillsCount":5,"totalTokensCount":13034,"categories":["anthropic-skill","claude-rule"],"hasMcp":false,"mcpConfig":null,"found":[".claude/skills/types/rules/architecture.md",".github/skills/code-review/SKILL.md",".claude/skills/types/rules/gaps.md","CLAUDE.md",".claude/skills/types/SKILL.md"],"skills":{".claude/skills/types/rules/architecture.md":"# MySQL2 — Type Architecture\n\nInternal structure of the `.d.ts` files in `/typings`: how the types are organized, connected, and composed.\n\nA ⚠️ marks a spot where the declarations do not match the runtime. This file records the symptom only. [`gaps.md`](gaps.md) owns what each hole is, which tests hit it, and how to close it.\n\n---\n\n## 1. Mixin Pattern\n\n`Connection` and `Pool` get `query()` and `execute()` from mixin functions rather than direct declarations, which is what makes the whole query and execute API work.\n\n`QueryableBase<T>(Base?)` and `ExecutableBase<T>(Base?)` are higher-order functions: each takes a base class constructor and returns an extended class carrying the method overloads.\n\n### Callback versions (return `Query`)\n\nEach method has 4 overloads: `(sql, cb?)`, `(sql, values, cb?)`, `(options, cb?)`, `(options, values, cb?)`.\n\n- `typings/mysql/lib/protocol/sequences/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/ExecutableBase.d.ts`\n\n### Promise versions (return `Promise<[T, FieldPacket[]]>`)\n\nEach method has 2 overloads: `(sql, values?)`, `(options, values?)`.\n\n- `typings/mysql/lib/protocol/sequences/promise/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/promise/ExecutableBase.d.ts`\n\n### Who uses the mixins\n\n| Class/Interface            | Mixin Usage                                                                  |\n| -------------------------- | ---------------------------------------------------------------------------- |\n| `Connection` (callback)    | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `Pool` (callback)          | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `PoolCluster` (callback)   | `extends EventEmitter` directly, with **no mixins**                          |\n| `PoolNamespace` (callback) | interface extends `QueryableAndExecutableBase`                               |\n| `Connection` (promise)     | `extends QueryableAndExecutableBase` (anonymous class composing both mixins) |\n| `Pool` (promise)           | interface extends `Connection` (inherits mixins transitively)                |\n| `PoolNamespace` (promise)  | interface extends `QueryableAndExecutableBase`                               |\n\n### Helper type\n\nThe constraint on every mixin base class parameter:\n\n```ts\ntype QueryableConstructor<T = object> = new (...args: any[]) => T;\n```\n\n---\n\n## 2. Dual API (Callback vs Promise)\n\nMySQL2 exposes two parallel APIs: callback-based and promise-based.\n\n### Entry points\n\n| API      | Entry File            | Primary Type Source             |\n| -------- | --------------------- | ------------------------------- |\n| Callback | `index.d.ts` (root)   | `typings/mysql/index.d.ts`      |\n| Promise  | `promise.d.ts` (root) | declares promise classes inline |\n\n### How promise wraps callback\n\n1. `promise.d.ts` re-exports everything from `index.js`.\n2. It imports the promise-specific mixin functions from `typings/mysql/lib/protocol/sequences/promise/`.\n3. It composes an anonymous `QueryableAndExecutableBase` class:\n   ```ts\n   declare class QueryableAndExecutableBase extends QueryableBaseClass(\n     ExecutableBaseClass(EventEmitter)\n   ) {}\n   ```\n4. The promise classes extend or implement that base.\n\n### Key differences\n\n| Aspect                       | Callback                      | Promise                       |\n| ---------------------------- | ----------------------------- | ----------------------------- |\n| `query()`/`execute()` return | `Query`                       | `Promise<[T, FieldPacket[]]>` |\n| `createConnection()` return  | `Connection` (sync)           | `Promise<Connection>` (async) |\n| `createPool()` return        | `Pool` (sync)                 | `Pool` (sync)                 |\n| `end()`                      | `void` with optional callback | `Promise<void>`               |\n| Resource disposal            | `Symbol.dispose` (sync)       | `Symbol.asyncDispose` (async) |\n| `.promise()` method          | Available on callback classes | Not applicable                |\n\n### Bridging\n\nCallback classes carry a `.promise()` method returning the promise-wrapped version:\n\n- `Connection.promise()` returns `PromiseConnection`.\n- `Pool.promise()` returns `PromisePool`.\n- `PoolConnection.promise()` returns `PromisePool`. ⚠️\n\n---\n\n## 3. Class Hierarchy\n\n### Callback\n\n```\nEventEmitter\n├── Connection extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .authorized: boolean\n│   ├── .state: ConnectionState (readonly)\n│   ├── .sequenceId: number\n│   ├── static createQuery(): Query\n│   ├── beginTransaction(), connect(), commit(), rollback()\n│   ├── changeUser(), end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   ├── prepare(), unprepare(), ping()\n│   ├── serverHandshake(), writeOk(), writeError(), writeEof()\n│   ├── writeTextResult(), writePacket()\n│   ├── promise(): PromiseConnection\n│   └── Symbol.dispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       ├── promise(): PromisePool  ⚠️\n│       └── Symbol.dispose\n│\n├── Pool extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: PoolOptions\n│   ├── getConnection(cb), releaseConnection(conn)\n│   ├── end(cb?), unprepare(sql)\n│   ├── promise(): PromisePool\n│   ├── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│   └── Symbol.dispose\n│   ⚠️ escape(), escapeId(), format() exist at runtime and are undeclared\n│\n├── PoolCluster extends EventEmitter  (NO mixins)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── remove(pattern), end(cb?)\n│   ├── getConnection(cb), getConnection(group, cb), getConnection(group, selector, cb)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'online' | 'offline' | 'remove' | 'warn'\n│   └── Symbol.dispose\n│\n├── Server extends EventEmitter\n│   ├── .connections: Connection[]\n│   ├── listen(port): Server\n│   ├── close(callback): void\n│   ⚠️ listen(port, cb) and close() without a callback are undeclared\n│\n└── Sequence extends EventEmitter\n    ├── Query extends Sequence\n    │   ├── .sql: string\n    │   ├── start(), determinePacket(), stream()\n    │   └── on: 'error' | 'fields' | 'result' | 'end'\n    │\n    └── Prepare extends Sequence\n        ├── .sql: string\n        ├── start(), determinePacket(), stream()\n        └── on: 'error' | 'fields' | 'result' | 'end'\n\nPoolNamespace (interface extends QueryableAndExecutableBase)\n├── getConnection(cb)\n```\n\n### Promise\n\n```\nEventEmitter\n├── Connection extends QueryableAndExecutableBase\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .state: ConnectionState (readonly)\n│   ├── connect(), ping(), beginTransaction(), commit(), rollback()\n│   ├── changeUser(), prepare(), unprepare()\n│   ├── end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   └── Symbol.asyncDispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       └── Symbol.asyncDispose\n│\n├── Pool (interface extends Connection)\n│   ├── .pool: CorePool (callback Pool reference)\n│   ├── getConnection(): Promise<PoolConnection>\n│   ├── releaseConnection(conn)\n│   ├── end(): Promise<void>\n│   └── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│\n├── PoolCluster (interface extends EventEmitter)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── end(): Promise<void>\n│   ├── getConnection(), getConnection(group), getConnection(group, selector)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'remove' | 'warn'\n│   ├── Symbol.asyncDispose\n│   ⚠️ on 'online' and 'offline' are declared in the callback version only\n│   ⚠️ remove() is undeclared\n│\n└── PoolNamespace (interface extends QueryableAndExecutableBase)\n    └── getConnection(): Promise<PoolConnection>\n```\n\n---\n\n## 4. File Map\n\nEvery type definition file, its purpose, and its key exports.\n\n### Root entry points\n\n| File            | Purpose             | Key Exports                                                                                                                                                  |\n| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `/index.d.ts`   | Root callback entry | Re-exports from `typings/mysql/index.js`                                                                                                                     |\n| `/promise.d.ts` | Root promise entry  | `Connection`, `Pool`, `PoolConnection`, `PoolCluster`, `PoolNamespace`, `PreparedStatementInfo`, `createConnection()`, `createPool()`, `createPoolCluster()` |\n\n### Main API (`typings/mysql/`)\n\n| File         | Purpose          | Key Exports                                                                                                                                                          |\n| ------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `index.d.ts` | Callback API hub | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`, `escape`, `escapeId`, `format`, `raw`, `ConnectionConfig`, re-exports all sub-modules |\n\n### Core classes (`typings/mysql/lib/`)\n\n| File                  | Purpose                    | Key Exports                                                                                                                                                                                      |\n| --------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `Connection.d.ts`     | Connection class + options | `Connection`, `ConnectionOptions` (40+ props), `SslOptions`, `ConnectionState`                                                                                                                   |\n| `Pool.d.ts`           | Connection pool            | `Pool`, `PoolOptions`                                                                                                                                                                            |\n| `PoolCluster.d.ts`    | Pool cluster               | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                                                             |\n| `PoolConnection.d.ts` | Pool connection            | `PoolConnection`                                                                                                                                                                                 |\n| `Server.d.ts`         | MySQL server (testing)     | `Server`                                                                                                                                                                                         |\n| `Auth.d.ts`           | Auth plugins               | `AuthPlugin`, `authPlugins` (4 built-in plugins)                                                                                                                                                 |\n| `Tracing.d.ts`        | diagnostics_channel        | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`, `dc`, `hasTracingChannel`, `shouldTrace()`, `traceCallback()`, `tracePromise()`, channel variables |\n\n### Protocol sequences (`typings/mysql/lib/protocol/sequences/`)\n\n| File                  | Purpose                | Key Exports                                                                                                    |\n| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- |\n| `Sequence.d.ts`       | Base class             | `Sequence` (extends EventEmitter, minimal)                                                                     |\n| `Query.d.ts`          | Query sequence         | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`, `QueryableConstructor` |\n| `Prepare.d.ts`        | Prepare sequence       | `Prepare`, `PrepareStatementInfo`                                                                              |\n| `ExecutableBase.d.ts` | Callback execute mixin | `ExecutableBase()`                                                                                             |\n| `QueryableBase.d.ts`  | Callback query mixin   | `QueryableBase()`                                                                                              |\n\n### Promise sequences (`typings/mysql/lib/protocol/sequences/promise/`)\n\n| File                  | Purpose               | Key Exports        |\n| --------------------- | --------------------- | ------------------ |\n| `ExecutableBase.d.ts` | Promise execute mixin | `ExecutableBase()` |\n| `QueryableBase.d.ts`  | Promise query mixin   | `QueryableBase()`  |\n\n### Packets (`typings/mysql/lib/protocol/packets/`)\n\n| File                            | Purpose                             | Key Exports                                             |\n| ------------------------------- | ----------------------------------- | ------------------------------------------------------- |\n| `index.d.ts`                    | Packet hub                          | `QueryResult` (union type), re-exports all packet types |\n| `OkPacket.d.ts`                 | OK result _(deprecated)_            | `OkPacket`                                              |\n| `ResultSetHeader.d.ts`          | Result metadata                     | `ResultSetHeader`                                       |\n| `RowDataPacket.d.ts`            | Data row                            | `RowDataPacket`                                         |\n| `FieldPacket.d.ts`              | Column metadata                     | `FieldPacket`                                           |\n| `Field.d.ts`                    | TypeCast field _(deprecated alias)_ | `Field`, an alias of `TypeCastField`                    |\n| `ProcedurePacket.d.ts`          | Stored procedure result             | `ProcedureCallPacket<T>`                                |\n| `params/OkPacketParams.d.ts`    | Server OK params                    | `OkPacketParams`                                        |\n| `params/ErrorPacketParams.d.ts` | Server error params                 | `ErrorPacketParams`                                     |\n\n### Constants (`typings/mysql/lib/constants/`)\n\n| File                     | Purpose                 | Key Exports                                                         |\n| ------------------------ | ----------------------- | ------------------------------------------------------------------- |\n| `index.d.ts`             | Constants hub           | Re-exports Types, Charsets, CharsetToEncoding                       |\n| `Types.d.ts`             | MySQL column types      | `Types` (hex codes 0x00-0xff + named: DECIMAL, TINY, ..., GEOMETRY) |\n| `Charsets.d.ts`          | Charset/collation IDs   | `Charsets` (270+ named entries)                                     |\n| `CharsetToEncoding.d.ts` | Charset to encoding map | `CharsetToEncoding` (string[])                                      |\n\n### Parsers (`typings/mysql/lib/parsers/`)\n\n| File               | Purpose         | Key Exports                                                                       |\n| ------------------ | --------------- | --------------------------------------------------------------------------------- |\n| `index.d.ts`       | Parsers hub     | Re-exports as `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType` |\n| `typeCast.d.ts`    | Type casting    | `TypeCast`, `Field` (= TypeCastField), `Geometry`, `Next`, `Type`                 |\n| `ParserCache.d.ts` | Cache utilities | `setMaxParserCache()`, `clearParserCache()`                                       |\n\n---\n\n## 5. ConnectionOptions property map\n\nEvery property declared in `typings/mysql/lib/Connection.d.ts`.\n\n### With JSDoc documentation\n\n| Property                | Type                                                    | Default             | Description                          |\n| ----------------------- | ------------------------------------------------------- | ------------------- | ------------------------------------ |\n| `decimalNumbers`        | `boolean`                                               | `false`             | Return DECIMAL/NEWDECIMAL as numbers |\n| `user`                  | `string`                                                | none                | MySQL user                           |\n| `password`              | `string`                                                | none                | MySQL password                       |\n| `password1`             | `string`                                                | none                | Alias for password (MFA)             |\n| `password2`             | `string`                                                | none                | 2nd factor auth password             |\n| `password3`             | `string`                                                | none                | 3rd factor auth password             |\n| `database`              | `string`                                                | none                | Database name                        |\n| `charset`               | `string`                                                | `'UTF8_GENERAL_CI'` | Connection charset/collation         |\n| `host`                  | `string`                                                | `'localhost'`       | Database hostname                    |\n| `port`                  | `number`                                                | `3306`              | Port number                          |\n| `localAddress`          | `string`                                                | none                | Source IP for TCP                    |\n| `socketPath`            | `string`                                                | none                | Unix socket path                     |\n| `timezone`              | `Timezone`                                              | `'local'`           | Timezone for dates                   |\n| `connectTimeout`        | `number`                                                | `10000`             | Connection timeout (ms)              |\n| `stringifyObjects`      | `boolean`                                               | `false`             | Stringify objects in queries         |\n| `insecureAuth`          | `boolean`                                               | `false`             | Allow old auth method                |\n| `infileStreamFactory`   | `(path: string) => Readable`                            | none                | Custom stream for LOAD DATA INFILE   |\n| `typeCast`              | `TypeCast`                                              | `true`              | Column value conversion              |\n| `queryFormat`           | `(query: string, values: any) => void`                  | none                | Custom query format                  |\n| `supportBigNumbers`     | `boolean`                                               | `false`             | Handle BIGINT/DECIMAL                |\n| `bigNumberStrings`      | `boolean`                                               | `false`             | Return big numbers as strings        |\n| `dateStrings`           | `boolean \\| Array<'TIMESTAMP' \\| 'DATETIME' \\| 'DATE'>` | `false`             | Return dates as strings              |\n| `debug`                 | `any`                                                   | `false`             | Print packets to stdout              |\n| `trace`                 | `boolean`                                               | `true`              | Stack traces on errors               |\n| `multipleStatements`    | `boolean`                                               | `false`             | Allow multiple statements            |\n| `flags`                 | `Array<string>`                                         | none                | Connection flags                     |\n| `ssl`                   | `string \\| SslOptions`                                  | none                | SSL configuration                    |\n| `rowsAsArray`           | `boolean`                                               | none                | Return rows as arrays                |\n| `enableKeepAlive`       | `boolean`                                               | `true`              | Socket keep-alive                    |\n| `keepAliveInitialDelay` | `number`                                                | `0`                 | Keep-alive initial delay             |\n\n### Without JSDoc documentation\n\n| Property                | Type                                       | Notes                          |\n| ----------------------- | ------------------------------------------ | ------------------------------ |\n| `charsetNumber`         | `number`                                   | Numeric charset ID             |\n| `compress`              | `boolean`                                  | Protocol compression           |\n| `authSwitchHandler`     | `(data: any, callback: () => void) => any` | Auth switch handler            |\n| `connectAttributes`     | `{ [param: string]: any }`                 | Connection attributes          |\n| `isServer`              | `boolean`                                  | Server mode flag               |\n| `maxPreparedStatements` | `number`                                   | LRU cache limit                |\n| `namedPlaceholders`     | `boolean`                                  | Named placeholder support      |\n| `nestTables`            | `boolean \\| string`                        | Nest tables in results         |\n| `passwordSha1`          | `string`                                   | Pre-hashed password            |\n| `pool`                  | `any`                                      | Pool reference                 |\n| `stream`                | `any`                                      | Custom stream                  |\n| `uri`                   | `string`                                   | Connection URI                 |\n| `connectionLimit`       | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `maxIdle`               | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `idleTimeout`           | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `Promise`               | `any`                                      | Promise constructor            |\n| `queueLimit`            | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `waitForConnections`    | `boolean`                                  | ⚠️ Duplicated from PoolOptions |\n| `disableEval`           | `boolean`                                  | Disable eval in parsers        |\n| `authPlugins`           | `{ [key: string]: AuthPlugin }`            | Custom auth plugins            |\n| `jsonStrings`           | `boolean`                                  | Force JSON as string           |\n| `gracefulEnd`           | `boolean`                                  | Graceful pool end              |\n\n### PoolOptions (extends ConnectionOptions)\n\nAdds these properties with JSDoc, in `typings/mysql/lib/Pool.d.ts`:\n\n| Property             | Type      | Default                   |\n| -------------------- | --------- | ------------------------- |\n| `waitForConnections` | `boolean` | `true`                    |\n| `connectionLimit`    | `number`  | `10`                      |\n| `maxIdle`            | `number`  | same as `connectionLimit` |\n| `idleTimeout`        | `number`  | `60000`                   |\n| `queueLimit`         | `number`  | `0`                       |\n\n---\n\n## 6. QueryOptions vs ConnectionOptions\n\n### Shared options (override per-query)\n\nThese exist in both `ConnectionOptions` and `QueryOptions`, so a query can override the connection:\n\n| Option                | ConnectionOptions        | QueryOptions  |\n| --------------------- | ------------------------ | ------------- |\n| `typeCast`            | ✅                       | ✅            |\n| `nestTables`          | ✅ (`boolean \\| string`) | ✅ (`any` ⚠️) |\n| `rowsAsArray`         | ✅                       | ✅            |\n| `supportBigNumbers`   | ✅                       | ✅            |\n| `bigNumberStrings`    | ✅                       | ✅            |\n| `dateStrings`         | ✅                       | ✅            |\n| `timezone`            | ✅                       | ✅            |\n| `infileStreamFactory` | ✅                       | ✅            |\n| `namedPlaceholders`   | ✅ (undocumented)        | ✅            |\n\n### QueryOptions-only\n\n| Option    | Type          | Description              |\n| --------- | ------------- | ------------------------ |\n| `sql`     | `string`      | The SQL query (required) |\n| `values`  | `QueryValues` | Query parameter values   |\n| `timeout` | `number`      | Operation timeout (ms)   |\n\n### Not in QueryOptions but available at runtime\n\n| Option             | Status                                                       |\n| ------------------ | ------------------------------------------------------------ |\n| `insertIdAsNumber` | Missing from both QueryOptions and ConnectionOptions typings |\n| `decimalNumbers`   | Only in ConnectionOptions, not in QueryOptions               |\n",".github/skills/code-review/SKILL.md":"# MySQL2 — Pull Request Review Skill\n\nThe review checklist for MySQL2 pull requests. Read it before reviewing a pull request, a diff, or a branch.\n\nNever approve a PR that violates an item below without first alerting the author.\n\n## General\n\n1. **Tests:** every bug fix and every new feature ships with tests, and the tests for a fix must fail without it.\n2. **Documentation:** every new feature is documented under `website/docs/`.\n3. **Node 14 compatibility:** Node 14 is the minimum supported runtime, whatever the `engines` field declares.\n4. **Breaking changes:** flag anything that can change existing behavior, even in a patch or a minor feature. The change itself is not an error, an unintentional semver violation is. A change to an existing test is the strongest signal there is, so read it closely for regressions.\n5. **Comments:** ask for a better implementation, never for a better explanation.\n   - An obvious comment is a finding on its own.\n   - Comment length measures the code underneath it. The more explanation it needs, the worse it usually is.\n   - A comment that explains the implementation is replaced by clear names, decoupled functions with a defined scope, and proper abstractions.\n\n## Tests\n\n6. **Connection scope:** `end()`, `close()`, `destroy()`, and `release()` belong in a scope outside the assertions. The wrong shapes are not obvious, so check them against the section below.\n7. **`process.exit`:** a conditional skip uses Poku's `skip`.\n8. **`new Promise` with `setTimeout`:** waiting uses Poku's `sleep`.\n9. **`node:assert` and `node:test`:** assertions and test structure come from `poku`, and `strict` replaces `assert`.\n10. **`as unknown as` and `any`:** never in test files.\n11. **`@ts-expect-error`:** only `// @ts-expect-error: internal access` or `// @ts-expect-error: TODO: implement typings`, and only when the type error is unrelated to the contribution. When it is related, the fix belongs in `/typings`.\n12. **Timer-dependent tests:** waiting on an internal timer is flaky in CI, so the test asserts the state synchronously or calls the internal method directly.\n13. **Promise-based API:** new tests prefer `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need both modes. A recommendation, not a rule.\n14. **`async`/`await`:** `describe`, `it`, and `test` are awaited only when the callback is asynchronous.\n\n## Types\n\n15. **Typings structure:** types follow the existing structure in `/typings` and never land in an arbitrary location. See the [`/types` skill](../../../.claude/skills/types/SKILL.md) for the architecture and the known gaps.\n\n## Connection scope\n\nThe most frequent contributor mistake, and the most expensive: a failing assertion skips the teardown and the test process hangs until CI times out. Both wrong shapes below read as correct at a glance, so compare the diff against them directly.\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Each connection is isolated by a nested or dedicated `describe`.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n- `await conn.promise().end()` replaces wrapping a callback in `new Promise`.\n\n## Verifying the branch\n\n```sh\nnpm run lint         # lint and formatting\nnpm run typecheck    # type-check the project\nnpm test             # full suite, or FILTER=path/to/test.mts npx poku for a single file\nnpm run test:build   # build check\n```\n\nIntegration and global tests need a running MySQL with a `test` database. The global suite runs sequentially and needs elevated privileges, and Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n",".claude/skills/types/rules/gaps.md":"# MySQL2 — Known Type Gaps\n\nEvery occurrence of `@ts-expect-error: TODO: implement typings` across the test files, grouped by root cause. Each one is a runtime API that works correctly and is not yet reflected in the `.d.ts` files in `/typings`.\n\nFor the current count, run `grep -r \"TODO: implement typings\" test/ | wc -l`.\n\n[`architecture.md`](architecture.md) marks where each of these sits in the type structure.\n\n---\n\n## Category 1: Pool Methods Missing\n\n**Problem:** `Pool` inherits `query()` and `execute()` from the mixins, but the escaping and formatting helpers it exposes at runtime are absent from its type.\n\n**Affected typing file:** `typings/mysql/lib/Pool.d.ts`\n\n**Runtime source:** `lib/base/pool.js`, which delegates to `connection.escape()`, `connection.escapeId()`, and `connection.format()`\n\n**Gaps:**\n\n- `pool.escape()`\n- `pool.escapeId()`\n- `pool.format()`\n- `mysql.createPoolPromise()`, a factory function with no type at all\n\n**Test files:**\n\n- `test/integration/test-pool.test.mts`\n\n**Fix:** Add `escape()`, `escapeId()`, and `format()` to the `Pool` class in `Pool.d.ts`, matching the signatures on `Connection`.\n\n---\n\n## Category 2: PrepareStatementInfo Incomplete\n\n**Problem:** `PrepareStatementInfo` declares only `close()` and `execute()`, while the runtime object carries more.\n\n**Affected typing file:** `typings/mysql/lib/protocol/sequences/Prepare.d.ts`\n\n**Runtime source:** `lib/commands/prepare.js`\n\n**Missing properties:**\n\n- `.parameters`: the column and parameter definitions\n- `.columns`: the column definitions\n- `.id`: the numeric statement ID\n\n**Test files:**\n\n- `test/global/integration/regressions/2052.test.mts`\n- `test/integration/connection/test-prepare-simple.test.mts`\n\n**Fix:** Add the three properties to `PrepareStatementInfo`, then mirror the change on the promise version `PreparedStatementInfo` in `promise.d.ts`.\n\n---\n\n## Category 3: Server / createServer\n\n**Problem:** The `Server` class and the `createServer()` factory declare narrower signatures than the runtime accepts.\n\n**Affected typing files:**\n\n- `typings/mysql/index.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Runtime source:** `lib/server.js`\n\n**Gaps:**\n\n- `mysql.createServer()`: the runtime allows no arguments, the type requires a `handler`\n- `server.listen(port, callback)`: the type accepts `(port: number)` only\n- `server.close()`: the runtime allows no arguments, the type requires a callback\n\n**Test files:**\n\n- `test/unit/pool-cluster/test-connection-error-remove.test.mts`\n- `test/unit/pool-cluster/test-restore.test.mts`\n- `test/unit/pool-cluster/test-restore-events.test.mts`\n- `test/unit/pool-cluster/test-remove-by-name.test.mts`\n- `test/unit/pool-cluster/test-remove-by-pattern.test.mts`\n- `test/unit/pool-cluster/test-connection-retry.test.mts`\n- `test/integration/connection/test-server-listen.test.mts`\n\n**Fix:** Add the overloads to `Server.listen()` and `Server.close()`, and make the `createServer()` handler optional.\n\n---\n\n## Category 4: Connection Options in Wrong Contexts\n\n**Problem:** Some options are declared in `ConnectionOptions` but do not flow through every API path that accepts them.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `passwordSha1` in `changeUser()`: `changeUser()` accepts `ConnectionOptions`, yet inference fails on some call patterns\n- `uri` in `createPoolCluster()`: the option is declared in `ConnectionOptions` but does not reach this path\n\n**Test files:**\n\n- `test/global/integration/connection/test-change-user.test.mts`\n- `test/global/integration/connection/test-change-user-plugin-auth.test.mts`\n- `test/common.test.mts`\n\n---\n\n## Category 5: Auth Switch / Multi-Factor\n\n**Problem:** The auth plugin system works at runtime, but the declarations do not cover the server-side handshake or the auth switch protocol.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/Connection.d.ts`\n- `typings/mysql/lib/Auth.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Gaps:**\n\n- The server-side connection handler, `server.on('connection', conn => ...)`: the auth flow methods on `conn` are not fully typed\n- `conn.serverHandshake()`: takes an object with auth plugin configuration, typed as `any`\n- Multi-factor auth callback chains are absent from the types\n- Auth switch response handling is incomplete\n\n**Test files:**\n\n- `test/integration/test-auth-switch.test.mts`\n- `test/integration/test-auth-switch-plugin-error.test.mts`\n- `test/integration/test-auth-switch-plugin-async-error.test.mts`\n- `test/integration/test-auth-switch-multi-factor.test.mts`\n- `test/integration/connection/test-change-user-multi-factor.test.mts`\n\n**Fix:** Type the `serverHandshake()` parameter object, add the server-side connection event types, and type the auth plugin response chain.\n\n---\n\n## Category 6: Query Result Refinements\n\n**Problem:** A query property and a nested config path have no declarations.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/protocol/sequences/Query.d.ts`\n- `typings/mysql/lib/Pool.d.ts`\n\n**Gaps:**\n\n- `Query.values`: exists at runtime, while the `Query` class declares `.sql` only\n- `pool.config.connectionConfig.namedPlaceholders`: `Pool.config` is a `PoolOptions`, which exposes no `connectionConfig` sub-object\n\n**Test files:**\n\n- `test/integration/connection/test-named-placeholders.test.mts`\n- `test/integration/connection/test-multiple-results.test.mts`\n\n---\n\n## Category 7: Stream / Error Handling\n\n**Problem:** Connection stream access and some error handling patterns have no declarations.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `connection.stream`: the raw socket property is absent from the `Connection` type\n- Error event handler types do not cover every error code pattern\n\n**Test files:**\n\n- `test/integration/connection/test-stream-errors.test.mts`\n\n---\n\n## Category 8: Promise Wrapper Access\n\n**Problem:** A promise-wrapped connection exposes `.connection` to reach the underlying callback connection, but the EventEmitter surface of that inner connection is unreachable through the types.\n\n**Affected typing file:** `promise.d.ts`\n\n**Gaps:**\n\n- `conn.connection`: reaching the underlying callback connection from a promise wrapper\n- `conn.connection.listenerCount()` and `conn.connection.emit()`: EventEmitter methods neither inherited nor exposed\n- Assorted promise wrapper methods absent from the declarations\n\n**Test files:**\n\n- `test/integration/promise-wrappers/test-promise-wrappers.test.mts`\n\n---\n\n## Category 9: PoolCluster Promise Wrapper\n\n**Problem:** The promise version of `PoolCluster` declares fewer members than the callback version.\n\n**Affected typing file:** `promise.d.ts` (PoolCluster interface)\n\n**Reference:** `typings/mysql/lib/PoolCluster.d.ts` declares the full callback surface, including the `'online'` and `'offline'` events\n\n**Gaps:**\n\n- `remove()` is absent\n- The `'online'` and `'offline'` event overloads are absent\n- The remaining method signatures do not cover every valid call pattern\n\n**Test files:**\n\n- `test/integration/pool-cluster/test-promise-wrapper.test.mts`\n\n---\n\n## Category 10: Miscellaneous\n\n**Gaps:**\n\n- `connection.close()`: exists at runtime as an alias, while the `Connection` type declares `end()` and `destroy()` only\n- `pool.config.connectionConfig`: the nested config structure has no type\n- One-off gaps specific to a single test\n\n**Test files:**\n\n- `test/global/integration/connection/test-backpressure-load-data-infile.test.mts`\n- `test/integration/test-pool-connect-error.test.mts`\n- `test/integration/config/test-connect-timeout.test.mts`\n- `test/integration/test-server-close.test.mts`\n- `test/integration/test-pool-memory-leak.test.mts`\n\n---\n\n## Confirmed Bugs in Current Types\n\nThese are wrong types, not missing ones.\n\n### Bug 1: `PoolConnection.promise()` returns the wrong type\n\n**File:** `typings/mysql/lib/PoolConnection.d.ts`\n\n**Current:** `promise(promiseImpl?: PromiseConstructor): PromisePool`\n\n**Should be:** `promise(promiseImpl?: PromiseConstructor): PromisePoolConnection`\n\nA pool connection wrapped in a promise is a promise pool connection, not a promise pool.\n\n### Bug 2: `QueryOptions.nestTables` typed as `any`\n\n**File:** `typings/mysql/lib/protocol/sequences/Query.d.ts`\n\n**Current:** `nestTables?: any`\n\n**Should be:** `nestTables?: boolean | string`\n\nThe same property in `ConnectionOptions` already carries the correct type.\n\n### Bug 3: Pool options duplicated in ConnectionOptions\n\n**File:** `typings/mysql/lib/Connection.d.ts`\n\n**Problem:** `connectionLimit`, `maxIdle`, `idleTimeout`, `queueLimit`, and `waitForConnections` are declared in both `ConnectionOptions` and `PoolOptions`. Since `PoolOptions` extends `ConnectionOptions`, they belong in `PoolOptions` alone.\n\n**Impact:** Every function taking a `ConnectionOptions`, `createConnection()` among them, accepts pool-specific options without a TypeScript error.\n","CLAUDE.md":"# MySQL2 — Agent Instructions\n\nYou are an expert Node.js developer contributing to MySQL2, a high-performance MySQL driver focused on compatibility.\n\n## Project\n\n- Minimum compatibility: **Node 14**, whatever the `engines` field in `package.json` declares.\n- Core: `/lib`, exposed through `index.js` (callback) and `promise.js` (promise-based).\n- Types: `/typings` (`.d.ts` files), exposed through `index.d.ts` (callback) and `promise.d.ts` (promise-based).\n- Off limits: `/src` is a separate effort (the TypeScript transcription) and out of scope for any task that does not explicitly ask for it. Never touch it, and never replicate a change made to `/lib`, `index.js` or `promise.js` there.\n- Documentation: `website/docs/` (Docusaurus).\n- Tests:\n  - Unit: `test/unit`, parallel.\n  - Integration: `test/integration`, parallel, alongside the unit tests.\n  - Global: `test/global`, sequential, for tests that change MySQL Server global state and need careful cleanup.\n\n---\n\n## Skills\n\n- Use the [`/lagune` skill](.claude/skills/lagune/SKILL.md) whenever you build or change code, so it improves safety by default. When it is not available, install it from its manifest with `npx -y lagune@latest pull`.\n- Read the [code review skill](.github/skills/code-review/SKILL.md) before reviewing a pull request, a diff, or a branch.\n- Use the [`/types` skill](.claude/skills/types/SKILL.md) when working on `/typings` or fixing a type error in a test.\n\n---\n\n## Comments\n\nNever add an obvious comment nor narrate a statement: code that needs a comment to be understood is usually poorly implemented, and comment length measures how bad the code underneath it is.\n\nWhen code feels like it needs an explanation, improve the implementation instead: clear names, decoupled functions with a defined scope, and proper abstractions.\n\n---\n\n## Tests\n\n**Read `test/common.test.mts` before writing any test.** It provides the shared helpers for creating connections, pools, clusters, and servers, and for reading the test configuration.\n\nThe test runner is **Poku** ([docs](https://poku.io/docs), [repo](https://github.com/wellwelwel/poku)). Test files use `.mts` (ESM TypeScript) and support top-level `await`.\n\nAssertions, utilities, and test structure come from Poku:\n\n```ts\nimport { describe, it, assert, skip, sleep, strict } from 'poku';\n```\n\n- `skip` skips the entire test file and reports it, for cases like a Deno-only test or a specific Node version.\n- `sleep` waits for a given duration: `await sleep(100)`.\n\n| File                      | Description                                                         |\n| ------------------------- | ------------------------------------------------------------------- |\n| `test/common.test.mts`    | Shared helpers                                                      |\n| `poku.config.js`          | Poku config: parallel/sequential suites, timeouts, test directories |\n| `test/docker-compose.yml` | Local environment with MySQL, Node, Deno, Bun, and coverage         |\n\n```sh\nnpm run typecheck\nnpm run lint:fix\nFILTER=test/unit/my-test.mts npx poku    # run a single file through Poku\nnpx tsx test/unit/my-test.mts            # run a single file directly\n```\n\n### Connection scope and resource cleanup\n\nNever close the connection in the same scope as an assertion that may fail. When the assertion throws, `end()` is never reached and the process hangs indefinitely.\n\nOpen and close the connection in an outer scope:\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Use nested or dedicated `describe` blocks to isolate each connection.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n\nPrefer `await conn.promise().end()` instead of wrapping callbacks in `new Promise`:\n\n```ts\n// ❌ Avoid\nawait new Promise<void>((resolve) => pool.end(() => resolve()));\n\n// ✅ Prefer\nawait pool.promise().end();\n```\n\n### Avoid timer-dependent tests\n\nNever wait on an internal timer, such as idle connection cleanup, with `setTimeout` or `sleep`. Execution timing in CI is unpredictable and the test turns flaky. Call the internal method directly, or assert the state synchronously right after the action. When the timer behavior itself is what needs coverage, isolate it so no assertion depends on wall-clock timing.\n\n### `async`/`await`\n\nPoku treats `async`/`await` just like standard JavaScript, so `describe`, `it`, and `test` are awaited **only** when the callback is asynchronous.\n\n**Asynchronous:**\n\n```ts\nawait describe('test', async () => {\n  const connection = createConnection().promise();\n\n  await it('should do something', async () => {\n    const result = await connection.query('SELECT 1');\n\n    assert(result);\n  });\n\n  await connection.end();\n});\n```\n\n**Synchronous:**\n\n```ts\ndescribe('test', () => {\n  it('should do something', () => {\n    strict.equal(1 + 1, 2);\n  });\n});\n```\n\n### Prefer promise-based API\n\nNew tests prefer the promise-based API through `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need coverage in both modes.\n\n> A recommendation, not a strict rule.\n\n```ts\nconst connection = createConnection({/* ... */}).promise();\nconst pool = createPool({/* ... */}).promise();\n\nconst cluster = createPoolCluster({/* ... */});\ncluster.add('node1', {/* ... */});\nconst clusterConnection = await cluster.promise().getConnection();\n```\n\n### TypeScript in tests\n\nNever use `as unknown as` or `any` in test files.\n\nWhen reaching for an internal that the public typings do not expose, use exactly:\n\n```ts\n// @ts-expect-error: internal access\n```\n\nWhen a type in `typings/` is wrong or missing and the contribution has no relation to that type, use exactly:\n\n```ts\n// @ts-expect-error: TODO: implement typings\n```\n\nIf the type error **is** related to the contribution, fix the type in `typings/` instead of suppressing it.\n\n---\n\n## Cursor Cloud specific instructions\n\nMySQL2 is a **library**, not a long-running app. Development means installing the Node dependencies, starting MySQL for the integration tests, then running lint, typecheck, and tests against the driver.\n\n### Services\n\n| Service                          | Required?          | Notes                                                         |\n| -------------------------------- | ------------------ | ------------------------------------------------------------- |\n| **Node.js** (14 or later, CI 22) | Yes                | `npm ci` at repo root                                         |\n| **MySQL**                        | Yes for full tests | Integration and global tests need a `test` database           |\n| **Docker**                       | Recommended        | Runs MySQL the same way as CI                                 |\n| **Docusaurus** (`website/`)      | Optional           | `cd website && npm ci && npm start`, on http://localhost:3000 |\n\n### MySQL via Docker\n\nDocker is installed on the VM, but the daemon may need a manual start when systemd is not active:\n\n```sh\nsudo dockerd > /tmp/dockerd.log 2>&1 &\n```\n\nStart MySQL, which creates the `test` database with an empty root password:\n\n```sh\nsudo docker compose -f test/docker-compose.yml up -d mysql\nnode tools/wait-up.js\n```\n\nUse `sudo docker` when the socket permission error appears. CI pins **MySQL 8.3** (`mysql:8.3`), while `test/docker-compose.yml` uses `mysql:lts`, currently 9.x. For full CI parity, run:\n\n```sh\nsudo docker run -d --name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=test -p 3306:3306 mysql:8.3\n```\n\nWithout `CI=1`, tests use an empty root password, matching docker-compose. With `CI=1`, set `MYSQL_PASSWORD=root`.\n\n### Commands\n\nSee `package.json` and `Contributing.md` for the full set.\n\n| Task        | Command                                            |\n| ----------- | -------------------------------------------------- |\n| Lint        | `npm run lint`                                     |\n| Typecheck   | `npm run typecheck`                                |\n| Tests       | `npm test` (or `FILTER=path/to/test.mts npx poku`) |\n| Build check | `npm run test:build`                               |\n| Website     | `cd website && npm ci && npm test`                 |\n\n`test/global` runs sequentially and needs elevated MySQL privileges. Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n",".claude/skills/types/SKILL.md":"---\nname: types\ndescription: 'Fix a TypeScript error in a `.mts` test file, remove an `any` or a `!`, decide between `@ts-expect-error: internal access` and `@ts-expect-error: TODO: implement typings`, or change a `.d.ts` file under `/typings`. Carries the public type inventory, the decision tree, the type architecture, and every known typing gap.'\nargument-hint: Optionally name the test file or the type to fix, or leave empty\nuser-invocable: true\n---\n\n# MySQL2 — Typings Skill\n\nReference for fixing TypeScript types in `.mts` test files, and for guiding the conversion of `/lib` to TypeScript.\n\nFor how the types are organized, connected, and composed, see [`rules/architecture.md`](rules/architecture.md).\nFor the categorized list of known type gaps, see [`rules/gaps.md`](rules/gaps.md).\n\n---\n\n## 1. Public type inventory\n\nEvery type below is importable from the library. Check this inventory before reaching for `@ts-expect-error`.\n\n### From `index.js` (callback API)\n\n| Category    | Types                                                                                                                                               | Source (relative to `typings/mysql/`)                   |\n| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| Packets     | `RowDataPacket`, `ResultSetHeader`, `FieldPacket`, `OkPacket` _(deprecated)_, `Field` _(deprecated, now `TypeCastField`)_, `ProcedureCallPacket<T>` | `lib/protocol/packets/*.d.ts`                           |\n| Connection  | `Connection`, `ConnectionOptions` (40+ props), `ConnectionState`, `SslOptions`                                                                      | `lib/Connection.d.ts`                                   |\n| Pool        | `Pool`, `PoolOptions` (extends ConnectionOptions), `PoolConnection`                                                                                 | `lib/Pool.d.ts`, `lib/PoolConnection.d.ts`              |\n| Cluster     | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                | `lib/PoolCluster.d.ts`                                  |\n| Query       | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`                                                              | `lib/protocol/sequences/Query.d.ts`                     |\n| Prepare     | `Prepare`, `PrepareStatementInfo`                                                                                                                   | `lib/protocol/sequences/Prepare.d.ts`                   |\n| Parsers     | `TypeCast`, `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType`                                                                     | `lib/parsers/typeCast.d.ts`                             |\n| Constants   | `Types`, `Charsets`, `CharsetToEncoding`                                                                                                            | `lib/constants/*.d.ts`                                  |\n| Auth        | `AuthPlugin`, `authPlugins`                                                                                                                         | `lib/Auth.d.ts`                                         |\n| Server      | `Server`, `OkPacketParams`, `ErrorPacketParams`                                                                                                     | `lib/Server.d.ts`, `lib/protocol/packets/params/*.d.ts` |\n| Tracing     | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`                                                        | `lib/Tracing.d.ts`                                      |\n| Utilities   | `escape()`, `escapeId()`, `format()`, `raw()`, `setMaxParserCache()`, `clearParserCache()`                                                          | `index.d.ts`                                            |\n| SQL escaper | `Raw`, `SqlValue`, `Timezone`                                                                                                                       | re-export from `sql-escaper`                            |\n| Factory     | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`                                                                       | `index.d.ts`                                            |\n| Config      | `ConnectionConfig` (interface with static methods: `mergeFlags`, `getDefaultFlags`, `getCharsetNumber`, `getSSLProfile`, `parseUrl`)                | `index.d.ts`                                            |\n\n### From `promise.js`\n\nRe-exports everything above, with these differences:\n\n| Type                                           | Difference vs callback                                                                 |\n| ---------------------------------------------- | -------------------------------------------------------------------------------------- |\n| `Connection`                                   | `.query()`/`.execute()` return `Promise<[T, FieldPacket[]]>` instead of `Query`        |\n| `Pool` (interface extends Connection)          | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PoolConnection` (extends Connection)          | has `release()` and `Symbol.asyncDispose`                                              |\n| `PoolCluster` (interface extends EventEmitter) | `.getConnection()` returns `Promise<PoolConnection>`, `.end()` returns `Promise<void>` |\n| `PoolNamespace` (interface)                    | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PreparedStatementInfo`                        | `.execute()` returns `Promise<[T, FieldPacket[]]>`, `.close()` returns `Promise<void>` |\n| `createConnection()`                           | returns `Promise<Connection>`, against a synchronous `Connection` in the callback API  |\n| `createPool()`                                 | returns `Pool` synchronously, same as the callback API                                 |\n| `createPoolCluster()`                          | returns `PoolCluster` synchronously, same as the callback API                          |\n\n### Connection methods (callback-based)\n\n- `.query(sql)`, `.query(sql, values)`, `.query(options)`, `.query(options, values)` return `Query`.\n- `.execute(sql)`, `.execute(sql, values)`, `.execute(options)`, `.execute(options, values)` return `Query`.\n- `.prepare(sql, callback?)` returns `Prepare`.\n- `.unprepare(sql)` returns `PrepareStatementInfo`.\n- `.promise(promiseImpl?)` returns the promise-wrapped connection.\n- `.connect(cb?)`, `.end(cb?)`, `.destroy()`, `.pause()`, `.resume()`, `.ping(cb?)`.\n- `.beginTransaction(cb)`, `.commit(cb?)`, `.rollback(cb)`, `.changeUser(options, cb?)`.\n- `.escape(value)`, `.escapeId(value)`, `.format(sql, values?)`.\n\n### Connection methods (server-side, on incoming connections)\n\n- `.serverHandshake(args)` initiates the server handshake.\n- `.writeOk(params?: OkPacketParams)` sends an OK packet.\n- `.writeError(params?: ErrorPacketParams)` sends an error packet.\n- `.writeEof(warnings?, statusFlags?)` sends an EOF packet.\n- `.writeTextResult(rows?, columns?)` sends a result set.\n- `.writePacket(packet)` sends a raw packet.\n\n### Union types\n\n- `QueryResult` = `OkPacket | ResultSetHeader | ResultSetHeader[] | RowDataPacket[] | RowDataPacket[][] | OkPacket[] | ProcedureCallPacket`\n- `ConnectionState` = `'disconnected' | 'protocol_handshake' | 'connected' | 'authenticated' | 'error'`\n\n---\n\n## 2. Decision tree for type errors\n\nClassify the error before fixing it.\n\n### 2.1. The library type exists\n\nWhen the calling function already declares the callback signature, let TypeScript infer the parameter types instead of annotating them again.\n\n```ts\n// ✅ Correct: the types flow from the .query() signature\nconnection.query('SELECT 1', (err, rows, fields) => { ... });\n\n// ❌ Wrong: annotations that duplicate what inference already provides\nconnection.query('SELECT 1', (err: QueryError | null, rows: RowDataPacket[]) => { ... });\n```\n\nThis is the fix for every `any` on a callback parameter. Remove the annotation, do not replace it:\n\n| Pattern with `any`                   | Fix                   |\n| ------------------------------------ | --------------------- |\n| `(err: any, result: any)`            | `(err, result)`       |\n| `(err: any, rows: any)`              | `(err, rows)`         |\n| `(err: any, rows: any, fields: any)` | `(err, rows, fields)` |\n| `(err: any, connection: any)`        | `(err, connection)`   |\n| `(err: any)`                         | `(err)`               |\n\nAnnotate explicitly only where inference cannot reach, such as a standalone callback or a calling function without typings:\n\n```ts\nimport type { TypeCastField } from '../../index.js';\n\nconst conn = createConnection({\n  typeCast: (field: TypeCastField) => field.string(),\n});\n```\n\nQuery and execute results in the promise API still need their generic:\n\n```ts\nimport type { RowDataPacket } from '../../promise.js';\n\nconst [rows] = await connection.query<RowDataPacket[]>('SELECT 1');\n```\n\n### 2.2. The API exists at runtime, the types do not\n\nThe call works, but `/typings` declares it incompletely or not at all. Mark it with `@ts-expect-error: TODO: implement typings`, which flags the work for a later typings fix.\n\nWorking runtime code that TypeScript rejects is a typings gap, never a code bug. Wrong arity, a missing overload, an unrecognized property: all of them mean the declaration is behind the implementation. Add the comment and leave the call exactly as it was. Never rewrite working code to satisfy a wrong type definition.\n\n```ts\n// @ts-expect-error: TODO: implement typings\nconst server = mysql.createServer();\n\n// @ts-expect-error: TODO: implement typings\nserver.listen(3307, () => {\n  /* ... */\n});\n```\n\nEvery known gap is categorized in [`rules/gaps.md`](rules/gaps.md).\n\n### 2.3. The property is internal\n\nThe test deliberately reaches for an implementation detail that sits outside the public API and will never be typed. Mark it with `@ts-expect-error: internal access`.\n\nAnything starting with `_` qualifies: `connection._protocol`, `connection._statements`, `connection._handshakePacket`, `pool._freeConnections`, `pool._allConnections`, `packet._buf`. So does `connection.stream`, the raw socket, despite the missing underscore.\n\n```ts\n// @ts-expect-error: internal access\nconst protocol = connection._protocol;\n\n// @ts-expect-error: internal access\nassert.equal(pool._freeConnections.length, 0);\n```\n\n### 2.4. The shape belongs to the test\n\nA structure the test invents, with no library type behind it. Declare a `type` alias, never an `interface`, or annotate it inline.\n\n```ts\ntype DateTimeRow = RowDataPacket & {\n  date: string | null;\n  time: string | null;\n  datetime: string | null;\n};\n\nconst [rows] = await connection.query<DateTimeRow[]>(\n  'SELECT date, time, datetime FROM test_table'\n);\n\n// A one-off shape intersects RowDataPacket inline\nconst [rows] = await connection.query<(RowDataPacket & { count: number })[]>(\n  'SELECT COUNT(*) as count FROM test_table'\n);\n```\n\n---\n\n## 3. Import conventions\n\n1. **`import type`** for type-only imports:\n\n   ```ts\n   import type { RowDataPacket, FieldPacket } from '../../promise.js';\n   import type { TypeCastField, ConnectionOptions } from '../../index.js';\n   ```\n\n2. **`.mjs` extension** when importing another `.mts` file, since TypeScript `NodeNext` resolves `.mjs` to `.mts`:\n\n   ```ts\n   import { createConnection } from '../../common.test.mjs';\n   ```\n\n3. **`.js` extension** for library imports:\n\n   ```ts\n   import mysql from '../../../index.js';\n   import type { RowDataPacket } from '../../../promise.js';\n   ```\n\n---\n\n## 4. Process\n\n1. **Read** the target `.mts` file.\n2. **Run** `npx tsc --noEmit --pretty <file>` to collect the compiler errors.\n3. **Audit** every `any` (`: any`, `as any`, `<any>`, callback parameters) and every non-null assertion `!` by hand. `noImplicitAny: false` means `tsc` never reports `any`, and `!` always compiles, so a clean build proves nothing about either.\n4. **Classify** each error, each `any`, and each `!` against the decision tree above.\n5. **Fix** them one at a time:\n   - Drop the annotation from callback parameters and let inference work.\n   - Replace `any` in a variable declaration with the real type.\n   - Replace `!` with optional chaining: `fields![0].name` becomes `fields?.[0].name`.\n   - Add the missing `import type` statements.\n   - Add the generic to `.query<T>()` and `.execute<T>()`.\n   - Add `// @ts-expect-error: TODO: implement typings` where the library type is missing.\n   - Add `// @ts-expect-error: internal access` where the property is internal.\n   - Declare a `type` alias for a test-local structure.\n6. **Re-run** `npx tsc --noEmit --pretty <file>`.\n7. **Re-audit** the file for `any` and `!`.\n8. **Done** when all three hold: no compiler errors, no `any`, no `!`.\n\n---\n\n## 5. Pitfalls\n\n- **`any` is forbidden.** Every `: any`, `as any`, and `<any>` has to go, and step 3 above is the only thing that finds them.\n- **`!` is forbidden.** `fields![0].name` is a hack that silences the compiler, `fields?.[0].name` is safe access.\n- **The two `@ts-expect-error` comments are not interchangeable.** `TODO: implement typings` tracks work the library owes, `internal access` marks something intentionally private. Collapsing them loses the distinction that makes the first list actionable.\n- **A change in `/typings` lands on both APIs.** Verify the callback entry (`typings/mysql/index.d.ts`) and the promise entry (`promise.d.ts`) together, and remember that touching `QueryableBase` or `ExecutableBase` reaches `Connection`, `Pool`, and `PoolNamespace` at once.\n"},"files":{".claude/skills/types/rules/architecture.md":"# MySQL2 — Type Architecture\n\nInternal structure of the `.d.ts` files in `/typings`: how the types are organized, connected, and composed.\n\nA ⚠️ marks a spot where the declarations do not match the runtime. This file records the symptom only. [`gaps.md`](gaps.md) owns what each hole is, which tests hit it, and how to close it.\n\n---\n\n## 1. Mixin Pattern\n\n`Connection` and `Pool` get `query()` and `execute()` from mixin functions rather than direct declarations, which is what makes the whole query and execute API work.\n\n`QueryableBase<T>(Base?)` and `ExecutableBase<T>(Base?)` are higher-order functions: each takes a base class constructor and returns an extended class carrying the method overloads.\n\n### Callback versions (return `Query`)\n\nEach method has 4 overloads: `(sql, cb?)`, `(sql, values, cb?)`, `(options, cb?)`, `(options, values, cb?)`.\n\n- `typings/mysql/lib/protocol/sequences/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/ExecutableBase.d.ts`\n\n### Promise versions (return `Promise<[T, FieldPacket[]]>`)\n\nEach method has 2 overloads: `(sql, values?)`, `(options, values?)`.\n\n- `typings/mysql/lib/protocol/sequences/promise/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/promise/ExecutableBase.d.ts`\n\n### Who uses the mixins\n\n| Class/Interface            | Mixin Usage                                                                  |\n| -------------------------- | ---------------------------------------------------------------------------- |\n| `Connection` (callback)    | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `Pool` (callback)          | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `PoolCluster` (callback)   | `extends EventEmitter` directly, with **no mixins**                          |\n| `PoolNamespace` (callback) | interface extends `QueryableAndExecutableBase`                               |\n| `Connection` (promise)     | `extends QueryableAndExecutableBase` (anonymous class composing both mixins) |\n| `Pool` (promise)           | interface extends `Connection` (inherits mixins transitively)                |\n| `PoolNamespace` (promise)  | interface extends `QueryableAndExecutableBase`                               |\n\n### Helper type\n\nThe constraint on every mixin base class parameter:\n\n```ts\ntype QueryableConstructor<T = object> = new (...args: any[]) => T;\n```\n\n---\n\n## 2. Dual API (Callback vs Promise)\n\nMySQL2 exposes two parallel APIs: callback-based and promise-based.\n\n### Entry points\n\n| API      | Entry File            | Primary Type Source             |\n| -------- | --------------------- | ------------------------------- |\n| Callback | `index.d.ts` (root)   | `typings/mysql/index.d.ts`      |\n| Promise  | `promise.d.ts` (root) | declares promise classes inline |\n\n### How promise wraps callback\n\n1. `promise.d.ts` re-exports everything from `index.js`.\n2. It imports the promise-specific mixin functions from `typings/mysql/lib/protocol/sequences/promise/`.\n3. It composes an anonymous `QueryableAndExecutableBase` class:\n   ```ts\n   declare class QueryableAndExecutableBase extends QueryableBaseClass(\n     ExecutableBaseClass(EventEmitter)\n   ) {}\n   ```\n4. The promise classes extend or implement that base.\n\n### Key differences\n\n| Aspect                       | Callback                      | Promise                       |\n| ---------------------------- | ----------------------------- | ----------------------------- |\n| `query()`/`execute()` return | `Query`                       | `Promise<[T, FieldPacket[]]>` |\n| `createConnection()` return  | `Connection` (sync)           | `Promise<Connection>` (async) |\n| `createPool()` return        | `Pool` (sync)                 | `Pool` (sync)                 |\n| `end()`                      | `void` with optional callback | `Promise<void>`               |\n| Resource disposal            | `Symbol.dispose` (sync)       | `Symbol.asyncDispose` (async) |\n| `.promise()` method          | Available on callback classes | Not applicable                |\n\n### Bridging\n\nCallback classes carry a `.promise()` method returning the promise-wrapped version:\n\n- `Connection.promise()` returns `PromiseConnection`.\n- `Pool.promise()` returns `PromisePool`.\n- `PoolConnection.promise()` returns `PromisePool`. ⚠️\n\n---\n\n## 3. Class Hierarchy\n\n### Callback\n\n```\nEventEmitter\n├── Connection extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .authorized: boolean\n│   ├── .state: ConnectionState (readonly)\n│   ├── .sequenceId: number\n│   ├── static createQuery(): Query\n│   ├── beginTransaction(), connect(), commit(), rollback()\n│   ├── changeUser(), end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   ├── prepare(), unprepare(), ping()\n│   ├── serverHandshake(), writeOk(), writeError(), writeEof()\n│   ├── writeTextResult(), writePacket()\n│   ├── promise(): PromiseConnection\n│   └── Symbol.dispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       ├── promise(): PromisePool  ⚠️\n│       └── Symbol.dispose\n│\n├── Pool extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: PoolOptions\n│   ├── getConnection(cb), releaseConnection(conn)\n│   ├── end(cb?), unprepare(sql)\n│   ├── promise(): PromisePool\n│   ├── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│   └── Symbol.dispose\n│   ⚠️ escape(), escapeId(), format() exist at runtime and are undeclared\n│\n├── PoolCluster extends EventEmitter  (NO mixins)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── remove(pattern), end(cb?)\n│   ├── getConnection(cb), getConnection(group, cb), getConnection(group, selector, cb)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'online' | 'offline' | 'remove' | 'warn'\n│   └── Symbol.dispose\n│\n├── Server extends EventEmitter\n│   ├── .connections: Connection[]\n│   ├── listen(port): Server\n│   ├── close(callback): void\n│   ⚠️ listen(port, cb) and close() without a callback are undeclared\n│\n└── Sequence extends EventEmitter\n    ├── Query extends Sequence\n    │   ├── .sql: string\n    │   ├── start(), determinePacket(), stream()\n    │   └── on: 'error' | 'fields' | 'result' | 'end'\n    │\n    └── Prepare extends Sequence\n        ├── .sql: string\n        ├── start(), determinePacket(), stream()\n        └── on: 'error' | 'fields' | 'result' | 'end'\n\nPoolNamespace (interface extends QueryableAndExecutableBase)\n├── getConnection(cb)\n```\n\n### Promise\n\n```\nEventEmitter\n├── Connection extends QueryableAndExecutableBase\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .state: ConnectionState (readonly)\n│   ├── connect(), ping(), beginTransaction(), commit(), rollback()\n│   ├── changeUser(), prepare(), unprepare()\n│   ├── end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   └── Symbol.asyncDispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       └── Symbol.asyncDispose\n│\n├── Pool (interface extends Connection)\n│   ├── .pool: CorePool (callback Pool reference)\n│   ├── getConnection(): Promise<PoolConnection>\n│   ├── releaseConnection(conn)\n│   ├── end(): Promise<void>\n│   └── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│\n├── PoolCluster (interface extends EventEmitter)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── end(): Promise<void>\n│   ├── getConnection(), getConnection(group), getConnection(group, selector)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'remove' | 'warn'\n│   ├── Symbol.asyncDispose\n│   ⚠️ on 'online' and 'offline' are declared in the callback version only\n│   ⚠️ remove() is undeclared\n│\n└── PoolNamespace (interface extends QueryableAndExecutableBase)\n    └── getConnection(): Promise<PoolConnection>\n```\n\n---\n\n## 4. File Map\n\nEvery type definition file, its purpose, and its key exports.\n\n### Root entry points\n\n| File            | Purpose             | Key Exports                                                                                                                                                  |\n| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `/index.d.ts`   | Root callback entry | Re-exports from `typings/mysql/index.js`                                                                                                                     |\n| `/promise.d.ts` | Root promise entry  | `Connection`, `Pool`, `PoolConnection`, `PoolCluster`, `PoolNamespace`, `PreparedStatementInfo`, `createConnection()`, `createPool()`, `createPoolCluster()` |\n\n### Main API (`typings/mysql/`)\n\n| File         | Purpose          | Key Exports                                                                                                                                                          |\n| ------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `index.d.ts` | Callback API hub | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`, `escape`, `escapeId`, `format`, `raw`, `ConnectionConfig`, re-exports all sub-modules |\n\n### Core classes (`typings/mysql/lib/`)\n\n| File                  | Purpose                    | Key Exports                                                                                                                                                                                      |\n| --------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `Connection.d.ts`     | Connection class + options | `Connection`, `ConnectionOptions` (40+ props), `SslOptions`, `ConnectionState`                                                                                                                   |\n| `Pool.d.ts`           | Connection pool            | `Pool`, `PoolOptions`                                                                                                                                                                            |\n| `PoolCluster.d.ts`    | Pool cluster               | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                                                             |\n| `PoolConnection.d.ts` | Pool connection            | `PoolConnection`                                                                                                                                                                                 |\n| `Server.d.ts`         | MySQL server (testing)     | `Server`                                                                                                                                                                                         |\n| `Auth.d.ts`           | Auth plugins               | `AuthPlugin`, `authPlugins` (4 built-in plugins)                                                                                                                                                 |\n| `Tracing.d.ts`        | diagnostics_channel        | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`, `dc`, `hasTracingChannel`, `shouldTrace()`, `traceCallback()`, `tracePromise()`, channel variables |\n\n### Protocol sequences (`typings/mysql/lib/protocol/sequences/`)\n\n| File                  | Purpose                | Key Exports                                                                                                    |\n| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- |\n| `Sequence.d.ts`       | Base class             | `Sequence` (extends EventEmitter, minimal)                                                                     |\n| `Query.d.ts`          | Query sequence         | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`, `QueryableConstructor` |\n| `Prepare.d.ts`        | Prepare sequence       | `Prepare`, `PrepareStatementInfo`                                                                              |\n| `ExecutableBase.d.ts` | Callback execute mixin | `ExecutableBase()`                                                                                             |\n| `QueryableBase.d.ts`  | Callback query mixin   | `QueryableBase()`                                                                                              |\n\n### Promise sequences (`typings/mysql/lib/protocol/sequences/promise/`)\n\n| File                  | Purpose               | Key Exports        |\n| --------------------- | --------------------- | ------------------ |\n| `ExecutableBase.d.ts` | Promise execute mixin | `ExecutableBase()` |\n| `QueryableBase.d.ts`  | Promise query mixin   | `QueryableBase()`  |\n\n### Packets (`typings/mysql/lib/protocol/packets/`)\n\n| File                            | Purpose                             | Key Exports                                             |\n| ------------------------------- | ----------------------------------- | ------------------------------------------------------- |\n| `index.d.ts`                    | Packet hub                          | `QueryResult` (union type), re-exports all packet types |\n| `OkPacket.d.ts`                 | OK result _(deprecated)_            | `OkPacket`                                              |\n| `ResultSetHeader.d.ts`          | Result metadata                     | `ResultSetHeader`                                       |\n| `RowDataPacket.d.ts`            | Data row                            | `RowDataPacket`                                         |\n| `FieldPacket.d.ts`              | Column metadata                     | `FieldPacket`                                           |\n| `Field.d.ts`                    | TypeCast field _(deprecated alias)_ | `Field`, an alias of `TypeCastField`                    |\n| `ProcedurePacket.d.ts`          | Stored procedure result             | `ProcedureCallPacket<T>`                                |\n| `params/OkPacketParams.d.ts`    | Server OK params                    | `OkPacketParams`                                        |\n| `params/ErrorPacketParams.d.ts` | Server error params                 | `ErrorPacketParams`                                     |\n\n### Constants (`typings/mysql/lib/constants/`)\n\n| File                     | Purpose                 | Key Exports                                                         |\n| ------------------------ | ----------------------- | ------------------------------------------------------------------- |\n| `index.d.ts`             | Constants hub           | Re-exports Types, Charsets, CharsetToEncoding                       |\n| `Types.d.ts`             | MySQL column types      | `Types` (hex codes 0x00-0xff + named: DECIMAL, TINY, ..., GEOMETRY) |\n| `Charsets.d.ts`          | Charset/collation IDs   | `Charsets` (270+ named entries)                                     |\n| `CharsetToEncoding.d.ts` | Charset to encoding map | `CharsetToEncoding` (string[])                                      |\n\n### Parsers (`typings/mysql/lib/parsers/`)\n\n| File               | Purpose         | Key Exports                                                                       |\n| ------------------ | --------------- | --------------------------------------------------------------------------------- |\n| `index.d.ts`       | Parsers hub     | Re-exports as `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType` |\n| `typeCast.d.ts`    | Type casting    | `TypeCast`, `Field` (= TypeCastField), `Geometry`, `Next`, `Type`                 |\n| `ParserCache.d.ts` | Cache utilities | `setMaxParserCache()`, `clearParserCache()`                                       |\n\n---\n\n## 5. ConnectionOptions property map\n\nEvery property declared in `typings/mysql/lib/Connection.d.ts`.\n\n### With JSDoc documentation\n\n| Property                | Type                                                    | Default             | Description                          |\n| ----------------------- | ------------------------------------------------------- | ------------------- | ------------------------------------ |\n| `decimalNumbers`        | `boolean`                                               | `false`             | Return DECIMAL/NEWDECIMAL as numbers |\n| `user`                  | `string`                                                | none                | MySQL user                           |\n| `password`              | `string`                                                | none                | MySQL password                       |\n| `password1`             | `string`                                                | none                | Alias for password (MFA)             |\n| `password2`             | `string`                                                | none                | 2nd factor auth password             |\n| `password3`             | `string`                                                | none                | 3rd factor auth password             |\n| `database`              | `string`                                                | none                | Database name                        |\n| `charset`               | `string`                                                | `'UTF8_GENERAL_CI'` | Connection charset/collation         |\n| `host`                  | `string`                                                | `'localhost'`       | Database hostname                    |\n| `port`                  | `number`                                                | `3306`              | Port number                          |\n| `localAddress`          | `string`                                                | none                | Source IP for TCP                    |\n| `socketPath`            | `string`                                                | none                | Unix socket path                     |\n| `timezone`              | `Timezone`                                              | `'local'`           | Timezone for dates                   |\n| `connectTimeout`        | `number`                                                | `10000`             | Connection timeout (ms)              |\n| `stringifyObjects`      | `boolean`                                               | `false`             | Stringify objects in queries         |\n| `insecureAuth`          | `boolean`                                               | `false`             | Allow old auth method                |\n| `infileStreamFactory`   | `(path: string) => Readable`                            | none                | Custom stream for LOAD DATA INFILE   |\n| `typeCast`              | `TypeCast`                                              | `true`              | Column value conversion              |\n| `queryFormat`           | `(query: string, values: any) => void`                  | none                | Custom query format                  |\n| `supportBigNumbers`     | `boolean`                                               | `false`             | Handle BIGINT/DECIMAL                |\n| `bigNumberStrings`      | `boolean`                                               | `false`             | Return big numbers as strings        |\n| `dateStrings`           | `boolean \\| Array<'TIMESTAMP' \\| 'DATETIME' \\| 'DATE'>` | `false`             | Return dates as strings              |\n| `debug`                 | `any`                                                   | `false`             | Print packets to stdout              |\n| `trace`                 | `boolean`                                               | `true`              | Stack traces on errors               |\n| `multipleStatements`    | `boolean`                                               | `false`             | Allow multiple statements            |\n| `flags`                 | `Array<string>`                                         | none                | Connection flags                     |\n| `ssl`                   | `string \\| SslOptions`                                  | none                | SSL configuration                    |\n| `rowsAsArray`           | `boolean`                                               | none                | Return rows as arrays                |\n| `enableKeepAlive`       | `boolean`                                               | `true`              | Socket keep-alive                    |\n| `keepAliveInitialDelay` | `number`                                                | `0`                 | Keep-alive initial delay             |\n\n### Without JSDoc documentation\n\n| Property                | Type                                       | Notes                          |\n| ----------------------- | ------------------------------------------ | ------------------------------ |\n| `charsetNumber`         | `number`                                   | Numeric charset ID             |\n| `compress`              | `boolean`                                  | Protocol compression           |\n| `authSwitchHandler`     | `(data: any, callback: () => void) => any` | Auth switch handler            |\n| `connectAttributes`     | `{ [param: string]: any }`                 | Connection attributes          |\n| `isServer`              | `boolean`                                  | Server mode flag               |\n| `maxPreparedStatements` | `number`                                   | LRU cache limit                |\n| `namedPlaceholders`     | `boolean`                                  | Named placeholder support      |\n| `nestTables`            | `boolean \\| string`                        | Nest tables in results         |\n| `passwordSha1`          | `string`                                   | Pre-hashed password            |\n| `pool`                  | `any`                                      | Pool reference                 |\n| `stream`                | `any`                                      | Custom stream                  |\n| `uri`                   | `string`                                   | Connection URI                 |\n| `connectionLimit`       | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `maxIdle`               | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `idleTimeout`           | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `Promise`               | `any`                                      | Promise constructor            |\n| `queueLimit`            | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `waitForConnections`    | `boolean`                                  | ⚠️ Duplicated from PoolOptions |\n| `disableEval`           | `boolean`                                  | Disable eval in parsers        |\n| `authPlugins`           | `{ [key: string]: AuthPlugin }`            | Custom auth plugins            |\n| `jsonStrings`           | `boolean`                                  | Force JSON as string           |\n| `gracefulEnd`           | `boolean`                                  | Graceful pool end              |\n\n### PoolOptions (extends ConnectionOptions)\n\nAdds these properties with JSDoc, in `typings/mysql/lib/Pool.d.ts`:\n\n| Property             | Type      | Default                   |\n| -------------------- | --------- | ------------------------- |\n| `waitForConnections` | `boolean` | `true`                    |\n| `connectionLimit`    | `number`  | `10`                      |\n| `maxIdle`            | `number`  | same as `connectionLimit` |\n| `idleTimeout`        | `number`  | `60000`                   |\n| `queueLimit`         | `number`  | `0`                       |\n\n---\n\n## 6. QueryOptions vs ConnectionOptions\n\n### Shared options (override per-query)\n\nThese exist in both `ConnectionOptions` and `QueryOptions`, so a query can override the connection:\n\n| Option                | ConnectionOptions        | QueryOptions  |\n| --------------------- | ------------------------ | ------------- |\n| `typeCast`            | ✅                       | ✅            |\n| `nestTables`          | ✅ (`boolean \\| string`) | ✅ (`any` ⚠️) |\n| `rowsAsArray`         | ✅                       | ✅            |\n| `supportBigNumbers`   | ✅                       | ✅            |\n| `bigNumberStrings`    | ✅                       | ✅            |\n| `dateStrings`         | ✅                       | ✅            |\n| `timezone`            | ✅                       | ✅            |\n| `infileStreamFactory` | ✅                       | ✅            |\n| `namedPlaceholders`   | ✅ (undocumented)        | ✅            |\n\n### QueryOptions-only\n\n| Option    | Type          | Description              |\n| --------- | ------------- | ------------------------ |\n| `sql`     | `string`      | The SQL query (required) |\n| `values`  | `QueryValues` | Query parameter values   |\n| `timeout` | `number`      | Operation timeout (ms)   |\n\n### Not in QueryOptions but available at runtime\n\n| Option             | Status                                                       |\n| ------------------ | ------------------------------------------------------------ |\n| `insertIdAsNumber` | Missing from both QueryOptions and ConnectionOptions typings |\n| `decimalNumbers`   | Only in ConnectionOptions, not in QueryOptions               |\n",".github/skills/code-review/SKILL.md":"# MySQL2 — Pull Request Review Skill\n\nThe review checklist for MySQL2 pull requests. Read it before reviewing a pull request, a diff, or a branch.\n\nNever approve a PR that violates an item below without first alerting the author.\n\n## General\n\n1. **Tests:** every bug fix and every new feature ships with tests, and the tests for a fix must fail without it.\n2. **Documentation:** every new feature is documented under `website/docs/`.\n3. **Node 14 compatibility:** Node 14 is the minimum supported runtime, whatever the `engines` field declares.\n4. **Breaking changes:** flag anything that can change existing behavior, even in a patch or a minor feature. The change itself is not an error, an unintentional semver violation is. A change to an existing test is the strongest signal there is, so read it closely for regressions.\n5. **Comments:** ask for a better implementation, never for a better explanation.\n   - An obvious comment is a finding on its own.\n   - Comment length measures the code underneath it. The more explanation it needs, the worse it usually is.\n   - A comment that explains the implementation is replaced by clear names, decoupled functions with a defined scope, and proper abstractions.\n\n## Tests\n\n6. **Connection scope:** `end()`, `close()`, `destroy()`, and `release()` belong in a scope outside the assertions. The wrong shapes are not obvious, so check them against the section below.\n7. **`process.exit`:** a conditional skip uses Poku's `skip`.\n8. **`new Promise` with `setTimeout`:** waiting uses Poku's `sleep`.\n9. **`node:assert` and `node:test`:** assertions and test structure come from `poku`, and `strict` replaces `assert`.\n10. **`as unknown as` and `any`:** never in test files.\n11. **`@ts-expect-error`:** only `// @ts-expect-error: internal access` or `// @ts-expect-error: TODO: implement typings`, and only when the type error is unrelated to the contribution. When it is related, the fix belongs in `/typings`.\n12. **Timer-dependent tests:** waiting on an internal timer is flaky in CI, so the test asserts the state synchronously or calls the internal method directly.\n13. **Promise-based API:** new tests prefer `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need both modes. A recommendation, not a rule.\n14. **`async`/`await`:** `describe`, `it`, and `test` are awaited only when the callback is asynchronous.\n\n## Types\n\n15. **Typings structure:** types follow the existing structure in `/typings` and never land in an arbitrary location. See the [`/types` skill](../../../.claude/skills/types/SKILL.md) for the architecture and the known gaps.\n\n## Connection scope\n\nThe most frequent contributor mistake, and the most expensive: a failing assertion skips the teardown and the test process hangs until CI times out. Both wrong shapes below read as correct at a glance, so compare the diff against them directly.\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Each connection is isolated by a nested or dedicated `describe`.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n- `await conn.promise().end()` replaces wrapping a callback in `new Promise`.\n\n## Verifying the branch\n\n```sh\nnpm run lint         # lint and formatting\nnpm run typecheck    # type-check the project\nnpm test             # full suite, or FILTER=path/to/test.mts npx poku for a single file\nnpm run test:build   # build check\n```\n\nIntegration and global tests need a running MySQL with a `test` database. The global suite runs sequentially and needs elevated privileges, and Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n",".claude/skills/types/rules/gaps.md":"# MySQL2 — Known Type Gaps\n\nEvery occurrence of `@ts-expect-error: TODO: implement typings` across the test files, grouped by root cause. Each one is a runtime API that works correctly and is not yet reflected in the `.d.ts` files in `/typings`.\n\nFor the current count, run `grep -r \"TODO: implement typings\" test/ | wc -l`.\n\n[`architecture.md`](architecture.md) marks where each of these sits in the type structure.\n\n---\n\n## Category 1: Pool Methods Missing\n\n**Problem:** `Pool` inherits `query()` and `execute()` from the mixins, but the escaping and formatting helpers it exposes at runtime are absent from its type.\n\n**Affected typing file:** `typings/mysql/lib/Pool.d.ts`\n\n**Runtime source:** `lib/base/pool.js`, which delegates to `connection.escape()`, `connection.escapeId()`, and `connection.format()`\n\n**Gaps:**\n\n- `pool.escape()`\n- `pool.escapeId()`\n- `pool.format()`\n- `mysql.createPoolPromise()`, a factory function with no type at all\n\n**Test files:**\n\n- `test/integration/test-pool.test.mts`\n\n**Fix:** Add `escape()`, `escapeId()`, and `format()` to the `Pool` class in `Pool.d.ts`, matching the signatures on `Connection`.\n\n---\n\n## Category 2: PrepareStatementInfo Incomplete\n\n**Problem:** `PrepareStatementInfo` declares only `close()` and `execute()`, while the runtime object carries more.\n\n**Affected typing file:** `typings/mysql/lib/protocol/sequences/Prepare.d.ts`\n\n**Runtime source:** `lib/commands/prepare.js`\n\n**Missing properties:**\n\n- `.parameters`: the column and parameter definitions\n- `.columns`: the column definitions\n- `.id`: the numeric statement ID\n\n**Test files:**\n\n- `test/global/integration/regressions/2052.test.mts`\n- `test/integration/connection/test-prepare-simple.test.mts`\n\n**Fix:** Add the three properties to `PrepareStatementInfo`, then mirror the change on the promise version `PreparedStatementInfo` in `promise.d.ts`.\n\n---\n\n## Category 3: Server / createServer\n\n**Problem:** The `Server` class and the `createServer()` factory declare narrower signatures than the runtime accepts.\n\n**Affected typing files:**\n\n- `typings/mysql/index.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Runtime source:** `lib/server.js`\n\n**Gaps:**\n\n- `mysql.createServer()`: the runtime allows no arguments, the type requires a `handler`\n- `server.listen(port, callback)`: the type accepts `(port: number)` only\n- `server.close()`: the runtime allows no arguments, the type requires a callback\n\n**Test files:**\n\n- `test/unit/pool-cluster/test-connection-error-remove.test.mts`\n- `test/unit/pool-cluster/test-restore.test.mts`\n- `test/unit/pool-cluster/test-restore-events.test.mts`\n- `test/unit/pool-cluster/test-remove-by-name.test.mts`\n- `test/unit/pool-cluster/test-remove-by-pattern.test.mts`\n- `test/unit/pool-cluster/test-connection-retry.test.mts`\n- `test/integration/connection/test-server-listen.test.mts`\n\n**Fix:** Add the overloads to `Server.listen()` and `Server.close()`, and make the `createServer()` handler optional.\n\n---\n\n## Category 4: Connection Options in Wrong Contexts\n\n**Problem:** Some options are declared in `ConnectionOptions` but do not flow through every API path that accepts them.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `passwordSha1` in `changeUser()`: `changeUser()` accepts `ConnectionOptions`, yet inference fails on some call patterns\n- `uri` in `createPoolCluster()`: the option is declared in `ConnectionOptions` but does not reach this path\n\n**Test files:**\n\n- `test/global/integration/connection/test-change-user.test.mts`\n- `test/global/integration/connection/test-change-user-plugin-auth.test.mts`\n- `test/common.test.mts`\n\n---\n\n## Category 5: Auth Switch / Multi-Factor\n\n**Problem:** The auth plugin system works at runtime, but the declarations do not cover the server-side handshake or the auth switch protocol.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/Connection.d.ts`\n- `typings/mysql/lib/Auth.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Gaps:**\n\n- The server-side connection handler, `server.on('connection', conn => ...)`: the auth flow methods on `conn` are not fully typed\n- `conn.serverHandshake()`: takes an object with auth plugin configuration, typed as `any`\n- Multi-factor auth callback chains are absent from the types\n- Auth switch response handling is incomplete\n\n**Test files:**\n\n- `test/integration/test-auth-switch.test.mts`\n- `test/integration/test-auth-switch-plugin-error.test.mts`\n- `test/integration/test-auth-switch-plugin-async-error.test.mts`\n- `test/integration/test-auth-switch-multi-factor.test.mts`\n- `test/integration/connection/test-change-user-multi-factor.test.mts`\n\n**Fix:** Type the `serverHandshake()` parameter object, add the server-side connection event types, and type the auth plugin response chain.\n\n---\n\n## Category 6: Query Result Refinements\n\n**Problem:** A query property and a nested config path have no declarations.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/protocol/sequences/Query.d.ts`\n- `typings/mysql/lib/Pool.d.ts`\n\n**Gaps:**\n\n- `Query.values`: exists at runtime, while the `Query` class declares `.sql` only\n- `pool.config.connectionConfig.namedPlaceholders`: `Pool.config` is a `PoolOptions`, which exposes no `connectionConfig` sub-object\n\n**Test files:**\n\n- `test/integration/connection/test-named-placeholders.test.mts`\n- `test/integration/connection/test-multiple-results.test.mts`\n\n---\n\n## Category 7: Stream / Error Handling\n\n**Problem:** Connection stream access and some error handling patterns have no declarations.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `connection.stream`: the raw socket property is absent from the `Connection` type\n- Error event handler types do not cover every error code pattern\n\n**Test files:**\n\n- `test/integration/connection/test-stream-errors.test.mts`\n\n---\n\n## Category 8: Promise Wrapper Access\n\n**Problem:** A promise-wrapped connection exposes `.connection` to reach the underlying callback connection, but the EventEmitter surface of that inner connection is unreachable through the types.\n\n**Affected typing file:** `promise.d.ts`\n\n**Gaps:**\n\n- `conn.connection`: reaching the underlying callback connection from a promise wrapper\n- `conn.connection.listenerCount()` and `conn.connection.emit()`: EventEmitter methods neither inherited nor exposed\n- Assorted promise wrapper methods absent from the declarations\n\n**Test files:**\n\n- `test/integration/promise-wrappers/test-promise-wrappers.test.mts`\n\n---\n\n## Category 9: PoolCluster Promise Wrapper\n\n**Problem:** The promise version of `PoolCluster` declares fewer members than the callback version.\n\n**Affected typing file:** `promise.d.ts` (PoolCluster interface)\n\n**Reference:** `typings/mysql/lib/PoolCluster.d.ts` declares the full callback surface, including the `'online'` and `'offline'` events\n\n**Gaps:**\n\n- `remove()` is absent\n- The `'online'` and `'offline'` event overloads are absent\n- The remaining method signatures do not cover every valid call pattern\n\n**Test files:**\n\n- `test/integration/pool-cluster/test-promise-wrapper.test.mts`\n\n---\n\n## Category 10: Miscellaneous\n\n**Gaps:**\n\n- `connection.close()`: exists at runtime as an alias, while the `Connection` type declares `end()` and `destroy()` only\n- `pool.config.connectionConfig`: the nested config structure has no type\n- One-off gaps specific to a single test\n\n**Test files:**\n\n- `test/global/integration/connection/test-backpressure-load-data-infile.test.mts`\n- `test/integration/test-pool-connect-error.test.mts`\n- `test/integration/config/test-connect-timeout.test.mts`\n- `test/integration/test-server-close.test.mts`\n- `test/integration/test-pool-memory-leak.test.mts`\n\n---\n\n## Confirmed Bugs in Current Types\n\nThese are wrong types, not missing ones.\n\n### Bug 1: `PoolConnection.promise()` returns the wrong type\n\n**File:** `typings/mysql/lib/PoolConnection.d.ts`\n\n**Current:** `promise(promiseImpl?: PromiseConstructor): PromisePool`\n\n**Should be:** `promise(promiseImpl?: PromiseConstructor): PromisePoolConnection`\n\nA pool connection wrapped in a promise is a promise pool connection, not a promise pool.\n\n### Bug 2: `QueryOptions.nestTables` typed as `any`\n\n**File:** `typings/mysql/lib/protocol/sequences/Query.d.ts`\n\n**Current:** `nestTables?: any`\n\n**Should be:** `nestTables?: boolean | string`\n\nThe same property in `ConnectionOptions` already carries the correct type.\n\n### Bug 3: Pool options duplicated in ConnectionOptions\n\n**File:** `typings/mysql/lib/Connection.d.ts`\n\n**Problem:** `connectionLimit`, `maxIdle`, `idleTimeout`, `queueLimit`, and `waitForConnections` are declared in both `ConnectionOptions` and `PoolOptions`. Since `PoolOptions` extends `ConnectionOptions`, they belong in `PoolOptions` alone.\n\n**Impact:** Every function taking a `ConnectionOptions`, `createConnection()` among them, accepts pool-specific options without a TypeScript error.\n","CLAUDE.md":"# MySQL2 — Agent Instructions\n\nYou are an expert Node.js developer contributing to MySQL2, a high-performance MySQL driver focused on compatibility.\n\n## Project\n\n- Minimum compatibility: **Node 14**, whatever the `engines` field in `package.json` declares.\n- Core: `/lib`, exposed through `index.js` (callback) and `promise.js` (promise-based).\n- Types: `/typings` (`.d.ts` files), exposed through `index.d.ts` (callback) and `promise.d.ts` (promise-based).\n- Off limits: `/src` is a separate effort (the TypeScript transcription) and out of scope for any task that does not explicitly ask for it. Never touch it, and never replicate a change made to `/lib`, `index.js` or `promise.js` there.\n- Documentation: `website/docs/` (Docusaurus).\n- Tests:\n  - Unit: `test/unit`, parallel.\n  - Integration: `test/integration`, parallel, alongside the unit tests.\n  - Global: `test/global`, sequential, for tests that change MySQL Server global state and need careful cleanup.\n\n---\n\n## Skills\n\n- Use the [`/lagune` skill](.claude/skills/lagune/SKILL.md) whenever you build or change code, so it improves safety by default. When it is not available, install it from its manifest with `npx -y lagune@latest pull`.\n- Read the [code review skill](.github/skills/code-review/SKILL.md) before reviewing a pull request, a diff, or a branch.\n- Use the [`/types` skill](.claude/skills/types/SKILL.md) when working on `/typings` or fixing a type error in a test.\n\n---\n\n## Comments\n\nNever add an obvious comment nor narrate a statement: code that needs a comment to be understood is usually poorly implemented, and comment length measures how bad the code underneath it is.\n\nWhen code feels like it needs an explanation, improve the implementation instead: clear names, decoupled functions with a defined scope, and proper abstractions.\n\n---\n\n## Tests\n\n**Read `test/common.test.mts` before writing any test.** It provides the shared helpers for creating connections, pools, clusters, and servers, and for reading the test configuration.\n\nThe test runner is **Poku** ([docs](https://poku.io/docs), [repo](https://github.com/wellwelwel/poku)). Test files use `.mts` (ESM TypeScript) and support top-level `await`.\n\nAssertions, utilities, and test structure come from Poku:\n\n```ts\nimport { describe, it, assert, skip, sleep, strict } from 'poku';\n```\n\n- `skip` skips the entire test file and reports it, for cases like a Deno-only test or a specific Node version.\n- `sleep` waits for a given duration: `await sleep(100)`.\n\n| File                      | Description                                                         |\n| ------------------------- | ------------------------------------------------------------------- |\n| `test/common.test.mts`    | Shared helpers                                                      |\n| `poku.config.js`          | Poku config: parallel/sequential suites, timeouts, test directories |\n| `test/docker-compose.yml` | Local environment with MySQL, Node, Deno, Bun, and coverage         |\n\n```sh\nnpm run typecheck\nnpm run lint:fix\nFILTER=test/unit/my-test.mts npx poku    # run a single file through Poku\nnpx tsx test/unit/my-test.mts            # run a single file directly\n```\n\n### Connection scope and resource cleanup\n\nNever close the connection in the same scope as an assertion that may fail. When the assertion throws, `end()` is never reached and the process hangs indefinitely.\n\nOpen and close the connection in an outer scope:\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Use nested or dedicated `describe` blocks to isolate each connection.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n\nPrefer `await conn.promise().end()` instead of wrapping callbacks in `new Promise`:\n\n```ts\n// ❌ Avoid\nawait new Promise<void>((resolve) => pool.end(() => resolve()));\n\n// ✅ Prefer\nawait pool.promise().end();\n```\n\n### Avoid timer-dependent tests\n\nNever wait on an internal timer, such as idle connection cleanup, with `setTimeout` or `sleep`. Execution timing in CI is unpredictable and the test turns flaky. Call the internal method directly, or assert the state synchronously right after the action. When the timer behavior itself is what needs coverage, isolate it so no assertion depends on wall-clock timing.\n\n### `async`/`await`\n\nPoku treats `async`/`await` just like standard JavaScript, so `describe`, `it`, and `test` are awaited **only** when the callback is asynchronous.\n\n**Asynchronous:**\n\n```ts\nawait describe('test', async () => {\n  const connection = createConnection().promise();\n\n  await it('should do something', async () => {\n    const result = await connection.query('SELECT 1');\n\n    assert(result);\n  });\n\n  await connection.end();\n});\n```\n\n**Synchronous:**\n\n```ts\ndescribe('test', () => {\n  it('should do something', () => {\n    strict.equal(1 + 1, 2);\n  });\n});\n```\n\n### Prefer promise-based API\n\nNew tests prefer the promise-based API through `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need coverage in both modes.\n\n> A recommendation, not a strict rule.\n\n```ts\nconst connection = createConnection({/* ... */}).promise();\nconst pool = createPool({/* ... */}).promise();\n\nconst cluster = createPoolCluster({/* ... */});\ncluster.add('node1', {/* ... */});\nconst clusterConnection = await cluster.promise().getConnection();\n```\n\n### TypeScript in tests\n\nNever use `as unknown as` or `any` in test files.\n\nWhen reaching for an internal that the public typings do not expose, use exactly:\n\n```ts\n// @ts-expect-error: internal access\n```\n\nWhen a type in `typings/` is wrong or missing and the contribution has no relation to that type, use exactly:\n\n```ts\n// @ts-expect-error: TODO: implement typings\n```\n\nIf the type error **is** related to the contribution, fix the type in `typings/` instead of suppressing it.\n\n---\n\n## Cursor Cloud specific instructions\n\nMySQL2 is a **library**, not a long-running app. Development means installing the Node dependencies, starting MySQL for the integration tests, then running lint, typecheck, and tests against the driver.\n\n### Services\n\n| Service                          | Required?          | Notes                                                         |\n| -------------------------------- | ------------------ | ------------------------------------------------------------- |\n| **Node.js** (14 or later, CI 22) | Yes                | `npm ci` at repo root                                         |\n| **MySQL**                        | Yes for full tests | Integration and global tests need a `test` database           |\n| **Docker**                       | Recommended        | Runs MySQL the same way as CI                                 |\n| **Docusaurus** (`website/`)      | Optional           | `cd website && npm ci && npm start`, on http://localhost:3000 |\n\n### MySQL via Docker\n\nDocker is installed on the VM, but the daemon may need a manual start when systemd is not active:\n\n```sh\nsudo dockerd > /tmp/dockerd.log 2>&1 &\n```\n\nStart MySQL, which creates the `test` database with an empty root password:\n\n```sh\nsudo docker compose -f test/docker-compose.yml up -d mysql\nnode tools/wait-up.js\n```\n\nUse `sudo docker` when the socket permission error appears. CI pins **MySQL 8.3** (`mysql:8.3`), while `test/docker-compose.yml` uses `mysql:lts`, currently 9.x. For full CI parity, run:\n\n```sh\nsudo docker run -d --name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=test -p 3306:3306 mysql:8.3\n```\n\nWithout `CI=1`, tests use an empty root password, matching docker-compose. With `CI=1`, set `MYSQL_PASSWORD=root`.\n\n### Commands\n\nSee `package.json` and `Contributing.md` for the full set.\n\n| Task        | Command                                            |\n| ----------- | -------------------------------------------------- |\n| Lint        | `npm run lint`                                     |\n| Typecheck   | `npm run typecheck`                                |\n| Tests       | `npm test` (or `FILTER=path/to/test.mts npx poku`) |\n| Build check | `npm run test:build`                               |\n| Website     | `cd website && npm ci && npm test`                 |\n\n`test/global` runs sequentially and needs elevated MySQL privileges. Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n",".claude/skills/types/SKILL.md":"---\nname: types\ndescription: 'Fix a TypeScript error in a `.mts` test file, remove an `any` or a `!`, decide between `@ts-expect-error: internal access` and `@ts-expect-error: TODO: implement typings`, or change a `.d.ts` file under `/typings`. Carries the public type inventory, the decision tree, the type architecture, and every known typing gap.'\nargument-hint: Optionally name the test file or the type to fix, or leave empty\nuser-invocable: true\n---\n\n# MySQL2 — Typings Skill\n\nReference for fixing TypeScript types in `.mts` test files, and for guiding the conversion of `/lib` to TypeScript.\n\nFor how the types are organized, connected, and composed, see [`rules/architecture.md`](rules/architecture.md).\nFor the categorized list of known type gaps, see [`rules/gaps.md`](rules/gaps.md).\n\n---\n\n## 1. Public type inventory\n\nEvery type below is importable from the library. Check this inventory before reaching for `@ts-expect-error`.\n\n### From `index.js` (callback API)\n\n| Category    | Types                                                                                                                                               | Source (relative to `typings/mysql/`)                   |\n| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| Packets     | `RowDataPacket`, `ResultSetHeader`, `FieldPacket`, `OkPacket` _(deprecated)_, `Field` _(deprecated, now `TypeCastField`)_, `ProcedureCallPacket<T>` | `lib/protocol/packets/*.d.ts`                           |\n| Connection  | `Connection`, `ConnectionOptions` (40+ props), `ConnectionState`, `SslOptions`                                                                      | `lib/Connection.d.ts`                                   |\n| Pool        | `Pool`, `PoolOptions` (extends ConnectionOptions), `PoolConnection`                                                                                 | `lib/Pool.d.ts`, `lib/PoolConnection.d.ts`              |\n| Cluster     | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                | `lib/PoolCluster.d.ts`                                  |\n| Query       | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`                                                              | `lib/protocol/sequences/Query.d.ts`                     |\n| Prepare     | `Prepare`, `PrepareStatementInfo`                                                                                                                   | `lib/protocol/sequences/Prepare.d.ts`                   |\n| Parsers     | `TypeCast`, `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType`                                                                     | `lib/parsers/typeCast.d.ts`                             |\n| Constants   | `Types`, `Charsets`, `CharsetToEncoding`                                                                                                            | `lib/constants/*.d.ts`                                  |\n| Auth        | `AuthPlugin`, `authPlugins`                                                                                                                         | `lib/Auth.d.ts`                                         |\n| Server      | `Server`, `OkPacketParams`, `ErrorPacketParams`                                                                                                     | `lib/Server.d.ts`, `lib/protocol/packets/params/*.d.ts` |\n| Tracing     | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`                                                        | `lib/Tracing.d.ts`                                      |\n| Utilities   | `escape()`, `escapeId()`, `format()`, `raw()`, `setMaxParserCache()`, `clearParserCache()`                                                          | `index.d.ts`                                            |\n| SQL escaper | `Raw`, `SqlValue`, `Timezone`                                                                                                                       | re-export from `sql-escaper`                            |\n| Factory     | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`                                                                       | `index.d.ts`                                            |\n| Config      | `ConnectionConfig` (interface with static methods: `mergeFlags`, `getDefaultFlags`, `getCharsetNumber`, `getSSLProfile`, `parseUrl`)                | `index.d.ts`                                            |\n\n### From `promise.js`\n\nRe-exports everything above, with these differences:\n\n| Type                                           | Difference vs callback                                                                 |\n| ---------------------------------------------- | -------------------------------------------------------------------------------------- |\n| `Connection`                                   | `.query()`/`.execute()` return `Promise<[T, FieldPacket[]]>` instead of `Query`        |\n| `Pool` (interface extends Connection)          | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PoolConnection` (extends Connection)          | has `release()` and `Symbol.asyncDispose`                                              |\n| `PoolCluster` (interface extends EventEmitter) | `.getConnection()` returns `Promise<PoolConnection>`, `.end()` returns `Promise<void>` |\n| `PoolNamespace` (interface)                    | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PreparedStatementInfo`                        | `.execute()` returns `Promise<[T, FieldPacket[]]>`, `.close()` returns `Promise<void>` |\n| `createConnection()`                           | returns `Promise<Connection>`, against a synchronous `Connection` in the callback API  |\n| `createPool()`                                 | returns `Pool` synchronously, same as the callback API                                 |\n| `createPoolCluster()`                          | returns `PoolCluster` synchronously, same as the callback API                          |\n\n### Connection methods (callback-based)\n\n- `.query(sql)`, `.query(sql, values)`, `.query(options)`, `.query(options, values)` return `Query`.\n- `.execute(sql)`, `.execute(sql, values)`, `.execute(options)`, `.execute(options, values)` return `Query`.\n- `.prepare(sql, callback?)` returns `Prepare`.\n- `.unprepare(sql)` returns `PrepareStatementInfo`.\n- `.promise(promiseImpl?)` returns the promise-wrapped connection.\n- `.connect(cb?)`, `.end(cb?)`, `.destroy()`, `.pause()`, `.resume()`, `.ping(cb?)`.\n- `.beginTransaction(cb)`, `.commit(cb?)`, `.rollback(cb)`, `.changeUser(options, cb?)`.\n- `.escape(value)`, `.escapeId(value)`, `.format(sql, values?)`.\n\n### Connection methods (server-side, on incoming connections)\n\n- `.serverHandshake(args)` initiates the server handshake.\n- `.writeOk(params?: OkPacketParams)` sends an OK packet.\n- `.writeError(params?: ErrorPacketParams)` sends an error packet.\n- `.writeEof(warnings?, statusFlags?)` sends an EOF packet.\n- `.writeTextResult(rows?, columns?)` sends a result set.\n- `.writePacket(packet)` sends a raw packet.\n\n### Union types\n\n- `QueryResult` = `OkPacket | ResultSetHeader | ResultSetHeader[] | RowDataPacket[] | RowDataPacket[][] | OkPacket[] | ProcedureCallPacket`\n- `ConnectionState` = `'disconnected' | 'protocol_handshake' | 'connected' | 'authenticated' | 'error'`\n\n---\n\n## 2. Decision tree for type errors\n\nClassify the error before fixing it.\n\n### 2.1. The library type exists\n\nWhen the calling function already declares the callback signature, let TypeScript infer the parameter types instead of annotating them again.\n\n```ts\n// ✅ Correct: the types flow from the .query() signature\nconnection.query('SELECT 1', (err, rows, fields) => { ... });\n\n// ❌ Wrong: annotations that duplicate what inference already provides\nconnection.query('SELECT 1', (err: QueryError | null, rows: RowDataPacket[]) => { ... });\n```\n\nThis is the fix for every `any` on a callback parameter. Remove the annotation, do not replace it:\n\n| Pattern with `any`                   | Fix                   |\n| ------------------------------------ | --------------------- |\n| `(err: any, result: any)`            | `(err, result)`       |\n| `(err: any, rows: any)`              | `(err, rows)`         |\n| `(err: any, rows: any, fields: any)` | `(err, rows, fields)` |\n| `(err: any, connection: any)`        | `(err, connection)`   |\n| `(err: any)`                         | `(err)`               |\n\nAnnotate explicitly only where inference cannot reach, such as a standalone callback or a calling function without typings:\n\n```ts\nimport type { TypeCastField } from '../../index.js';\n\nconst conn = createConnection({\n  typeCast: (field: TypeCastField) => field.string(),\n});\n```\n\nQuery and execute results in the promise API still need their generic:\n\n```ts\nimport type { RowDataPacket } from '../../promise.js';\n\nconst [rows] = await connection.query<RowDataPacket[]>('SELECT 1');\n```\n\n### 2.2. The API exists at runtime, the types do not\n\nThe call works, but `/typings` declares it incompletely or not at all. Mark it with `@ts-expect-error: TODO: implement typings`, which flags the work for a later typings fix.\n\nWorking runtime code that TypeScript rejects is a typings gap, never a code bug. Wrong arity, a missing overload, an unrecognized property: all of them mean the declaration is behind the implementation. Add the comment and leave the call exactly as it was. Never rewrite working code to satisfy a wrong type definition.\n\n```ts\n// @ts-expect-error: TODO: implement typings\nconst server = mysql.createServer();\n\n// @ts-expect-error: TODO: implement typings\nserver.listen(3307, () => {\n  /* ... */\n});\n```\n\nEvery known gap is categorized in [`rules/gaps.md`](rules/gaps.md).\n\n### 2.3. The property is internal\n\nThe test deliberately reaches for an implementation detail that sits outside the public API and will never be typed. Mark it with `@ts-expect-error: internal access`.\n\nAnything starting with `_` qualifies: `connection._protocol`, `connection._statements`, `connection._handshakePacket`, `pool._freeConnections`, `pool._allConnections`, `packet._buf`. So does `connection.stream`, the raw socket, despite the missing underscore.\n\n```ts\n// @ts-expect-error: internal access\nconst protocol = connection._protocol;\n\n// @ts-expect-error: internal access\nassert.equal(pool._freeConnections.length, 0);\n```\n\n### 2.4. The shape belongs to the test\n\nA structure the test invents, with no library type behind it. Declare a `type` alias, never an `interface`, or annotate it inline.\n\n```ts\ntype DateTimeRow = RowDataPacket & {\n  date: string | null;\n  time: string | null;\n  datetime: string | null;\n};\n\nconst [rows] = await connection.query<DateTimeRow[]>(\n  'SELECT date, time, datetime FROM test_table'\n);\n\n// A one-off shape intersects RowDataPacket inline\nconst [rows] = await connection.query<(RowDataPacket & { count: number })[]>(\n  'SELECT COUNT(*) as count FROM test_table'\n);\n```\n\n---\n\n## 3. Import conventions\n\n1. **`import type`** for type-only imports:\n\n   ```ts\n   import type { RowDataPacket, FieldPacket } from '../../promise.js';\n   import type { TypeCastField, ConnectionOptions } from '../../index.js';\n   ```\n\n2. **`.mjs` extension** when importing another `.mts` file, since TypeScript `NodeNext` resolves `.mjs` to `.mts`:\n\n   ```ts\n   import { createConnection } from '../../common.test.mjs';\n   ```\n\n3. **`.js` extension** for library imports:\n\n   ```ts\n   import mysql from '../../../index.js';\n   import type { RowDataPacket } from '../../../promise.js';\n   ```\n\n---\n\n## 4. Process\n\n1. **Read** the target `.mts` file.\n2. **Run** `npx tsc --noEmit --pretty <file>` to collect the compiler errors.\n3. **Audit** every `any` (`: any`, `as any`, `<any>`, callback parameters) and every non-null assertion `!` by hand. `noImplicitAny: false` means `tsc` never reports `any`, and `!` always compiles, so a clean build proves nothing about either.\n4. **Classify** each error, each `any`, and each `!` against the decision tree above.\n5. **Fix** them one at a time:\n   - Drop the annotation from callback parameters and let inference work.\n   - Replace `any` in a variable declaration with the real type.\n   - Replace `!` with optional chaining: `fields![0].name` becomes `fields?.[0].name`.\n   - Add the missing `import type` statements.\n   - Add the generic to `.query<T>()` and `.execute<T>()`.\n   - Add `// @ts-expect-error: TODO: implement typings` where the library type is missing.\n   - Add `// @ts-expect-error: internal access` where the property is internal.\n   - Declare a `type` alias for a test-local structure.\n6. **Re-run** `npx tsc --noEmit --pretty <file>`.\n7. **Re-audit** the file for `any` and `!`.\n8. **Done** when all three hold: no compiler errors, no `any`, no `!`.\n\n---\n\n## 5. Pitfalls\n\n- **`any` is forbidden.** Every `: any`, `as any`, and `<any>` has to go, and step 3 above is the only thing that finds them.\n- **`!` is forbidden.** `fields![0].name` is a hack that silences the compiler, `fields?.[0].name` is safe access.\n- **The two `@ts-expect-error` comments are not interchangeable.** `TODO: implement typings` tracks work the library owes, `internal access` marks something intentionally private. Collapsing them loses the distinction that makes the first list actionable.\n- **A change in `/typings` lands on both APIs.** Verify the callback entry (`typings/mysql/index.d.ts`) and the promise entry (`promise.d.ts`) together, and remember that touching `QueryableBase` or `ExecutableBase` reaches `Connection`, `Pool`, and `PoolNamespace` at once.\n"},"items":[{"name":"architecture.md","path":".claude/skills/types/rules/architecture.md","rawUrl":"https://raw.githubusercontent.com/sidorares/node-mysql2/HEAD/.claude/skills/types/rules/architecture.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# MySQL2 — Type Architecture\n\nInternal structure of the `.d.ts` files in `/typings`: how the types are organized, connected, and composed.\n\nA ⚠️ marks a spot where the declarations do not match the runtime. This file records the symptom only. [`gaps.md`](gaps.md) owns what each hole is, which tests hit it, and how to close it.\n\n---\n\n## 1. Mixin Pattern\n\n`Connection` and `Pool` get `query()` and `execute()` from mixin functions rather than direct declarations, which is what makes the whole query and execute API work.\n\n`QueryableBase<T>(Base?)` and `ExecutableBase<T>(Base?)` are higher-order functions: each takes a base class constructor and returns an extended class carrying the method overloads.\n\n### Callback versions (return `Query`)\n\nEach method has 4 overloads: `(sql, cb?)`, `(sql, values, cb?)`, `(options, cb?)`, `(options, values, cb?)`.\n\n- `typings/mysql/lib/protocol/sequences/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/ExecutableBase.d.ts`\n\n### Promise versions (return `Promise<[T, FieldPacket[]]>`)\n\nEach method has 2 overloads: `(sql, values?)`, `(options, values?)`.\n\n- `typings/mysql/lib/protocol/sequences/promise/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/promise/ExecutableBase.d.ts`\n\n### Who uses the mixins\n\n| Class/Interface            | Mixin Usage                                                                  |\n| -------------------------- | ---------------------------------------------------------------------------- |\n| `Connection` (callback)    | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `Pool` (callback)          | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `PoolCluster` (callback)   | `extends EventEmitter` directly, with **no mixins**                          |\n| `PoolNamespace` (callback) | interface extends `QueryableAndExecutableBase`                               |\n| `Connection` (promise)     | `extends QueryableAndExecutableBase` (anonymous class composing both mixins) |\n| `Pool` (promise)           | interface extends `Connection` (inherits mixins transitively)                |\n| `PoolNamespace` (promise)  | interface extends `QueryableAndExecutableBase`                               |\n\n### Helper type\n\nThe constraint on every mixin base class parameter:\n\n```ts\ntype QueryableConstructor<T = object> = new (...args: any[]) => T;\n```\n\n---\n\n## 2. Dual API (Callback vs Promise)\n\nMySQL2 exposes two parallel APIs: callback-based and promise-based.\n\n### Entry points\n\n| API      | Entry File            | Primary Type Source             |\n| -------- | --------------------- | ------------------------------- |\n| Callback | `index.d.ts` (root)   | `typings/mysql/index.d.ts`      |\n| Promise  | `promise.d.ts` (root) | declares promise classes inline |\n\n### How promise wraps callback\n\n1. `promise.d.ts` re-exports everything from `index.js`.\n2. It imports the promise-specific mixin functions from `typings/mysql/lib/protocol/sequences/promise/`.\n3. It composes an anonymous `QueryableAndExecutableBase` class:\n   ```ts\n   declare class QueryableAndExecutableBase extends QueryableBaseClass(\n     ExecutableBaseClass(EventEmitter)\n   ) {}\n   ```\n4. The promise classes extend or implement that base.\n\n### Key differences\n\n| Aspect                       | Callback                      | Promise                       |\n| ---------------------------- | ----------------------------- | ----------------------------- |\n| `query()`/`execute()` return | `Query`                       | `Promise<[T, FieldPacket[]]>` |\n| `createConnection()` return  | `Connection` (sync)           | `Promise<Connection>` (async) |\n| `createPool()` return        | `Pool` (sync)                 | `Pool` (sync)                 |\n| `end()`                      | `void` with optional callback | `Promise<void>`               |\n| Resource disposal            | `Symbol.dispose` (sync)       | `Symbol.asyncDispose` (async) |\n| `.promise()` method          | Available on callback classes | Not applicable                |\n\n### Bridging\n\nCallback classes carry a `.promise()` method returning the promise-wrapped version:\n\n- `Connection.promise()` returns `PromiseConnection`.\n- `Pool.promise()` returns `PromisePool`.\n- `PoolConnection.promise()` returns `PromisePool`. ⚠️\n\n---\n\n## 3. Class Hierarchy\n\n### Callback\n\n```\nEventEmitter\n├── Connection extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .authorized: boolean\n│   ├── .state: ConnectionState (readonly)\n│   ├── .sequenceId: number\n│   ├── static createQuery(): Query\n│   ├── beginTransaction(), connect(), commit(), rollback()\n│   ├── changeUser(), end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   ├── prepare(), unprepare(), ping()\n│   ├── serverHandshake(), writeOk(), writeError(), writeEof()\n│   ├── writeTextResult(), writePacket()\n│   ├── promise(): PromiseConnection\n│   └── Symbol.dispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       ├── promise(): PromisePool  ⚠️\n│       └── Symbol.dispose\n│\n├── Pool extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: PoolOptions\n│   ├── getConnection(cb), releaseConnection(conn)\n│   ├── end(cb?), unprepare(sql)\n│   ├── promise(): PromisePool\n│   ├── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│   └── Symbol.dispose\n│   ⚠️ escape(), escapeId(), format() exist at runtime and are undeclared\n│\n├── PoolCluster extends EventEmitter  (NO mixins)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── remove(pattern), end(cb?)\n│   ├── getConnection(cb), getConnection(group, cb), getConnection(group, selector, cb)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'online' | 'offline' | 'remove' | 'warn'\n│   └── Symbol.dispose\n│\n├── Server extends EventEmitter\n│   ├── .connections: Connection[]\n│   ├── listen(port): Server\n│   ├── close(callback): void\n│   ⚠️ listen(port, cb) and close() without a callback are undeclared\n│\n└── Sequence extends EventEmitter\n    ├── Query extends Sequence\n    │   ├── .sql: string\n    │   ├── start(), determinePacket(), stream()\n    │   └── on: 'error' | 'fields' | 'result' | 'end'\n    │\n    └── Prepare extends Sequence\n        ├── .sql: string\n        ├── start(), determinePacket(), stream()\n        └── on: 'error' | 'fields' | 'result' | 'end'\n\nPoolNamespace (interface extends QueryableAndExecutableBase)\n├── getConnection(cb)\n```\n\n### Promise\n\n```\nEventEmitter\n├── Connection extends QueryableAndExecutableBase\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .state: ConnectionState (readonly)\n│   ├── connect(), ping(), beginTransaction(), commit(), rollback()\n│   ├── changeUser(), prepare(), unprepare()\n│   ├── end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   └── Symbol.asyncDispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       └── Symbol.asyncDispose\n│\n├── Pool (interface extends Connection)\n│   ├── .pool: CorePool (callback Pool reference)\n│   ├── getConnection(): Promise<PoolConnection>\n│   ├── releaseConnection(conn)\n│   ├── end(): Promise<void>\n│   └── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│\n├── PoolCluster (interface extends EventEmitter)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── end(): Promise<void>\n│   ├── getConnection(), getConnection(group), getConnection(group, selector)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'remove' | 'warn'\n│   ├── Symbol.asyncDispose\n│   ⚠️ on 'online' and 'offline' are declared in the callback version only\n│   ⚠️ remove() is undeclared\n│\n└── PoolNamespace (interface extends QueryableAndExecutableBase)\n    └── getConnection(): Promise<PoolConnection>\n```\n\n---\n\n## 4. File Map\n\nEvery type definition file, its purpose, and its key exports.\n\n### Root entry points\n\n| File            | Purpose             | Key Exports                                                                                                                                                  |\n| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `/index.d.ts`   | Root callback entry | Re-exports from `typings/mysql/index.js`                                                                                                                     |\n| `/promise.d.ts` | Root promise entry  | `Connection`, `Pool`, `PoolConnection`, `PoolCluster`, `PoolNamespace`, `PreparedStatementInfo`, `createConnection()`, `createPool()`, `createPoolCluster()` |\n\n### Main API (`typings/mysql/`)\n\n| File         | Purpose          | Key Exports                                                                                                                                                          |\n| ------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `index.d.ts` | Callback API hub | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`, `escape`, `escapeId`, `format`, `raw`, `ConnectionConfig`, re-exports all sub-modules |\n\n### Core classes (`typings/mysql/lib/`)\n\n| File                  | Purpose                    | Key Exports                                                                                                                                                                                      |\n| --------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `Connection.d.ts`     | Connection class + options | `Connection`, `ConnectionOptions` (40+ props), `SslOptions`, `ConnectionState`                                                                                                                   |\n| `Pool.d.ts`           | Connection pool            | `Pool`, `PoolOptions`                                                                                                                                                                            |\n| `PoolCluster.d.ts`    | Pool cluster               | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                                                             |\n| `PoolConnection.d.ts` | Pool connection            | `PoolConnection`                                                                                                                                                                                 |\n| `Server.d.ts`         | MySQL server (testing)     | `Server`                                                                                                                                                                                         |\n| `Auth.d.ts`           | Auth plugins               | `AuthPlugin`, `authPlugins` (4 built-in plugins)                                                                                                                                                 |\n| `Tracing.d.ts`        | diagnostics_channel        | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`, `dc`, `hasTracingChannel`, `shouldTrace()`, `traceCallback()`, `tracePromise()`, channel variables |\n\n### Protocol sequences (`typings/mysql/lib/protocol/sequences/`)\n\n| File                  | Purpose                | Key Exports                                                                                                    |\n| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- |\n| `Sequence.d.ts`       | Base class             | `Sequence` (extends EventEmitter, minimal)                                                                     |\n| `Query.d.ts`          | Query sequence         | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`, `QueryableConstructor` |\n| `Prepare.d.ts`        | Prepare sequence       | `Prepare`, `PrepareStatementInfo`                                                                              |\n| `ExecutableBase.d.ts` | Callback execute mixin | `ExecutableBase()`                                                                                             |\n| `QueryableBase.d.ts`  | Callback query mixin   | `QueryableBase()`                                                                                              |\n\n### Promise sequences (`typings/mysql/lib/protocol/sequences/promise/`)\n\n| File                  | Purpose               | Key Exports        |\n| --------------------- | --------------------- | ------------------ |\n| `ExecutableBase.d.ts` | Promise execute mixin | `ExecutableBase()` |\n| `QueryableBase.d.ts`  | Promise query mixin   | `QueryableBase()`  |\n\n### Packets (`typings/mysql/lib/protocol/packets/`)\n\n| File                            | Purpose                             | Key Exports                                             |\n| ------------------------------- | ----------------------------------- | ------------------------------------------------------- |\n| `index.d.ts`                    | Packet hub                          | `QueryResult` (union type), re-exports all packet types |\n| `OkPacket.d.ts`                 | OK result _(deprecated)_            | `OkPacket`                                              |\n| `ResultSetHeader.d.ts`          | Result metadata                     | `ResultSetHeader`                                       |\n| `RowDataPacket.d.ts`            | Data row                            | `RowDataPacket`                                         |\n| `FieldPacket.d.ts`              | Column metadata                     | `FieldPacket`                                           |\n| `Field.d.ts`                    | TypeCast field _(deprecated alias)_ | `Field`, an alias of `TypeCastField`                    |\n| `ProcedurePacket.d.ts`          | Stored procedure result             | `ProcedureCallPacket<T>`                                |\n| `params/OkPacketParams.d.ts`    | Server OK params                    | `OkPacketParams`                                        |\n| `params/ErrorPacketParams.d.ts` | Server error params                 | `ErrorPacketParams`                                     |\n\n### Constants (`typings/mysql/lib/constants/`)\n\n| File                     | Purpose                 | Key Exports                                                         |\n| ------------------------ | ----------------------- | ------------------------------------------------------------------- |\n| `index.d.ts`             | Constants hub           | Re-exports Types, Charsets, CharsetToEncoding                       |\n| `Types.d.ts`             | MySQL column types      | `Types` (hex codes 0x00-0xff + named: DECIMAL, TINY, ..., GEOMETRY) |\n| `Charsets.d.ts`          | Charset/collation IDs   | `Charsets` (270+ named entries)                                     |\n| `CharsetToEncoding.d.ts` | Charset to encoding map | `CharsetToEncoding` (string[])                                      |\n\n### Parsers (`typings/mysql/lib/parsers/`)\n\n| File               | Purpose         | Key Exports                                                                       |\n| ------------------ | --------------- | --------------------------------------------------------------------------------- |\n| `index.d.ts`       | Parsers hub     | Re-exports as `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType` |\n| `typeCast.d.ts`    | Type casting    | `TypeCast`, `Field` (= TypeCastField), `Geometry`, `Next`, `Type`                 |\n| `ParserCache.d.ts` | Cache utilities | `setMaxParserCache()`, `clearParserCache()`                                       |\n\n---\n\n## 5. ConnectionOptions property map\n\nEvery property declared in `typings/mysql/lib/Connection.d.ts`.\n\n### With JSDoc documentation\n\n| Property                | Type                                                    | Default             | Description                          |\n| ----------------------- | ------------------------------------------------------- | ------------------- | ------------------------------------ |\n| `decimalNumbers`        | `boolean`                                               | `false`             | Return DECIMAL/NEWDECIMAL as numbers |\n| `user`                  | `string`                                                | none                | MySQL user                           |\n| `password`              | `string`                                                | none                | MySQL password                       |\n| `password1`             | `string`                                                | none                | Alias for password (MFA)             |\n| `password2`             | `string`                                                | none                | 2nd factor auth password             |\n| `password3`             | `string`                                                | none                | 3rd factor auth password             |\n| `database`              | `string`                                                | none                | Database name                        |\n| `charset`               | `string`                                                | `'UTF8_GENERAL_CI'` | Connection charset/collation         |\n| `host`                  | `string`                                                | `'localhost'`       | Database hostname                    |\n| `port`                  | `number`                                                | `3306`              | Port number                          |\n| `localAddress`          | `string`                                                | none                | Source IP for TCP                    |\n| `socketPath`            | `string`                                                | none                | Unix socket path                     |\n| `timezone`              | `Timezone`                                              | `'local'`           | Timezone for dates                   |\n| `connectTimeout`        | `number`                                                | `10000`             | Connection timeout (ms)              |\n| `stringifyObjects`      | `boolean`                                               | `false`             | Stringify objects in queries         |\n| `insecureAuth`          | `boolean`                                               | `false`             | Allow old auth method                |\n| `infileStreamFactory`   | `(path: string) => Readable`                            | none                | Custom stream for LOAD DATA INFILE   |\n| `typeCast`              | `TypeCast`                                              | `true`              | Column value conversion              |\n| `queryFormat`           | `(query: string, values: any) => void`                  | none                | Custom query format                  |\n| `supportBigNumbers`     | `boolean`                                               | `false`             | Handle BIGINT/DECIMAL                |\n| `bigNumberStrings`      | `boolean`                                               | `false`             | Return big numbers as strings        |\n| `dateStrings`           | `boolean \\| Array<'TIMESTAMP' \\| 'DATETIME' \\| 'DATE'>` | `false`             | Return dates as strings              |\n| `debug`                 | `any`                                                   | `false`             | Print packets to stdout              |\n| `trace`                 | `boolean`                                               | `true`              | Stack traces on errors               |\n| `multipleStatements`    | `boolean`                                               | `false`             | Allow multiple statements            |\n| `flags`                 | `Array<string>`                                         | none                | Connection flags                     |\n| `ssl`                   | `string \\| SslOptions`                                  | none                | SSL configuration                    |\n| `rowsAsArray`           | `boolean`                                               | none                | Return rows as arrays                |\n| `enableKeepAlive`       | `boolean`                                               | `true`              | Socket keep-alive                    |\n| `keepAliveInitialDelay` | `number`                                                | `0`                 | Keep-alive initial delay             |\n\n### Without JSDoc documentation\n\n| Property                | Type                                       | Notes                          |\n| ----------------------- | ------------------------------------------ | ------------------------------ |\n| `charsetNumber`         | `number`                                   | Numeric charset ID             |\n| `compress`              | `boolean`                                  | Protocol compression           |\n| `authSwitchHandler`     | `(data: any, callback: () => void) => any` | Auth switch handler            |\n| `connectAttributes`     | `{ [param: string]: any }`                 | Connection attributes          |\n| `isServer`              | `boolean`                                  | Server mode flag               |\n| `maxPreparedStatements` | `number`                                   | LRU cache limit                |\n| `namedPlaceholders`     | `boolean`                                  | Named placeholder support      |\n| `nestTables`            | `boolean \\| string`                        | Nest tables in results         |\n| `passwordSha1`          | `string`                                   | Pre-hashed password            |\n| `pool`                  | `any`                                      | Pool reference                 |\n| `stream`                | `any`                                      | Custom stream                  |\n| `uri`                   | `string`                                   | Connection URI                 |\n| `connectionLimit`       | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `maxIdle`               | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `idleTimeout`           | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `Promise`               | `any`                                      | Promise constructor            |\n| `queueLimit`            | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `waitForConnections`    | `boolean`                                  | ⚠️ Duplicated from PoolOptions |\n| `disableEval`           | `boolean`                                  | Disable eval in parsers        |\n| `authPlugins`           | `{ [key: string]: AuthPlugin }`            | Custom auth plugins            |\n| `jsonStrings`           | `boolean`                                  | Force JSON as string           |\n| `gracefulEnd`           | `boolean`                                  | Graceful pool end              |\n\n### PoolOptions (extends ConnectionOptions)\n\nAdds these properties with JSDoc, in `typings/mysql/lib/Pool.d.ts`:\n\n| Property             | Type      | Default                   |\n| -------------------- | --------- | ------------------------- |\n| `waitForConnections` | `boolean` | `true`                    |\n| `connectionLimit`    | `number`  | `10`                      |\n| `maxIdle`            | `number`  | same as `connectionLimit` |\n| `idleTimeout`        | `number`  | `60000`                   |\n| `queueLimit`         | `number`  | `0`                       |\n\n---\n\n## 6. QueryOptions vs ConnectionOptions\n\n### Shared options (override per-query)\n\nThese exist in both `ConnectionOptions` and `QueryOptions`, so a query can override the connection:\n\n| Option                | ConnectionOptions        | QueryOptions  |\n| --------------------- | ------------------------ | ------------- |\n| `typeCast`            | ✅                       | ✅            |\n| `nestTables`          | ✅ (`boolean \\| string`) | ✅ (`any` ⚠️) |\n| `rowsAsArray`         | ✅                       | ✅            |\n| `supportBigNumbers`   | ✅                       | ✅            |\n| `bigNumberStrings`    | ✅                       | ✅            |\n| `dateStrings`         | ✅                       | ✅            |\n| `timezone`            | ✅                       | ✅            |\n| `infileStreamFactory` | ✅                       | ✅            |\n| `namedPlaceholders`   | ✅ (undocumented)        | ✅            |\n\n### QueryOptions-only\n\n| Option    | Type          | Description              |\n| --------- | ------------- | ------------------------ |\n| `sql`     | `string`      | The SQL query (required) |\n| `values`  | `QueryValues` | Query parameter values   |\n| `timeout` | `number`      | Operation timeout (ms)   |\n\n### Not in QueryOptions but available at runtime\n\n| Option             | Status                                                       |\n| ------------------ | ------------------------------------------------------------ |\n| `insertIdAsNumber` | Missing from both QueryOptions and ConnectionOptions typings |\n| `decimalNumbers`   | Only in ConnectionOptions, not in QueryOptions               |\n","isInternal":false,"tokens":4813,"sizeBytes":26704},{"name":"gaps.md","path":".claude/skills/types/rules/gaps.md","rawUrl":"https://raw.githubusercontent.com/sidorares/node-mysql2/HEAD/.claude/skills/types/rules/gaps.md","title":"Rules Skill","category":"anthropic-skill","format":"markdown","content":"# MySQL2 — Known Type Gaps\n\nEvery occurrence of `@ts-expect-error: TODO: implement typings` across the test files, grouped by root cause. Each one is a runtime API that works correctly and is not yet reflected in the `.d.ts` files in `/typings`.\n\nFor the current count, run `grep -r \"TODO: implement typings\" test/ | wc -l`.\n\n[`architecture.md`](architecture.md) marks where each of these sits in the type structure.\n\n---\n\n## Category 1: Pool Methods Missing\n\n**Problem:** `Pool` inherits `query()` and `execute()` from the mixins, but the escaping and formatting helpers it exposes at runtime are absent from its type.\n\n**Affected typing file:** `typings/mysql/lib/Pool.d.ts`\n\n**Runtime source:** `lib/base/pool.js`, which delegates to `connection.escape()`, `connection.escapeId()`, and `connection.format()`\n\n**Gaps:**\n\n- `pool.escape()`\n- `pool.escapeId()`\n- `pool.format()`\n- `mysql.createPoolPromise()`, a factory function with no type at all\n\n**Test files:**\n\n- `test/integration/test-pool.test.mts`\n\n**Fix:** Add `escape()`, `escapeId()`, and `format()` to the `Pool` class in `Pool.d.ts`, matching the signatures on `Connection`.\n\n---\n\n## Category 2: PrepareStatementInfo Incomplete\n\n**Problem:** `PrepareStatementInfo` declares only `close()` and `execute()`, while the runtime object carries more.\n\n**Affected typing file:** `typings/mysql/lib/protocol/sequences/Prepare.d.ts`\n\n**Runtime source:** `lib/commands/prepare.js`\n\n**Missing properties:**\n\n- `.parameters`: the column and parameter definitions\n- `.columns`: the column definitions\n- `.id`: the numeric statement ID\n\n**Test files:**\n\n- `test/global/integration/regressions/2052.test.mts`\n- `test/integration/connection/test-prepare-simple.test.mts`\n\n**Fix:** Add the three properties to `PrepareStatementInfo`, then mirror the change on the promise version `PreparedStatementInfo` in `promise.d.ts`.\n\n---\n\n## Category 3: Server / createServer\n\n**Problem:** The `Server` class and the `createServer()` factory declare narrower signatures than the runtime accepts.\n\n**Affected typing files:**\n\n- `typings/mysql/index.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Runtime source:** `lib/server.js`\n\n**Gaps:**\n\n- `mysql.createServer()`: the runtime allows no arguments, the type requires a `handler`\n- `server.listen(port, callback)`: the type accepts `(port: number)` only\n- `server.close()`: the runtime allows no arguments, the type requires a callback\n\n**Test files:**\n\n- `test/unit/pool-cluster/test-connection-error-remove.test.mts`\n- `test/unit/pool-cluster/test-restore.test.mts`\n- `test/unit/pool-cluster/test-restore-events.test.mts`\n- `test/unit/pool-cluster/test-remove-by-name.test.mts`\n- `test/unit/pool-cluster/test-remove-by-pattern.test.mts`\n- `test/unit/pool-cluster/test-connection-retry.test.mts`\n- `test/integration/connection/test-server-listen.test.mts`\n\n**Fix:** Add the overloads to `Server.listen()` and `Server.close()`, and make the `createServer()` handler optional.\n\n---\n\n## Category 4: Connection Options in Wrong Contexts\n\n**Problem:** Some options are declared in `ConnectionOptions` but do not flow through every API path that accepts them.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `passwordSha1` in `changeUser()`: `changeUser()` accepts `ConnectionOptions`, yet inference fails on some call patterns\n- `uri` in `createPoolCluster()`: the option is declared in `ConnectionOptions` but does not reach this path\n\n**Test files:**\n\n- `test/global/integration/connection/test-change-user.test.mts`\n- `test/global/integration/connection/test-change-user-plugin-auth.test.mts`\n- `test/common.test.mts`\n\n---\n\n## Category 5: Auth Switch / Multi-Factor\n\n**Problem:** The auth plugin system works at runtime, but the declarations do not cover the server-side handshake or the auth switch protocol.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/Connection.d.ts`\n- `typings/mysql/lib/Auth.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Gaps:**\n\n- The server-side connection handler, `server.on('connection', conn => ...)`: the auth flow methods on `conn` are not fully typed\n- `conn.serverHandshake()`: takes an object with auth plugin configuration, typed as `any`\n- Multi-factor auth callback chains are absent from the types\n- Auth switch response handling is incomplete\n\n**Test files:**\n\n- `test/integration/test-auth-switch.test.mts`\n- `test/integration/test-auth-switch-plugin-error.test.mts`\n- `test/integration/test-auth-switch-plugin-async-error.test.mts`\n- `test/integration/test-auth-switch-multi-factor.test.mts`\n- `test/integration/connection/test-change-user-multi-factor.test.mts`\n\n**Fix:** Type the `serverHandshake()` parameter object, add the server-side connection event types, and type the auth plugin response chain.\n\n---\n\n## Category 6: Query Result Refinements\n\n**Problem:** A query property and a nested config path have no declarations.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/protocol/sequences/Query.d.ts`\n- `typings/mysql/lib/Pool.d.ts`\n\n**Gaps:**\n\n- `Query.values`: exists at runtime, while the `Query` class declares `.sql` only\n- `pool.config.connectionConfig.namedPlaceholders`: `Pool.config` is a `PoolOptions`, which exposes no `connectionConfig` sub-object\n\n**Test files:**\n\n- `test/integration/connection/test-named-placeholders.test.mts`\n- `test/integration/connection/test-multiple-results.test.mts`\n\n---\n\n## Category 7: Stream / Error Handling\n\n**Problem:** Connection stream access and some error handling patterns have no declarations.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `connection.stream`: the raw socket property is absent from the `Connection` type\n- Error event handler types do not cover every error code pattern\n\n**Test files:**\n\n- `test/integration/connection/test-stream-errors.test.mts`\n\n---\n\n## Category 8: Promise Wrapper Access\n\n**Problem:** A promise-wrapped connection exposes `.connection` to reach the underlying callback connection, but the EventEmitter surface of that inner connection is unreachable through the types.\n\n**Affected typing file:** `promise.d.ts`\n\n**Gaps:**\n\n- `conn.connection`: reaching the underlying callback connection from a promise wrapper\n- `conn.connection.listenerCount()` and `conn.connection.emit()`: EventEmitter methods neither inherited nor exposed\n- Assorted promise wrapper methods absent from the declarations\n\n**Test files:**\n\n- `test/integration/promise-wrappers/test-promise-wrappers.test.mts`\n\n---\n\n## Category 9: PoolCluster Promise Wrapper\n\n**Problem:** The promise version of `PoolCluster` declares fewer members than the callback version.\n\n**Affected typing file:** `promise.d.ts` (PoolCluster interface)\n\n**Reference:** `typings/mysql/lib/PoolCluster.d.ts` declares the full callback surface, including the `'online'` and `'offline'` events\n\n**Gaps:**\n\n- `remove()` is absent\n- The `'online'` and `'offline'` event overloads are absent\n- The remaining method signatures do not cover every valid call pattern\n\n**Test files:**\n\n- `test/integration/pool-cluster/test-promise-wrapper.test.mts`\n\n---\n\n## Category 10: Miscellaneous\n\n**Gaps:**\n\n- `connection.close()`: exists at runtime as an alias, while the `Connection` type declares `end()` and `destroy()` only\n- `pool.config.connectionConfig`: the nested config structure has no type\n- One-off gaps specific to a single test\n\n**Test files:**\n\n- `test/global/integration/connection/test-backpressure-load-data-infile.test.mts`\n- `test/integration/test-pool-connect-error.test.mts`\n- `test/integration/config/test-connect-timeout.test.mts`\n- `test/integration/test-server-close.test.mts`\n- `test/integration/test-pool-memory-leak.test.mts`\n\n---\n\n## Confirmed Bugs in Current Types\n\nThese are wrong types, not missing ones.\n\n### Bug 1: `PoolConnection.promise()` returns the wrong type\n\n**File:** `typings/mysql/lib/PoolConnection.d.ts`\n\n**Current:** `promise(promiseImpl?: PromiseConstructor): PromisePool`\n\n**Should be:** `promise(promiseImpl?: PromiseConstructor): PromisePoolConnection`\n\nA pool connection wrapped in a promise is a promise pool connection, not a promise pool.\n\n### Bug 2: `QueryOptions.nestTables` typed as `any`\n\n**File:** `typings/mysql/lib/protocol/sequences/Query.d.ts`\n\n**Current:** `nestTables?: any`\n\n**Should be:** `nestTables?: boolean | string`\n\nThe same property in `ConnectionOptions` already carries the correct type.\n\n### Bug 3: Pool options duplicated in ConnectionOptions\n\n**File:** `typings/mysql/lib/Connection.d.ts`\n\n**Problem:** `connectionLimit`, `maxIdle`, `idleTimeout`, `queueLimit`, and `waitForConnections` are declared in both `ConnectionOptions` and `PoolOptions`. Since `PoolOptions` extends `ConnectionOptions`, they belong in `PoolOptions` alone.\n\n**Impact:** Every function taking a `ConnectionOptions`, `createConnection()` among them, accepts pool-specific options without a TypeScript error.\n","isInternal":false,"tokens":2052,"sizeBytes":8871},{"name":"SKILL.md","path":".claude/skills/types/SKILL.md","rawUrl":"https://raw.githubusercontent.com/sidorares/node-mysql2/HEAD/.claude/skills/types/SKILL.md","title":"types","category":"anthropic-skill","format":"markdown","content":"---\nname: types\ndescription: 'Fix a TypeScript error in a `.mts` test file, remove an `any` or a `!`, decide between `@ts-expect-error: internal access` and `@ts-expect-error: TODO: implement typings`, or change a `.d.ts` file under `/typings`. Carries the public type inventory, the decision tree, the type architecture, and every known typing gap.'\nargument-hint: Optionally name the test file or the type to fix, or leave empty\nuser-invocable: true\n---\n\n# MySQL2 — Typings Skill\n\nReference for fixing TypeScript types in `.mts` test files, and for guiding the conversion of `/lib` to TypeScript.\n\nFor how the types are organized, connected, and composed, see [`rules/architecture.md`](rules/architecture.md).\nFor the categorized list of known type gaps, see [`rules/gaps.md`](rules/gaps.md).\n\n---\n\n## 1. Public type inventory\n\nEvery type below is importable from the library. Check this inventory before reaching for `@ts-expect-error`.\n\n### From `index.js` (callback API)\n\n| Category    | Types                                                                                                                                               | Source (relative to `typings/mysql/`)                   |\n| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| Packets     | `RowDataPacket`, `ResultSetHeader`, `FieldPacket`, `OkPacket` _(deprecated)_, `Field` _(deprecated, now `TypeCastField`)_, `ProcedureCallPacket<T>` | `lib/protocol/packets/*.d.ts`                           |\n| Connection  | `Connection`, `ConnectionOptions` (40+ props), `ConnectionState`, `SslOptions`                                                                      | `lib/Connection.d.ts`                                   |\n| Pool        | `Pool`, `PoolOptions` (extends ConnectionOptions), `PoolConnection`                                                                                 | `lib/Pool.d.ts`, `lib/PoolConnection.d.ts`              |\n| Cluster     | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                | `lib/PoolCluster.d.ts`                                  |\n| Query       | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`                                                              | `lib/protocol/sequences/Query.d.ts`                     |\n| Prepare     | `Prepare`, `PrepareStatementInfo`                                                                                                                   | `lib/protocol/sequences/Prepare.d.ts`                   |\n| Parsers     | `TypeCast`, `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType`                                                                     | `lib/parsers/typeCast.d.ts`                             |\n| Constants   | `Types`, `Charsets`, `CharsetToEncoding`                                                                                                            | `lib/constants/*.d.ts`                                  |\n| Auth        | `AuthPlugin`, `authPlugins`                                                                                                                         | `lib/Auth.d.ts`                                         |\n| Server      | `Server`, `OkPacketParams`, `ErrorPacketParams`                                                                                                     | `lib/Server.d.ts`, `lib/protocol/packets/params/*.d.ts` |\n| Tracing     | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`                                                        | `lib/Tracing.d.ts`                                      |\n| Utilities   | `escape()`, `escapeId()`, `format()`, `raw()`, `setMaxParserCache()`, `clearParserCache()`                                                          | `index.d.ts`                                            |\n| SQL escaper | `Raw`, `SqlValue`, `Timezone`                                                                                                                       | re-export from `sql-escaper`                            |\n| Factory     | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`                                                                       | `index.d.ts`                                            |\n| Config      | `ConnectionConfig` (interface with static methods: `mergeFlags`, `getDefaultFlags`, `getCharsetNumber`, `getSSLProfile`, `parseUrl`)                | `index.d.ts`                                            |\n\n### From `promise.js`\n\nRe-exports everything above, with these differences:\n\n| Type                                           | Difference vs callback                                                                 |\n| ---------------------------------------------- | -------------------------------------------------------------------------------------- |\n| `Connection`                                   | `.query()`/`.execute()` return `Promise<[T, FieldPacket[]]>` instead of `Query`        |\n| `Pool` (interface extends Connection)          | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PoolConnection` (extends Connection)          | has `release()` and `Symbol.asyncDispose`                                              |\n| `PoolCluster` (interface extends EventEmitter) | `.getConnection()` returns `Promise<PoolConnection>`, `.end()` returns `Promise<void>` |\n| `PoolNamespace` (interface)                    | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PreparedStatementInfo`                        | `.execute()` returns `Promise<[T, FieldPacket[]]>`, `.close()` returns `Promise<void>` |\n| `createConnection()`                           | returns `Promise<Connection>`, against a synchronous `Connection` in the callback API  |\n| `createPool()`                                 | returns `Pool` synchronously, same as the callback API                                 |\n| `createPoolCluster()`                          | returns `PoolCluster` synchronously, same as the callback API                          |\n\n### Connection methods (callback-based)\n\n- `.query(sql)`, `.query(sql, values)`, `.query(options)`, `.query(options, values)` return `Query`.\n- `.execute(sql)`, `.execute(sql, values)`, `.execute(options)`, `.execute(options, values)` return `Query`.\n- `.prepare(sql, callback?)` returns `Prepare`.\n- `.unprepare(sql)` returns `PrepareStatementInfo`.\n- `.promise(promiseImpl?)` returns the promise-wrapped connection.\n- `.connect(cb?)`, `.end(cb?)`, `.destroy()`, `.pause()`, `.resume()`, `.ping(cb?)`.\n- `.beginTransaction(cb)`, `.commit(cb?)`, `.rollback(cb)`, `.changeUser(options, cb?)`.\n- `.escape(value)`, `.escapeId(value)`, `.format(sql, values?)`.\n\n### Connection methods (server-side, on incoming connections)\n\n- `.serverHandshake(args)` initiates the server handshake.\n- `.writeOk(params?: OkPacketParams)` sends an OK packet.\n- `.writeError(params?: ErrorPacketParams)` sends an error packet.\n- `.writeEof(warnings?, statusFlags?)` sends an EOF packet.\n- `.writeTextResult(rows?, columns?)` sends a result set.\n- `.writePacket(packet)` sends a raw packet.\n\n### Union types\n\n- `QueryResult` = `OkPacket | ResultSetHeader | ResultSetHeader[] | RowDataPacket[] | RowDataPacket[][] | OkPacket[] | ProcedureCallPacket`\n- `ConnectionState` = `'disconnected' | 'protocol_handshake' | 'connected' | 'authenticated' | 'error'`\n\n---\n\n## 2. Decision tree for type errors\n\nClassify the error before fixing it.\n\n### 2.1. The library type exists\n\nWhen the calling function already declares the callback signature, let TypeScript infer the parameter types instead of annotating them again.\n\n```ts\n// ✅ Correct: the types flow from the .query() signature\nconnection.query('SELECT 1', (err, rows, fields) => { ... });\n\n// ❌ Wrong: annotations that duplicate what inference already provides\nconnection.query('SELECT 1', (err: QueryError | null, rows: RowDataPacket[]) => { ... });\n```\n\nThis is the fix for every `any` on a callback parameter. Remove the annotation, do not replace it:\n\n| Pattern with `any`                   | Fix                   |\n| ------------------------------------ | --------------------- |\n| `(err: any, result: any)`            | `(err, result)`       |\n| `(err: any, rows: any)`              | `(err, rows)`         |\n| `(err: any, rows: any, fields: any)` | `(err, rows, fields)` |\n| `(err: any, connection: any)`        | `(err, connection)`   |\n| `(err: any)`                         | `(err)`               |\n\nAnnotate explicitly only where inference cannot reach, such as a standalone callback or a calling function without typings:\n\n```ts\nimport type { TypeCastField } from '../../index.js';\n\nconst conn = createConnection({\n  typeCast: (field: TypeCastField) => field.string(),\n});\n```\n\nQuery and execute results in the promise API still need their generic:\n\n```ts\nimport type { RowDataPacket } from '../../promise.js';\n\nconst [rows] = await connection.query<RowDataPacket[]>('SELECT 1');\n```\n\n### 2.2. The API exists at runtime, the types do not\n\nThe call works, but `/typings` declares it incompletely or not at all. Mark it with `@ts-expect-error: TODO: implement typings`, which flags the work for a later typings fix.\n\nWorking runtime code that TypeScript rejects is a typings gap, never a code bug. Wrong arity, a missing overload, an unrecognized property: all of them mean the declaration is behind the implementation. Add the comment and leave the call exactly as it was. Never rewrite working code to satisfy a wrong type definition.\n\n```ts\n// @ts-expect-error: TODO: implement typings\nconst server = mysql.createServer();\n\n// @ts-expect-error: TODO: implement typings\nserver.listen(3307, () => {\n  /* ... */\n});\n```\n\nEvery known gap is categorized in [`rules/gaps.md`](rules/gaps.md).\n\n### 2.3. The property is internal\n\nThe test deliberately reaches for an implementation detail that sits outside the public API and will never be typed. Mark it with `@ts-expect-error: internal access`.\n\nAnything starting with `_` qualifies: `connection._protocol`, `connection._statements`, `connection._handshakePacket`, `pool._freeConnections`, `pool._allConnections`, `packet._buf`. So does `connection.stream`, the raw socket, despite the missing underscore.\n\n```ts\n// @ts-expect-error: internal access\nconst protocol = connection._protocol;\n\n// @ts-expect-error: internal access\nassert.equal(pool._freeConnections.length, 0);\n```\n\n### 2.4. The shape belongs to the test\n\nA structure the test invents, with no library type behind it. Declare a `type` alias, never an `interface`, or annotate it inline.\n\n```ts\ntype DateTimeRow = RowDataPacket & {\n  date: string | null;\n  time: string | null;\n  datetime: string | null;\n};\n\nconst [rows] = await connection.query<DateTimeRow[]>(\n  'SELECT date, time, datetime FROM test_table'\n);\n\n// A one-off shape intersects RowDataPacket inline\nconst [rows] = await connection.query<(RowDataPacket & { count: number })[]>(\n  'SELECT COUNT(*) as count FROM test_table'\n);\n```\n\n---\n\n## 3. Import conventions\n\n1. **`import type`** for type-only imports:\n\n   ```ts\n   import type { RowDataPacket, FieldPacket } from '../../promise.js';\n   import type { TypeCastField, ConnectionOptions } from '../../index.js';\n   ```\n\n2. **`.mjs` extension** when importing another `.mts` file, since TypeScript `NodeNext` resolves `.mjs` to `.mts`:\n\n   ```ts\n   import { createConnection } from '../../common.test.mjs';\n   ```\n\n3. **`.js` extension** for library imports:\n\n   ```ts\n   import mysql from '../../../index.js';\n   import type { RowDataPacket } from '../../../promise.js';\n   ```\n\n---\n\n## 4. Process\n\n1. **Read** the target `.mts` file.\n2. **Run** `npx tsc --noEmit --pretty <file>` to collect the compiler errors.\n3. **Audit** every `any` (`: any`, `as any`, `<any>`, callback parameters) and every non-null assertion `!` by hand. `noImplicitAny: false` means `tsc` never reports `any`, and `!` always compiles, so a clean build proves nothing about either.\n4. **Classify** each error, each `any`, and each `!` against the decision tree above.\n5. **Fix** them one at a time:\n   - Drop the annotation from callback parameters and let inference work.\n   - Replace `any` in a variable declaration with the real type.\n   - Replace `!` with optional chaining: `fields![0].name` becomes `fields?.[0].name`.\n   - Add the missing `import type` statements.\n   - Add the generic to `.query<T>()` and `.execute<T>()`.\n   - Add `// @ts-expect-error: TODO: implement typings` where the library type is missing.\n   - Add `// @ts-expect-error: internal access` where the property is internal.\n   - Declare a `type` alias for a test-local structure.\n6. **Re-run** `npx tsc --noEmit --pretty <file>`.\n7. **Re-audit** the file for `any` and `!`.\n8. **Done** when all three hold: no compiler errors, no `any`, no `!`.\n\n---\n\n## 5. Pitfalls\n\n- **`any` is forbidden.** Every `: any`, `as any`, and `<any>` has to go, and step 3 above is the only thing that finds them.\n- **`!` is forbidden.** `fields![0].name` is a hack that silences the compiler, `fields?.[0].name` is safe access.\n- **The two `@ts-expect-error` comments are not interchangeable.** `TODO: implement typings` tracks work the library owes, `internal access` marks something intentionally private. Collapsing them loses the distinction that makes the first list actionable.\n- **A change in `/typings` lands on both APIs.** Verify the callback entry (`typings/mysql/index.d.ts`) and the promise entry (`promise.d.ts`) together, and remember that touching `QueryableBase` or `ExecutableBase` reaches `Connection`, `Pool`, and `PoolNamespace` at once.\n","frontmatter":{"name":"types","description":"Fix a TypeScript error in a `.mts` test file, remove an `any` or a `!`, decide between `@ts-expect-error: internal access` and `@ts-expect-error: TODO: implement typings`, or change a `.d.ts` file under `/typings`. Carries the public type inventory, the decision tree, the type architecture, and every known typing gap.","argument-hint":"Optionally name the test file or the type to fix, or leave empty","user-invocable":true},"isInternal":false,"tokens":2963,"sizeBytes":13963},{"name":"SKILL.md","path":".github/skills/code-review/SKILL.md","rawUrl":"https://raw.githubusercontent.com/sidorares/node-mysql2/HEAD/.github/skills/code-review/SKILL.md","title":"code-review","category":"anthropic-skill","format":"markdown","content":"# MySQL2 — Pull Request Review Skill\n\nThe review checklist for MySQL2 pull requests. Read it before reviewing a pull request, a diff, or a branch.\n\nNever approve a PR that violates an item below without first alerting the author.\n\n## General\n\n1. **Tests:** every bug fix and every new feature ships with tests, and the tests for a fix must fail without it.\n2. **Documentation:** every new feature is documented under `website/docs/`.\n3. **Node 14 compatibility:** Node 14 is the minimum supported runtime, whatever the `engines` field declares.\n4. **Breaking changes:** flag anything that can change existing behavior, even in a patch or a minor feature. The change itself is not an error, an unintentional semver violation is. A change to an existing test is the strongest signal there is, so read it closely for regressions.\n5. **Comments:** ask for a better implementation, never for a better explanation.\n   - An obvious comment is a finding on its own.\n   - Comment length measures the code underneath it. The more explanation it needs, the worse it usually is.\n   - A comment that explains the implementation is replaced by clear names, decoupled functions with a defined scope, and proper abstractions.\n\n## Tests\n\n6. **Connection scope:** `end()`, `close()`, `destroy()`, and `release()` belong in a scope outside the assertions. The wrong shapes are not obvious, so check them against the section below.\n7. **`process.exit`:** a conditional skip uses Poku's `skip`.\n8. **`new Promise` with `setTimeout`:** waiting uses Poku's `sleep`.\n9. **`node:assert` and `node:test`:** assertions and test structure come from `poku`, and `strict` replaces `assert`.\n10. **`as unknown as` and `any`:** never in test files.\n11. **`@ts-expect-error`:** only `// @ts-expect-error: internal access` or `// @ts-expect-error: TODO: implement typings`, and only when the type error is unrelated to the contribution. When it is related, the fix belongs in `/typings`.\n12. **Timer-dependent tests:** waiting on an internal timer is flaky in CI, so the test asserts the state synchronously or calls the internal method directly.\n13. **Promise-based API:** new tests prefer `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need both modes. A recommendation, not a rule.\n14. **`async`/`await`:** `describe`, `it`, and `test` are awaited only when the callback is asynchronous.\n\n## Types\n\n15. **Typings structure:** types follow the existing structure in `/typings` and never land in an arbitrary location. See the [`/types` skill](../../../.claude/skills/types/SKILL.md) for the architecture and the known gaps.\n\n## Connection scope\n\nThe most frequent contributor mistake, and the most expensive: a failing assertion skips the teardown and the test process hangs until CI times out. Both wrong shapes below read as correct at a glance, so compare the diff against them directly.\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Each connection is isolated by a nested or dedicated `describe`.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n- `await conn.promise().end()` replaces wrapping a callback in `new Promise`.\n\n## Verifying the branch\n\n```sh\nnpm run lint         # lint and formatting\nnpm run typecheck    # type-check the project\nnpm test             # full suite, or FILTER=path/to/test.mts npx poku for a single file\nnpm run test:build   # build check\n```\n\nIntegration and global tests need a running MySQL with a `test` database. The global suite runs sequentially and needs elevated privileges, and Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n","isInternal":false,"tokens":1090,"sizeBytes":4768},{"name":"CLAUDE.md","path":"CLAUDE.md","rawUrl":"https://raw.githubusercontent.com/sidorares/node-mysql2/HEAD/CLAUDE.md","title":"Claude Agent Guidelines & System Prompt","category":"claude-rule","format":"markdown","content":"# MySQL2 — Agent Instructions\n\nYou are an expert Node.js developer contributing to MySQL2, a high-performance MySQL driver focused on compatibility.\n\n## Project\n\n- Minimum compatibility: **Node 14**, whatever the `engines` field in `package.json` declares.\n- Core: `/lib`, exposed through `index.js` (callback) and `promise.js` (promise-based).\n- Types: `/typings` (`.d.ts` files), exposed through `index.d.ts` (callback) and `promise.d.ts` (promise-based).\n- Off limits: `/src` is a separate effort (the TypeScript transcription) and out of scope for any task that does not explicitly ask for it. Never touch it, and never replicate a change made to `/lib`, `index.js` or `promise.js` there.\n- Documentation: `website/docs/` (Docusaurus).\n- Tests:\n  - Unit: `test/unit`, parallel.\n  - Integration: `test/integration`, parallel, alongside the unit tests.\n  - Global: `test/global`, sequential, for tests that change MySQL Server global state and need careful cleanup.\n\n---\n\n## Skills\n\n- Use the [`/lagune` skill](.claude/skills/lagune/SKILL.md) whenever you build or change code, so it improves safety by default. When it is not available, install it from its manifest with `npx -y lagune@latest pull`.\n- Read the [code review skill](.github/skills/code-review/SKILL.md) before reviewing a pull request, a diff, or a branch.\n- Use the [`/types` skill](.claude/skills/types/SKILL.md) when working on `/typings` or fixing a type error in a test.\n\n---\n\n## Comments\n\nNever add an obvious comment nor narrate a statement: code that needs a comment to be understood is usually poorly implemented, and comment length measures how bad the code underneath it is.\n\nWhen code feels like it needs an explanation, improve the implementation instead: clear names, decoupled functions with a defined scope, and proper abstractions.\n\n---\n\n## Tests\n\n**Read `test/common.test.mts` before writing any test.** It provides the shared helpers for creating connections, pools, clusters, and servers, and for reading the test configuration.\n\nThe test runner is **Poku** ([docs](https://poku.io/docs), [repo](https://github.com/wellwelwel/poku)). Test files use `.mts` (ESM TypeScript) and support top-level `await`.\n\nAssertions, utilities, and test structure come from Poku:\n\n```ts\nimport { describe, it, assert, skip, sleep, strict } from 'poku';\n```\n\n- `skip` skips the entire test file and reports it, for cases like a Deno-only test or a specific Node version.\n- `sleep` waits for a given duration: `await sleep(100)`.\n\n| File                      | Description                                                         |\n| ------------------------- | ------------------------------------------------------------------- |\n| `test/common.test.mts`    | Shared helpers                                                      |\n| `poku.config.js`          | Poku config: parallel/sequential suites, timeouts, test directories |\n| `test/docker-compose.yml` | Local environment with MySQL, Node, Deno, Bun, and coverage         |\n\n```sh\nnpm run typecheck\nnpm run lint:fix\nFILTER=test/unit/my-test.mts npx poku    # run a single file through Poku\nnpx tsx test/unit/my-test.mts            # run a single file directly\n```\n\n### Connection scope and resource cleanup\n\nNever close the connection in the same scope as an assertion that may fail. When the assertion throws, `end()` is never reached and the process hangs indefinitely.\n\nOpen and close the connection in an outer scope:\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Use nested or dedicated `describe` blocks to isolate each connection.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n\nPrefer `await conn.promise().end()` instead of wrapping callbacks in `new Promise`:\n\n```ts\n// ❌ Avoid\nawait new Promise<void>((resolve) => pool.end(() => resolve()));\n\n// ✅ Prefer\nawait pool.promise().end();\n```\n\n### Avoid timer-dependent tests\n\nNever wait on an internal timer, such as idle connection cleanup, with `setTimeout` or `sleep`. Execution timing in CI is unpredictable and the test turns flaky. Call the internal method directly, or assert the state synchronously right after the action. When the timer behavior itself is what needs coverage, isolate it so no assertion depends on wall-clock timing.\n\n### `async`/`await`\n\nPoku treats `async`/`await` just like standard JavaScript, so `describe`, `it`, and `test` are awaited **only** when the callback is asynchronous.\n\n**Asynchronous:**\n\n```ts\nawait describe('test', async () => {\n  const connection = createConnection().promise();\n\n  await it('should do something', async () => {\n    const result = await connection.query('SELECT 1');\n\n    assert(result);\n  });\n\n  await connection.end();\n});\n```\n\n**Synchronous:**\n\n```ts\ndescribe('test', () => {\n  it('should do something', () => {\n    strict.equal(1 + 1, 2);\n  });\n});\n```\n\n### Prefer promise-based API\n\nNew tests prefer the promise-based API through `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need coverage in both modes.\n\n> A recommendation, not a strict rule.\n\n```ts\nconst connection = createConnection({/* ... */}).promise();\nconst pool = createPool({/* ... */}).promise();\n\nconst cluster = createPoolCluster({/* ... */});\ncluster.add('node1', {/* ... */});\nconst clusterConnection = await cluster.promise().getConnection();\n```\n\n### TypeScript in tests\n\nNever use `as unknown as` or `any` in test files.\n\nWhen reaching for an internal that the public typings do not expose, use exactly:\n\n```ts\n// @ts-expect-error: internal access\n```\n\nWhen a type in `typings/` is wrong or missing and the contribution has no relation to that type, use exactly:\n\n```ts\n// @ts-expect-error: TODO: implement typings\n```\n\nIf the type error **is** related to the contribution, fix the type in `typings/` instead of suppressing it.\n\n---\n\n## Cursor Cloud specific instructions\n\nMySQL2 is a **library**, not a long-running app. Development means installing the Node dependencies, starting MySQL for the integration tests, then running lint, typecheck, and tests against the driver.\n\n### Services\n\n| Service                          | Required?          | Notes                                                         |\n| -------------------------------- | ------------------ | ------------------------------------------------------------- |\n| **Node.js** (14 or later, CI 22) | Yes                | `npm ci` at repo root                                         |\n| **MySQL**                        | Yes for full tests | Integration and global tests need a `test` database           |\n| **Docker**                       | Recommended        | Runs MySQL the same way as CI                                 |\n| **Docusaurus** (`website/`)      | Optional           | `cd website && npm ci && npm start`, on http://localhost:3000 |\n\n### MySQL via Docker\n\nDocker is installed on the VM, but the daemon may need a manual start when systemd is not active:\n\n```sh\nsudo dockerd > /tmp/dockerd.log 2>&1 &\n```\n\nStart MySQL, which creates the `test` database with an empty root password:\n\n```sh\nsudo docker compose -f test/docker-compose.yml up -d mysql\nnode tools/wait-up.js\n```\n\nUse `sudo docker` when the socket permission error appears. CI pins **MySQL 8.3** (`mysql:8.3`), while `test/docker-compose.yml` uses `mysql:lts`, currently 9.x. For full CI parity, run:\n\n```sh\nsudo docker run -d --name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=test -p 3306:3306 mysql:8.3\n```\n\nWithout `CI=1`, tests use an empty root password, matching docker-compose. With `CI=1`, set `MYSQL_PASSWORD=root`.\n\n### Commands\n\nSee `package.json` and `Contributing.md` for the full set.\n\n| Task        | Command                                            |\n| ----------- | -------------------------------------------------- |\n| Lint        | `npm run lint`                                     |\n| Typecheck   | `npm run typecheck`                                |\n| Tests       | `npm test` (or `FILTER=path/to/test.mts npx poku`) |\n| Build check | `npm run test:build`                               |\n| Website     | `cd website && npm ci && npm test`                 |\n\n`test/global` runs sequentially and needs elevated MySQL privileges. Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n","isInternal":false,"tokens":2116,"sizeBytes":9420}],"systemPromptSnippet":"<yakaai_skills repo=\"sidorares/node-mysql2\">\n<!-- File: .claude/skills/types/rules/architecture.md (Tokens: ~4813 | Category: anthropic-skill) -->\n# MySQL2 — Type Architecture\n\nInternal structure of the `.d.ts` files in `/typings`: how the types are organized, connected, and composed.\n\nA ⚠️ marks a spot where the declarations do not match the runtime. This file records the symptom only. [`gaps.md`](gaps.md) owns what each hole is, which tests hit it, and how to close it.\n\n---\n\n## 1. Mixin Pattern\n\n`Connection` and `Pool` get `query()` and `execute()` from mixin functions rather than direct declarations, which is what makes the whole query and execute API work.\n\n`QueryableBase<T>(Base?)` and `ExecutableBase<T>(Base?)` are higher-order functions: each takes a base class constructor and returns an extended class carrying the method overloads.\n\n### Callback versions (return `Query`)\n\nEach method has 4 overloads: `(sql, cb?)`, `(sql, values, cb?)`, `(options, cb?)`, `(options, values, cb?)`.\n\n- `typings/mysql/lib/protocol/sequences/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/ExecutableBase.d.ts`\n\n### Promise versions (return `Promise<[T, FieldPacket[]]>`)\n\nEach method has 2 overloads: `(sql, values?)`, `(options, values?)`.\n\n- `typings/mysql/lib/protocol/sequences/promise/QueryableBase.d.ts`\n- `typings/mysql/lib/protocol/sequences/promise/ExecutableBase.d.ts`\n\n### Who uses the mixins\n\n| Class/Interface            | Mixin Usage                                                                  |\n| -------------------------- | ---------------------------------------------------------------------------- |\n| `Connection` (callback)    | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `Pool` (callback)          | `extends QueryableBase(ExecutableBase(EventEmitter))`                        |\n| `PoolCluster` (callback)   | `extends EventEmitter` directly, with **no mixins**                          |\n| `PoolNamespace` (callback) | interface extends `QueryableAndExecutableBase`                               |\n| `Connection` (promise)     | `extends QueryableAndExecutableBase` (anonymous class composing both mixins) |\n| `Pool` (promise)           | interface extends `Connection` (inherits mixins transitively)                |\n| `PoolNamespace` (promise)  | interface extends `QueryableAndExecutableBase`                               |\n\n### Helper type\n\nThe constraint on every mixin base class parameter:\n\n```ts\ntype QueryableConstructor<T = object> = new (...args: any[]) => T;\n```\n\n---\n\n## 2. Dual API (Callback vs Promise)\n\nMySQL2 exposes two parallel APIs: callback-based and promise-based.\n\n### Entry points\n\n| API      | Entry File            | Primary Type Source             |\n| -------- | --------------------- | ------------------------------- |\n| Callback | `index.d.ts` (root)   | `typings/mysql/index.d.ts`      |\n| Promise  | `promise.d.ts` (root) | declares promise classes inline |\n\n### How promise wraps callback\n\n1. `promise.d.ts` re-exports everything from `index.js`.\n2. It imports the promise-specific mixin functions from `typings/mysql/lib/protocol/sequences/promise/`.\n3. It composes an anonymous `QueryableAndExecutableBase` class:\n   ```ts\n   declare class QueryableAndExecutableBase extends QueryableBaseClass(\n     ExecutableBaseClass(EventEmitter)\n   ) {}\n   ```\n4. The promise classes extend or implement that base.\n\n### Key differences\n\n| Aspect                       | Callback                      | Promise                       |\n| ---------------------------- | ----------------------------- | ----------------------------- |\n| `query()`/`execute()` return | `Query`                       | `Promise<[T, FieldPacket[]]>` |\n| `createConnection()` return  | `Connection` (sync)           | `Promise<Connection>` (async) |\n| `createPool()` return        | `Pool` (sync)                 | `Pool` (sync)                 |\n| `end()`                      | `void` with optional callback | `Promise<void>`               |\n| Resource disposal            | `Symbol.dispose` (sync)       | `Symbol.asyncDispose` (async) |\n| `.promise()` method          | Available on callback classes | Not applicable                |\n\n### Bridging\n\nCallback classes carry a `.promise()` method returning the promise-wrapped version:\n\n- `Connection.promise()` returns `PromiseConnection`.\n- `Pool.promise()` returns `PromisePool`.\n- `PoolConnection.promise()` returns `PromisePool`. ⚠️\n\n---\n\n## 3. Class Hierarchy\n\n### Callback\n\n```\nEventEmitter\n├── Connection extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .authorized: boolean\n│   ├── .state: ConnectionState (readonly)\n│   ├── .sequenceId: number\n│   ├── static createQuery(): Query\n│   ├── beginTransaction(), connect(), commit(), rollback()\n│   ├── changeUser(), end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   ├── prepare(), unprepare(), ping()\n│   ├── serverHandshake(), writeOk(), writeError(), writeEof()\n│   ├── writeTextResult(), writePacket()\n│   ├── promise(): PromiseConnection\n│   └── Symbol.dispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       ├── promise(): PromisePool  ⚠️\n│       └── Symbol.dispose\n│\n├── Pool extends QueryableBase(ExecutableBase(EventEmitter))\n│   ├── .config: PoolOptions\n│   ├── getConnection(cb), releaseConnection(conn)\n│   ├── end(cb?), unprepare(sql)\n│   ├── promise(): PromisePool\n│   ├── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│   └── Symbol.dispose\n│   ⚠️ escape(), escapeId(), format() exist at runtime and are undeclared\n│\n├── PoolCluster extends EventEmitter  (NO mixins)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── remove(pattern), end(cb?)\n│   ├── getConnection(cb), getConnection(group, cb), getConnection(group, selector, cb)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'online' | 'offline' | 'remove' | 'warn'\n│   └── Symbol.dispose\n│\n├── Server extends EventEmitter\n│   ├── .connections: Connection[]\n│   ├── listen(port): Server\n│   ├── close(callback): void\n│   ⚠️ listen(port, cb) and close() without a callback are undeclared\n│\n└── Sequence extends EventEmitter\n    ├── Query extends Sequence\n    │   ├── .sql: string\n    │   ├── start(), determinePacket(), stream()\n    │   └── on: 'error' | 'fields' | 'result' | 'end'\n    │\n    └── Prepare extends Sequence\n        ├── .sql: string\n        ├── start(), determinePacket(), stream()\n        └── on: 'error' | 'fields' | 'result' | 'end'\n\nPoolNamespace (interface extends QueryableAndExecutableBase)\n├── getConnection(cb)\n```\n\n### Promise\n\n```\nEventEmitter\n├── Connection extends QueryableAndExecutableBase\n│   ├── .config: ConnectionOptions\n│   ├── .threadId: number\n│   ├── .state: ConnectionState (readonly)\n│   ├── connect(), ping(), beginTransaction(), commit(), rollback()\n│   ├── changeUser(), prepare(), unprepare()\n│   ├── end(), destroy(), pause(), resume()\n│   ├── escape(), escapeId(), format()\n│   └── Symbol.asyncDispose\n│\n│   └── PoolConnection extends Connection\n│       ├── .connection: Connection\n│       ├── release(): void\n│       └── Symbol.asyncDispose\n│\n├── Pool (interface extends Connection)\n│   ├── .pool: CorePool (callback Pool reference)\n│   ├── getConnection(): Promise<PoolConnection>\n│   ├── releaseConnection(conn)\n│   ├── end(): Promise<void>\n│   └── on: 'connection' | 'acquire' | 'release' | 'enqueue'\n│\n├── PoolCluster (interface extends EventEmitter)\n│   ├── .config: PoolClusterOptions\n│   ├── add(config), add(group, uri), add(group, config)\n│   ├── end(): Promise<void>\n│   ├── getConnection(), getConnection(group), getConnection(group, selector)\n│   ├── of(pattern, selector?): PoolNamespace\n│   ├── on: 'remove' | 'warn'\n│   ├── Symbol.asyncDispose\n│   ⚠️ on 'online' and 'offline' are declared in the callback version only\n│   ⚠️ remove() is undeclared\n│\n└── PoolNamespace (interface extends QueryableAndExecutableBase)\n    └── getConnection(): Promise<PoolConnection>\n```\n\n---\n\n## 4. File Map\n\nEvery type definition file, its purpose, and its key exports.\n\n### Root entry points\n\n| File            | Purpose             | Key Exports                                                                                                                                                  |\n| --------------- | ------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `/index.d.ts`   | Root callback entry | Re-exports from `typings/mysql/index.js`                                                                                                                     |\n| `/promise.d.ts` | Root promise entry  | `Connection`, `Pool`, `PoolConnection`, `PoolCluster`, `PoolNamespace`, `PreparedStatementInfo`, `createConnection()`, `createPool()`, `createPoolCluster()` |\n\n### Main API (`typings/mysql/`)\n\n| File         | Purpose          | Key Exports                                                                                                                                                          |\n| ------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| `index.d.ts` | Callback API hub | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`, `escape`, `escapeId`, `format`, `raw`, `ConnectionConfig`, re-exports all sub-modules |\n\n### Core classes (`typings/mysql/lib/`)\n\n| File                  | Purpose                    | Key Exports                                                                                                                                                                                      |\n| --------------------- | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| `Connection.d.ts`     | Connection class + options | `Connection`, `ConnectionOptions` (40+ props), `SslOptions`, `ConnectionState`                                                                                                                   |\n| `Pool.d.ts`           | Connection pool            | `Pool`, `PoolOptions`                                                                                                                                                                            |\n| `PoolCluster.d.ts`    | Pool cluster               | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                                                             |\n| `PoolConnection.d.ts` | Pool connection            | `PoolConnection`                                                                                                                                                                                 |\n| `Server.d.ts`         | MySQL server (testing)     | `Server`                                                                                                                                                                                         |\n| `Auth.d.ts`           | Auth plugins               | `AuthPlugin`, `authPlugins` (4 built-in plugins)                                                                                                                                                 |\n| `Tracing.d.ts`        | diagnostics_channel        | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`, `dc`, `hasTracingChannel`, `shouldTrace()`, `traceCallback()`, `tracePromise()`, channel variables |\n\n### Protocol sequences (`typings/mysql/lib/protocol/sequences/`)\n\n| File                  | Purpose                | Key Exports                                                                                                    |\n| --------------------- | ---------------------- | -------------------------------------------------------------------------------------------------------------- |\n| `Sequence.d.ts`       | Base class             | `Sequence` (extends EventEmitter, minimal)                                                                     |\n| `Query.d.ts`          | Query sequence         | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`, `QueryableConstructor` |\n| `Prepare.d.ts`        | Prepare sequence       | `Prepare`, `PrepareStatementInfo`                                                                              |\n| `ExecutableBase.d.ts` | Callback execute mixin | `ExecutableBase()`                                                                                             |\n| `QueryableBase.d.ts`  | Callback query mixin   | `QueryableBase()`                                                                                              |\n\n### Promise sequences (`typings/mysql/lib/protocol/sequences/promise/`)\n\n| File                  | Purpose               | Key Exports        |\n| --------------------- | --------------------- | ------------------ |\n| `ExecutableBase.d.ts` | Promise execute mixin | `ExecutableBase()` |\n| `QueryableBase.d.ts`  | Promise query mixin   | `QueryableBase()`  |\n\n### Packets (`typings/mysql/lib/protocol/packets/`)\n\n| File                            | Purpose                             | Key Exports                                             |\n| ------------------------------- | ----------------------------------- | ------------------------------------------------------- |\n| `index.d.ts`                    | Packet hub                          | `QueryResult` (union type), re-exports all packet types |\n| `OkPacket.d.ts`                 | OK result _(deprecated)_            | `OkPacket`                                              |\n| `ResultSetHeader.d.ts`          | Result metadata                     | `ResultSetHeader`                                       |\n| `RowDataPacket.d.ts`            | Data row                            | `RowDataPacket`                                         |\n| `FieldPacket.d.ts`              | Column metadata                     | `FieldPacket`                                           |\n| `Field.d.ts`                    | TypeCast field _(deprecated alias)_ | `Field`, an alias of `TypeCastField`                    |\n| `ProcedurePacket.d.ts`          | Stored procedure result             | `ProcedureCallPacket<T>`                                |\n| `params/OkPacketParams.d.ts`    | Server OK params                    | `OkPacketParams`                                        |\n| `params/ErrorPacketParams.d.ts` | Server error params                 | `ErrorPacketParams`                                     |\n\n### Constants (`typings/mysql/lib/constants/`)\n\n| File                     | Purpose                 | Key Exports                                                         |\n| ------------------------ | ----------------------- | ------------------------------------------------------------------- |\n| `index.d.ts`             | Constants hub           | Re-exports Types, Charsets, CharsetToEncoding                       |\n| `Types.d.ts`             | MySQL column types      | `Types` (hex codes 0x00-0xff + named: DECIMAL, TINY, ..., GEOMETRY) |\n| `Charsets.d.ts`          | Charset/collation IDs   | `Charsets` (270+ named entries)                                     |\n| `CharsetToEncoding.d.ts` | Charset to encoding map | `CharsetToEncoding` (string[])                                      |\n\n### Parsers (`typings/mysql/lib/parsers/`)\n\n| File               | Purpose         | Key Exports                                                                       |\n| ------------------ | --------------- | --------------------------------------------------------------------------------- |\n| `index.d.ts`       | Parsers hub     | Re-exports as `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType` |\n| `typeCast.d.ts`    | Type casting    | `TypeCast`, `Field` (= TypeCastField), `Geometry`, `Next`, `Type`                 |\n| `ParserCache.d.ts` | Cache utilities | `setMaxParserCache()`, `clearParserCache()`                                       |\n\n---\n\n## 5. ConnectionOptions property map\n\nEvery property declared in `typings/mysql/lib/Connection.d.ts`.\n\n### With JSDoc documentation\n\n| Property                | Type                                                    | Default             | Description                          |\n| ----------------------- | ------------------------------------------------------- | ------------------- | ------------------------------------ |\n| `decimalNumbers`        | `boolean`                                               | `false`             | Return DECIMAL/NEWDECIMAL as numbers |\n| `user`                  | `string`                                                | none                | MySQL user                           |\n| `password`              | `string`                                                | none                | MySQL password                       |\n| `password1`             | `string`                                                | none                | Alias for password (MFA)             |\n| `password2`             | `string`                                                | none                | 2nd factor auth password             |\n| `password3`             | `string`                                                | none                | 3rd factor auth password             |\n| `database`              | `string`                                                | none                | Database name                        |\n| `charset`               | `string`                                                | `'UTF8_GENERAL_CI'` | Connection charset/collation         |\n| `host`                  | `string`                                                | `'localhost'`       | Database hostname                    |\n| `port`                  | `number`                                                | `3306`              | Port number                          |\n| `localAddress`          | `string`                                                | none                | Source IP for TCP                    |\n| `socketPath`            | `string`                                                | none                | Unix socket path                     |\n| `timezone`              | `Timezone`                                              | `'local'`           | Timezone for dates                   |\n| `connectTimeout`        | `number`                                                | `10000`             | Connection timeout (ms)              |\n| `stringifyObjects`      | `boolean`                                               | `false`             | Stringify objects in queries         |\n| `insecureAuth`          | `boolean`                                               | `false`             | Allow old auth method                |\n| `infileStreamFactory`   | `(path: string) => Readable`                            | none                | Custom stream for LOAD DATA INFILE   |\n| `typeCast`              | `TypeCast`                                              | `true`              | Column value conversion              |\n| `queryFormat`           | `(query: string, values: any) => void`                  | none                | Custom query format                  |\n| `supportBigNumbers`     | `boolean`                                               | `false`             | Handle BIGINT/DECIMAL                |\n| `bigNumberStrings`      | `boolean`                                               | `false`             | Return big numbers as strings        |\n| `dateStrings`           | `boolean \\| Array<'TIMESTAMP' \\| 'DATETIME' \\| 'DATE'>` | `false`             | Return dates as strings              |\n| `debug`                 | `any`                                                   | `false`             | Print packets to stdout              |\n| `trace`                 | `boolean`                                               | `true`              | Stack traces on errors               |\n| `multipleStatements`    | `boolean`                                               | `false`             | Allow multiple statements            |\n| `flags`                 | `Array<string>`                                         | none                | Connection flags                     |\n| `ssl`                   | `string \\| SslOptions`                                  | none                | SSL configuration                    |\n| `rowsAsArray`           | `boolean`                                               | none                | Return rows as arrays                |\n| `enableKeepAlive`       | `boolean`                                               | `true`              | Socket keep-alive                    |\n| `keepAliveInitialDelay` | `number`                                                | `0`                 | Keep-alive initial delay             |\n\n### Without JSDoc documentation\n\n| Property                | Type                                       | Notes                          |\n| ----------------------- | ------------------------------------------ | ------------------------------ |\n| `charsetNumber`         | `number`                                   | Numeric charset ID             |\n| `compress`              | `boolean`                                  | Protocol compression           |\n| `authSwitchHandler`     | `(data: any, callback: () => void) => any` | Auth switch handler            |\n| `connectAttributes`     | `{ [param: string]: any }`                 | Connection attributes          |\n| `isServer`              | `boolean`                                  | Server mode flag               |\n| `maxPreparedStatements` | `number`                                   | LRU cache limit                |\n| `namedPlaceholders`     | `boolean`                                  | Named placeholder support      |\n| `nestTables`            | `boolean \\| string`                        | Nest tables in results         |\n| `passwordSha1`          | `string`                                   | Pre-hashed password            |\n| `pool`                  | `any`                                      | Pool reference                 |\n| `stream`                | `any`                                      | Custom stream                  |\n| `uri`                   | `string`                                   | Connection URI                 |\n| `connectionLimit`       | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `maxIdle`               | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `idleTimeout`           | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `Promise`               | `any`                                      | Promise constructor            |\n| `queueLimit`            | `number`                                   | ⚠️ Duplicated from PoolOptions |\n| `waitForConnections`    | `boolean`                                  | ⚠️ Duplicated from PoolOptions |\n| `disableEval`           | `boolean`                                  | Disable eval in parsers        |\n| `authPlugins`           | `{ [key: string]: AuthPlugin }`            | Custom auth plugins            |\n| `jsonStrings`           | `boolean`                                  | Force JSON as string           |\n| `gracefulEnd`           | `boolean`                                  | Graceful pool end              |\n\n### PoolOptions (extends ConnectionOptions)\n\nAdds these properties with JSDoc, in `typings/mysql/lib/Pool.d.ts`:\n\n| Property             | Type      | Default                   |\n| -------------------- | --------- | ------------------------- |\n| `waitForConnections` | `boolean` | `true`                    |\n| `connectionLimit`    | `number`  | `10`                      |\n| `maxIdle`            | `number`  | same as `connectionLimit` |\n| `idleTimeout`        | `number`  | `60000`                   |\n| `queueLimit`         | `number`  | `0`                       |\n\n---\n\n## 6. QueryOptions vs ConnectionOptions\n\n### Shared options (override per-query)\n\nThese exist in both `ConnectionOptions` and `QueryOptions`, so a query can override the connection:\n\n| Option                | ConnectionOptions        | QueryOptions  |\n| --------------------- | ------------------------ | ------------- |\n| `typeCast`            | ✅                       | ✅            |\n| `nestTables`          | ✅ (`boolean \\| string`) | ✅ (`any` ⚠️) |\n| `rowsAsArray`         | ✅                       | ✅            |\n| `supportBigNumbers`   | ✅                       | ✅            |\n| `bigNumberStrings`    | ✅                       | ✅            |\n| `dateStrings`         | ✅                       | ✅            |\n| `timezone`            | ✅                       | ✅            |\n| `infileStreamFactory` | ✅                       | ✅            |\n| `namedPlaceholders`   | ✅ (undocumented)        | ✅            |\n\n### QueryOptions-only\n\n| Option    | Type          | Description              |\n| --------- | ------------- | ------------------------ |\n| `sql`     | `string`      | The SQL query (required) |\n| `values`  | `QueryValues` | Query parameter values   |\n| `timeout` | `number`      | Operation timeout (ms)   |\n\n### Not in QueryOptions but available at runtime\n\n| Option             | Status                                                       |\n| ------------------ | ------------------------------------------------------------ |\n| `insertIdAsNumber` | Missing from both QueryOptions and ConnectionOptions typings |\n| `decimalNumbers`   | Only in ConnectionOptions, not in QueryOptions               |\n\n<!-- File: .claude/skills/types/rules/gaps.md (Tokens: ~2052 | Category: anthropic-skill) -->\n# MySQL2 — Known Type Gaps\n\nEvery occurrence of `@ts-expect-error: TODO: implement typings` across the test files, grouped by root cause. Each one is a runtime API that works correctly and is not yet reflected in the `.d.ts` files in `/typings`.\n\nFor the current count, run `grep -r \"TODO: implement typings\" test/ | wc -l`.\n\n[`architecture.md`](architecture.md) marks where each of these sits in the type structure.\n\n---\n\n## Category 1: Pool Methods Missing\n\n**Problem:** `Pool` inherits `query()` and `execute()` from the mixins, but the escaping and formatting helpers it exposes at runtime are absent from its type.\n\n**Affected typing file:** `typings/mysql/lib/Pool.d.ts`\n\n**Runtime source:** `lib/base/pool.js`, which delegates to `connection.escape()`, `connection.escapeId()`, and `connection.format()`\n\n**Gaps:**\n\n- `pool.escape()`\n- `pool.escapeId()`\n- `pool.format()`\n- `mysql.createPoolPromise()`, a factory function with no type at all\n\n**Test files:**\n\n- `test/integration/test-pool.test.mts`\n\n**Fix:** Add `escape()`, `escapeId()`, and `format()` to the `Pool` class in `Pool.d.ts`, matching the signatures on `Connection`.\n\n---\n\n## Category 2: PrepareStatementInfo Incomplete\n\n**Problem:** `PrepareStatementInfo` declares only `close()` and `execute()`, while the runtime object carries more.\n\n**Affected typing file:** `typings/mysql/lib/protocol/sequences/Prepare.d.ts`\n\n**Runtime source:** `lib/commands/prepare.js`\n\n**Missing properties:**\n\n- `.parameters`: the column and parameter definitions\n- `.columns`: the column definitions\n- `.id`: the numeric statement ID\n\n**Test files:**\n\n- `test/global/integration/regressions/2052.test.mts`\n- `test/integration/connection/test-prepare-simple.test.mts`\n\n**Fix:** Add the three properties to `PrepareStatementInfo`, then mirror the change on the promise version `PreparedStatementInfo` in `promise.d.ts`.\n\n---\n\n## Category 3: Server / createServer\n\n**Problem:** The `Server` class and the `createServer()` factory declare narrower signatures than the runtime accepts.\n\n**Affected typing files:**\n\n- `typings/mysql/index.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Runtime source:** `lib/server.js`\n\n**Gaps:**\n\n- `mysql.createServer()`: the runtime allows no arguments, the type requires a `handler`\n- `server.listen(port, callback)`: the type accepts `(port: number)` only\n- `server.close()`: the runtime allows no arguments, the type requires a callback\n\n**Test files:**\n\n- `test/unit/pool-cluster/test-connection-error-remove.test.mts`\n- `test/unit/pool-cluster/test-restore.test.mts`\n- `test/unit/pool-cluster/test-restore-events.test.mts`\n- `test/unit/pool-cluster/test-remove-by-name.test.mts`\n- `test/unit/pool-cluster/test-remove-by-pattern.test.mts`\n- `test/unit/pool-cluster/test-connection-retry.test.mts`\n- `test/integration/connection/test-server-listen.test.mts`\n\n**Fix:** Add the overloads to `Server.listen()` and `Server.close()`, and make the `createServer()` handler optional.\n\n---\n\n## Category 4: Connection Options in Wrong Contexts\n\n**Problem:** Some options are declared in `ConnectionOptions` but do not flow through every API path that accepts them.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `passwordSha1` in `changeUser()`: `changeUser()` accepts `ConnectionOptions`, yet inference fails on some call patterns\n- `uri` in `createPoolCluster()`: the option is declared in `ConnectionOptions` but does not reach this path\n\n**Test files:**\n\n- `test/global/integration/connection/test-change-user.test.mts`\n- `test/global/integration/connection/test-change-user-plugin-auth.test.mts`\n- `test/common.test.mts`\n\n---\n\n## Category 5: Auth Switch / Multi-Factor\n\n**Problem:** The auth plugin system works at runtime, but the declarations do not cover the server-side handshake or the auth switch protocol.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/Connection.d.ts`\n- `typings/mysql/lib/Auth.d.ts`\n- `typings/mysql/lib/Server.d.ts`\n\n**Gaps:**\n\n- The server-side connection handler, `server.on('connection', conn => ...)`: the auth flow methods on `conn` are not fully typed\n- `conn.serverHandshake()`: takes an object with auth plugin configuration, typed as `any`\n- Multi-factor auth callback chains are absent from the types\n- Auth switch response handling is incomplete\n\n**Test files:**\n\n- `test/integration/test-auth-switch.test.mts`\n- `test/integration/test-auth-switch-plugin-error.test.mts`\n- `test/integration/test-auth-switch-plugin-async-error.test.mts`\n- `test/integration/test-auth-switch-multi-factor.test.mts`\n- `test/integration/connection/test-change-user-multi-factor.test.mts`\n\n**Fix:** Type the `serverHandshake()` parameter object, add the server-side connection event types, and type the auth plugin response chain.\n\n---\n\n## Category 6: Query Result Refinements\n\n**Problem:** A query property and a nested config path have no declarations.\n\n**Affected typing files:**\n\n- `typings/mysql/lib/protocol/sequences/Query.d.ts`\n- `typings/mysql/lib/Pool.d.ts`\n\n**Gaps:**\n\n- `Query.values`: exists at runtime, while the `Query` class declares `.sql` only\n- `pool.config.connectionConfig.namedPlaceholders`: `Pool.config` is a `PoolOptions`, which exposes no `connectionConfig` sub-object\n\n**Test files:**\n\n- `test/integration/connection/test-named-placeholders.test.mts`\n- `test/integration/connection/test-multiple-results.test.mts`\n\n---\n\n## Category 7: Stream / Error Handling\n\n**Problem:** Connection stream access and some error handling patterns have no declarations.\n\n**Affected typing file:** `typings/mysql/lib/Connection.d.ts`\n\n**Gaps:**\n\n- `connection.stream`: the raw socket property is absent from the `Connection` type\n- Error event handler types do not cover every error code pattern\n\n**Test files:**\n\n- `test/integration/connection/test-stream-errors.test.mts`\n\n---\n\n## Category 8: Promise Wrapper Access\n\n**Problem:** A promise-wrapped connection exposes `.connection` to reach the underlying callback connection, but the EventEmitter surface of that inner connection is unreachable through the types.\n\n**Affected typing file:** `promise.d.ts`\n\n**Gaps:**\n\n- `conn.connection`: reaching the underlying callback connection from a promise wrapper\n- `conn.connection.listenerCount()` and `conn.connection.emit()`: EventEmitter methods neither inherited nor exposed\n- Assorted promise wrapper methods absent from the declarations\n\n**Test files:**\n\n- `test/integration/promise-wrappers/test-promise-wrappers.test.mts`\n\n---\n\n## Category 9: PoolCluster Promise Wrapper\n\n**Problem:** The promise version of `PoolCluster` declares fewer members than the callback version.\n\n**Affected typing file:** `promise.d.ts` (PoolCluster interface)\n\n**Reference:** `typings/mysql/lib/PoolCluster.d.ts` declares the full callback surface, including the `'online'` and `'offline'` events\n\n**Gaps:**\n\n- `remove()` is absent\n- The `'online'` and `'offline'` event overloads are absent\n- The remaining method signatures do not cover every valid call pattern\n\n**Test files:**\n\n- `test/integration/pool-cluster/test-promise-wrapper.test.mts`\n\n---\n\n## Category 10: Miscellaneous\n\n**Gaps:**\n\n- `connection.close()`: exists at runtime as an alias, while the `Connection` type declares `end()` and `destroy()` only\n- `pool.config.connectionConfig`: the nested config structure has no type\n- One-off gaps specific to a single test\n\n**Test files:**\n\n- `test/global/integration/connection/test-backpressure-load-data-infile.test.mts`\n- `test/integration/test-pool-connect-error.test.mts`\n- `test/integration/config/test-connect-timeout.test.mts`\n- `test/integration/test-server-close.test.mts`\n- `test/integration/test-pool-memory-leak.test.mts`\n\n---\n\n## Confirmed Bugs in Current Types\n\nThese are wrong types, not missing ones.\n\n### Bug 1: `PoolConnection.promise()` returns the wrong type\n\n**File:** `typings/mysql/lib/PoolConnection.d.ts`\n\n**Current:** `promise(promiseImpl?: PromiseConstructor): PromisePool`\n\n**Should be:** `promise(promiseImpl?: PromiseConstructor): PromisePoolConnection`\n\nA pool connection wrapped in a promise is a promise pool connection, not a promise pool.\n\n### Bug 2: `QueryOptions.nestTables` typed as `any`\n\n**File:** `typings/mysql/lib/protocol/sequences/Query.d.ts`\n\n**Current:** `nestTables?: any`\n\n**Should be:** `nestTables?: boolean | string`\n\nThe same property in `ConnectionOptions` already carries the correct type.\n\n### Bug 3: Pool options duplicated in ConnectionOptions\n\n**File:** `typings/mysql/lib/Connection.d.ts`\n\n**Problem:** `connectionLimit`, `maxIdle`, `idleTimeout`, `queueLimit`, and `waitForConnections` are declared in both `ConnectionOptions` and `PoolOptions`. Since `PoolOptions` extends `ConnectionOptions`, they belong in `PoolOptions` alone.\n\n**Impact:** Every function taking a `ConnectionOptions`, `createConnection()` among them, accepts pool-specific options without a TypeScript error.\n\n<!-- File: .claude/skills/types/SKILL.md (Tokens: ~2963 | Category: anthropic-skill) -->\n---\nname: types\ndescription: 'Fix a TypeScript error in a `.mts` test file, remove an `any` or a `!`, decide between `@ts-expect-error: internal access` and `@ts-expect-error: TODO: implement typings`, or change a `.d.ts` file under `/typings`. Carries the public type inventory, the decision tree, the type architecture, and every known typing gap.'\nargument-hint: Optionally name the test file or the type to fix, or leave empty\nuser-invocable: true\n---\n\n# MySQL2 — Typings Skill\n\nReference for fixing TypeScript types in `.mts` test files, and for guiding the conversion of `/lib` to TypeScript.\n\nFor how the types are organized, connected, and composed, see [`rules/architecture.md`](rules/architecture.md).\nFor the categorized list of known type gaps, see [`rules/gaps.md`](rules/gaps.md).\n\n---\n\n## 1. Public type inventory\n\nEvery type below is importable from the library. Check this inventory before reaching for `@ts-expect-error`.\n\n### From `index.js` (callback API)\n\n| Category    | Types                                                                                                                                               | Source (relative to `typings/mysql/`)                   |\n| ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------- |\n| Packets     | `RowDataPacket`, `ResultSetHeader`, `FieldPacket`, `OkPacket` _(deprecated)_, `Field` _(deprecated, now `TypeCastField`)_, `ProcedureCallPacket<T>` | `lib/protocol/packets/*.d.ts`                           |\n| Connection  | `Connection`, `ConnectionOptions` (40+ props), `ConnectionState`, `SslOptions`                                                                      | `lib/Connection.d.ts`                                   |\n| Pool        | `Pool`, `PoolOptions` (extends ConnectionOptions), `PoolConnection`                                                                                 | `lib/Pool.d.ts`, `lib/PoolConnection.d.ts`              |\n| Cluster     | `PoolCluster`, `PoolClusterOptions`, `PoolNamespace`                                                                                                | `lib/PoolCluster.d.ts`                                  |\n| Query       | `Query`, `QueryOptions`, `QueryError`, `QueryValues`, `ExecuteValues`, `StreamOptions`                                                              | `lib/protocol/sequences/Query.d.ts`                     |\n| Prepare     | `Prepare`, `PrepareStatementInfo`                                                                                                                   | `lib/protocol/sequences/Prepare.d.ts`                   |\n| Parsers     | `TypeCast`, `TypeCastField`, `TypeCastGeometry`, `TypeCastNext`, `TypeCastType`                                                                     | `lib/parsers/typeCast.d.ts`                             |\n| Constants   | `Types`, `Charsets`, `CharsetToEncoding`                                                                                                            | `lib/constants/*.d.ts`                                  |\n| Auth        | `AuthPlugin`, `authPlugins`                                                                                                                         | `lib/Auth.d.ts`                                         |\n| Server      | `Server`, `OkPacketParams`, `ErrorPacketParams`                                                                                                     | `lib/Server.d.ts`, `lib/protocol/packets/params/*.d.ts` |\n| Tracing     | `QueryTraceContext`, `ExecuteTraceContext`, `ConnectTraceContext`, `PoolConnectTraceContext`                                                        | `lib/Tracing.d.ts`                                      |\n| Utilities   | `escape()`, `escapeId()`, `format()`, `raw()`, `setMaxParserCache()`, `clearParserCache()`                                                          | `index.d.ts`                                            |\n| SQL escaper | `Raw`, `SqlValue`, `Timezone`                                                                                                                       | re-export from `sql-escaper`                            |\n| Factory     | `createConnection()`, `createPool()`, `createPoolCluster()`, `createServer()`                                                                       | `index.d.ts`                                            |\n| Config      | `ConnectionConfig` (interface with static methods: `mergeFlags`, `getDefaultFlags`, `getCharsetNumber`, `getSSLProfile`, `parseUrl`)                | `index.d.ts`                                            |\n\n### From `promise.js`\n\nRe-exports everything above, with these differences:\n\n| Type                                           | Difference vs callback                                                                 |\n| ---------------------------------------------- | -------------------------------------------------------------------------------------- |\n| `Connection`                                   | `.query()`/`.execute()` return `Promise<[T, FieldPacket[]]>` instead of `Query`        |\n| `Pool` (interface extends Connection)          | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PoolConnection` (extends Connection)          | has `release()` and `Symbol.asyncDispose`                                              |\n| `PoolCluster` (interface extends EventEmitter) | `.getConnection()` returns `Promise<PoolConnection>`, `.end()` returns `Promise<void>` |\n| `PoolNamespace` (interface)                    | `.getConnection()` returns `Promise<PoolConnection>`                                   |\n| `PreparedStatementInfo`                        | `.execute()` returns `Promise<[T, FieldPacket[]]>`, `.close()` returns `Promise<void>` |\n| `createConnection()`                           | returns `Promise<Connection>`, against a synchronous `Connection` in the callback API  |\n| `createPool()`                                 | returns `Pool` synchronously, same as the callback API                                 |\n| `createPoolCluster()`                          | returns `PoolCluster` synchronously, same as the callback API                          |\n\n### Connection methods (callback-based)\n\n- `.query(sql)`, `.query(sql, values)`, `.query(options)`, `.query(options, values)` return `Query`.\n- `.execute(sql)`, `.execute(sql, values)`, `.execute(options)`, `.execute(options, values)` return `Query`.\n- `.prepare(sql, callback?)` returns `Prepare`.\n- `.unprepare(sql)` returns `PrepareStatementInfo`.\n- `.promise(promiseImpl?)` returns the promise-wrapped connection.\n- `.connect(cb?)`, `.end(cb?)`, `.destroy()`, `.pause()`, `.resume()`, `.ping(cb?)`.\n- `.beginTransaction(cb)`, `.commit(cb?)`, `.rollback(cb)`, `.changeUser(options, cb?)`.\n- `.escape(value)`, `.escapeId(value)`, `.format(sql, values?)`.\n\n### Connection methods (server-side, on incoming connections)\n\n- `.serverHandshake(args)` initiates the server handshake.\n- `.writeOk(params?: OkPacketParams)` sends an OK packet.\n- `.writeError(params?: ErrorPacketParams)` sends an error packet.\n- `.writeEof(warnings?, statusFlags?)` sends an EOF packet.\n- `.writeTextResult(rows?, columns?)` sends a result set.\n- `.writePacket(packet)` sends a raw packet.\n\n### Union types\n\n- `QueryResult` = `OkPacket | ResultSetHeader | ResultSetHeader[] | RowDataPacket[] | RowDataPacket[][] | OkPacket[] | ProcedureCallPacket`\n- `ConnectionState` = `'disconnected' | 'protocol_handshake' | 'connected' | 'authenticated' | 'error'`\n\n---\n\n## 2. Decision tree for type errors\n\nClassify the error before fixing it.\n\n### 2.1. The library type exists\n\nWhen the calling function already declares the callback signature, let TypeScript infer the parameter types instead of annotating them again.\n\n```ts\n// ✅ Correct: the types flow from the .query() signature\nconnection.query('SELECT 1', (err, rows, fields) => { ... });\n\n// ❌ Wrong: annotations that duplicate what inference already provides\nconnection.query('SELECT 1', (err: QueryError | null, rows: RowDataPacket[]) => { ... });\n```\n\nThis is the fix for every `any` on a callback parameter. Remove the annotation, do not replace it:\n\n| Pattern with `any`                   | Fix                   |\n| ------------------------------------ | --------------------- |\n| `(err: any, result: any)`            | `(err, result)`       |\n| `(err: any, rows: any)`              | `(err, rows)`         |\n| `(err: any, rows: any, fields: any)` | `(err, rows, fields)` |\n| `(err: any, connection: any)`        | `(err, connection)`   |\n| `(err: any)`                         | `(err)`               |\n\nAnnotate explicitly only where inference cannot reach, such as a standalone callback or a calling function without typings:\n\n```ts\nimport type { TypeCastField } from '../../index.js';\n\nconst conn = createConnection({\n  typeCast: (field: TypeCastField) => field.string(),\n});\n```\n\nQuery and execute results in the promise API still need their generic:\n\n```ts\nimport type { RowDataPacket } from '../../promise.js';\n\nconst [rows] = await connection.query<RowDataPacket[]>('SELECT 1');\n```\n\n### 2.2. The API exists at runtime, the types do not\n\nThe call works, but `/typings` declares it incompletely or not at all. Mark it with `@ts-expect-error: TODO: implement typings`, which flags the work for a later typings fix.\n\nWorking runtime code that TypeScript rejects is a typings gap, never a code bug. Wrong arity, a missing overload, an unrecognized property: all of them mean the declaration is behind the implementation. Add the comment and leave the call exactly as it was. Never rewrite working code to satisfy a wrong type definition.\n\n```ts\n// @ts-expect-error: TODO: implement typings\nconst server = mysql.createServer();\n\n// @ts-expect-error: TODO: implement typings\nserver.listen(3307, () => {\n  /* ... */\n});\n```\n\nEvery known gap is categorized in [`rules/gaps.md`](rules/gaps.md).\n\n### 2.3. The property is internal\n\nThe test deliberately reaches for an implementation detail that sits outside the public API and will never be typed. Mark it with `@ts-expect-error: internal access`.\n\nAnything starting with `_` qualifies: `connection._protocol`, `connection._statements`, `connection._handshakePacket`, `pool._freeConnections`, `pool._allConnections`, `packet._buf`. So does `connection.stream`, the raw socket, despite the missing underscore.\n\n```ts\n// @ts-expect-error: internal access\nconst protocol = connection._protocol;\n\n// @ts-expect-error: internal access\nassert.equal(pool._freeConnections.length, 0);\n```\n\n### 2.4. The shape belongs to the test\n\nA structure the test invents, with no library type behind it. Declare a `type` alias, never an `interface`, or annotate it inline.\n\n```ts\ntype DateTimeRow = RowDataPacket & {\n  date: string | null;\n  time: string | null;\n  datetime: string | null;\n};\n\nconst [rows] = await connection.query<DateTimeRow[]>(\n  'SELECT date, time, datetime FROM test_table'\n);\n\n// A one-off shape intersects RowDataPacket inline\nconst [rows] = await connection.query<(RowDataPacket & { count: number })[]>(\n  'SELECT COUNT(*) as count FROM test_table'\n);\n```\n\n---\n\n## 3. Import conventions\n\n1. **`import type`** for type-only imports:\n\n   ```ts\n   import type { RowDataPacket, FieldPacket } from '../../promise.js';\n   import type { TypeCastField, ConnectionOptions } from '../../index.js';\n   ```\n\n2. **`.mjs` extension** when importing another `.mts` file, since TypeScript `NodeNext` resolves `.mjs` to `.mts`:\n\n   ```ts\n   import { createConnection } from '../../common.test.mjs';\n   ```\n\n3. **`.js` extension** for library imports:\n\n   ```ts\n   import mysql from '../../../index.js';\n   import type { RowDataPacket } from '../../../promise.js';\n   ```\n\n---\n\n## 4. Process\n\n1. **Read** the target `.mts` file.\n2. **Run** `npx tsc --noEmit --pretty <file>` to collect the compiler errors.\n3. **Audit** every `any` (`: any`, `as any`, `<any>`, callback parameters) and every non-null assertion `!` by hand. `noImplicitAny: false` means `tsc` never reports `any`, and `!` always compiles, so a clean build proves nothing about either.\n4. **Classify** each error, each `any`, and each `!` against the decision tree above.\n5. **Fix** them one at a time:\n   - Drop the annotation from callback parameters and let inference work.\n   - Replace `any` in a variable declaration with the real type.\n   - Replace `!` with optional chaining: `fields![0].name` becomes `fields?.[0].name`.\n   - Add the missing `import type` statements.\n   - Add the generic to `.query<T>()` and `.execute<T>()`.\n   - Add `// @ts-expect-error: TODO: implement typings` where the library type is missing.\n   - Add `// @ts-expect-error: internal access` where the property is internal.\n   - Declare a `type` alias for a test-local structure.\n6. **Re-run** `npx tsc --noEmit --pretty <file>`.\n7. **Re-audit** the file for `any` and `!`.\n8. **Done** when all three hold: no compiler errors, no `any`, no `!`.\n\n---\n\n## 5. Pitfalls\n\n- **`any` is forbidden.** Every `: any`, `as any`, and `<any>` has to go, and step 3 above is the only thing that finds them.\n- **`!` is forbidden.** `fields![0].name` is a hack that silences the compiler, `fields?.[0].name` is safe access.\n- **The two `@ts-expect-error` comments are not interchangeable.** `TODO: implement typings` tracks work the library owes, `internal access` marks something intentionally private. Collapsing them loses the distinction that makes the first list actionable.\n- **A change in `/typings` lands on both APIs.** Verify the callback entry (`typings/mysql/index.d.ts`) and the promise entry (`promise.d.ts`) together, and remember that touching `QueryableBase` or `ExecutableBase` reaches `Connection`, `Pool`, and `PoolNamespace` at once.\n\n<!-- File: .github/skills/code-review/SKILL.md (Tokens: ~1090 | Category: anthropic-skill) -->\n# MySQL2 — Pull Request Review Skill\n\nThe review checklist for MySQL2 pull requests. Read it before reviewing a pull request, a diff, or a branch.\n\nNever approve a PR that violates an item below without first alerting the author.\n\n## General\n\n1. **Tests:** every bug fix and every new feature ships with tests, and the tests for a fix must fail without it.\n2. **Documentation:** every new feature is documented under `website/docs/`.\n3. **Node 14 compatibility:** Node 14 is the minimum supported runtime, whatever the `engines` field declares.\n4. **Breaking changes:** flag anything that can change existing behavior, even in a patch or a minor feature. The change itself is not an error, an unintentional semver violation is. A change to an existing test is the strongest signal there is, so read it closely for regressions.\n5. **Comments:** ask for a better implementation, never for a better explanation.\n   - An obvious comment is a finding on its own.\n   - Comment length measures the code underneath it. The more explanation it needs, the worse it usually is.\n   - A comment that explains the implementation is replaced by clear names, decoupled functions with a defined scope, and proper abstractions.\n\n## Tests\n\n6. **Connection scope:** `end()`, `close()`, `destroy()`, and `release()` belong in a scope outside the assertions. The wrong shapes are not obvious, so check them against the section below.\n7. **`process.exit`:** a conditional skip uses Poku's `skip`.\n8. **`new Promise` with `setTimeout`:** waiting uses Poku's `sleep`.\n9. **`node:assert` and `node:test`:** assertions and test structure come from `poku`, and `strict` replaces `assert`.\n10. **`as unknown as` and `any`:** never in test files.\n11. **`@ts-expect-error`:** only `// @ts-expect-error: internal access` or `// @ts-expect-error: TODO: implement typings`, and only when the type error is unrelated to the contribution. When it is related, the fix belongs in `/typings`.\n12. **Timer-dependent tests:** waiting on an internal timer is flaky in CI, so the test asserts the state synchronously or calls the internal method directly.\n13. **Promise-based API:** new tests prefer `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need both modes. A recommendation, not a rule.\n14. **`async`/`await`:** `describe`, `it`, and `test` are awaited only when the callback is asynchronous.\n\n## Types\n\n15. **Typings structure:** types follow the existing structure in `/typings` and never land in an arbitrary location. See the [`/types` skill](../../../.claude/skills/types/SKILL.md) for the architecture and the known gaps.\n\n## Connection scope\n\nThe most frequent contributor mistake, and the most expensive: a failing assertion skips the teardown and the test process hangs until CI times out. Both wrong shapes below read as correct at a glance, so compare the diff against them directly.\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Each connection is isolated by a nested or dedicated `describe`.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n- `await conn.promise().end()` replaces wrapping a callback in `new Promise`.\n\n## Verifying the branch\n\n```sh\nnpm run lint         # lint and formatting\nnpm run typecheck    # type-check the project\nnpm test             # full suite, or FILTER=path/to/test.mts npx poku for a single file\nnpm run test:build   # build check\n```\n\nIntegration and global tests need a running MySQL with a `test` database. The global suite runs sequentially and needs elevated privileges, and Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n\n<!-- File: CLAUDE.md (Tokens: ~2116 | Category: claude-rule) -->\n# MySQL2 — Agent Instructions\n\nYou are an expert Node.js developer contributing to MySQL2, a high-performance MySQL driver focused on compatibility.\n\n## Project\n\n- Minimum compatibility: **Node 14**, whatever the `engines` field in `package.json` declares.\n- Core: `/lib`, exposed through `index.js` (callback) and `promise.js` (promise-based).\n- Types: `/typings` (`.d.ts` files), exposed through `index.d.ts` (callback) and `promise.d.ts` (promise-based).\n- Off limits: `/src` is a separate effort (the TypeScript transcription) and out of scope for any task that does not explicitly ask for it. Never touch it, and never replicate a change made to `/lib`, `index.js` or `promise.js` there.\n- Documentation: `website/docs/` (Docusaurus).\n- Tests:\n  - Unit: `test/unit`, parallel.\n  - Integration: `test/integration`, parallel, alongside the unit tests.\n  - Global: `test/global`, sequential, for tests that change MySQL Server global state and need careful cleanup.\n\n---\n\n## Skills\n\n- Use the [`/lagune` skill](.claude/skills/lagune/SKILL.md) whenever you build or change code, so it improves safety by default. When it is not available, install it from its manifest with `npx -y lagune@latest pull`.\n- Read the [code review skill](.github/skills/code-review/SKILL.md) before reviewing a pull request, a diff, or a branch.\n- Use the [`/types` skill](.claude/skills/types/SKILL.md) when working on `/typings` or fixing a type error in a test.\n\n---\n\n## Comments\n\nNever add an obvious comment nor narrate a statement: code that needs a comment to be understood is usually poorly implemented, and comment length measures how bad the code underneath it is.\n\nWhen code feels like it needs an explanation, improve the implementation instead: clear names, decoupled functions with a defined scope, and proper abstractions.\n\n---\n\n## Tests\n\n**Read `test/common.test.mts` before writing any test.** It provides the shared helpers for creating connections, pools, clusters, and servers, and for reading the test configuration.\n\nThe test runner is **Poku** ([docs](https://poku.io/docs), [repo](https://github.com/wellwelwel/poku)). Test files use `.mts` (ESM TypeScript) and support top-level `await`.\n\nAssertions, utilities, and test structure come from Poku:\n\n```ts\nimport { describe, it, assert, skip, sleep, strict } from 'poku';\n```\n\n- `skip` skips the entire test file and reports it, for cases like a Deno-only test or a specific Node version.\n- `sleep` waits for a given duration: `await sleep(100)`.\n\n| File                      | Description                                                         |\n| ------------------------- | ------------------------------------------------------------------- |\n| `test/common.test.mts`    | Shared helpers                                                      |\n| `poku.config.js`          | Poku config: parallel/sequential suites, timeouts, test directories |\n| `test/docker-compose.yml` | Local environment with MySQL, Node, Deno, Bun, and coverage         |\n\n```sh\nnpm run typecheck\nnpm run lint:fix\nFILTER=test/unit/my-test.mts npx poku    # run a single file through Poku\nnpx tsx test/unit/my-test.mts            # run a single file directly\n```\n\n### Connection scope and resource cleanup\n\nNever close the connection in the same scope as an assertion that may fail. When the assertion throws, `end()` is never reached and the process hangs indefinitely.\n\nOpen and close the connection in an outer scope:\n\n```ts\n// ❌ Wrong: end() sits in the same scope as the assertion\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection(); // same for pool or cluster connections\n    assert(false);\n    await connection.end(); // never reached\n  });\n  // process hangs\n});\n\n// ❌ Wrong: try-finally is a workaround, not a fix\nawait describe('test', async () => {\n  await it('should do something', async () => {\n    const connection = await createConnection();\n    try {\n      assert(false);\n    } finally {\n      await connection.end();\n    }\n  });\n  // process hangs\n});\n\n// ✅ Correct: end() in an outer scope\nawait describe('test', async () => {\n  const connection = await createConnection();\n\n  it('should do something', () => {\n    assert(false); // fails in its own scope\n  });\n\n  await connection.end(); // always reached\n});\n```\n\n- Every teardown method and every connection type is affected: `close`, `end`, `destroy`, `release`, on `Connection`, `Pool`, `PoolCluster`, and the rest.\n- Use nested or dedicated `describe` blocks to isolate each connection.\n- Callbacks fail the same way, with the teardown buried in a nested callback that a failing assertion prevents from ever running.\n\nPrefer `await conn.promise().end()` instead of wrapping callbacks in `new Promise`:\n\n```ts\n// ❌ Avoid\nawait new Promise<void>((resolve) => pool.end(() => resolve()));\n\n// ✅ Prefer\nawait pool.promise().end();\n```\n\n### Avoid timer-dependent tests\n\nNever wait on an internal timer, such as idle connection cleanup, with `setTimeout` or `sleep`. Execution timing in CI is unpredictable and the test turns flaky. Call the internal method directly, or assert the state synchronously right after the action. When the timer behavior itself is what needs coverage, isolate it so no assertion depends on wall-clock timing.\n\n### `async`/`await`\n\nPoku treats `async`/`await` just like standard JavaScript, so `describe`, `it`, and `test` are awaited **only** when the callback is asynchronous.\n\n**Asynchronous:**\n\n```ts\nawait describe('test', async () => {\n  const connection = createConnection().promise();\n\n  await it('should do something', async () => {\n    const result = await connection.query('SELECT 1');\n\n    assert(result);\n  });\n\n  await connection.end();\n});\n```\n\n**Synchronous:**\n\n```ts\ndescribe('test', () => {\n  it('should do something', () => {\n    strict.equal(1 + 1, 2);\n  });\n});\n```\n\n### Prefer promise-based API\n\nNew tests prefer the promise-based API through `.promise()`. Callbacks stay for events, streams, anything the promise API does not cover, and features that genuinely need coverage in both modes.\n\n> A recommendation, not a strict rule.\n\n```ts\nconst connection = createConnection({/* ... */}).promise();\nconst pool = createPool({/* ... */}).promise();\n\nconst cluster = createPoolCluster({/* ... */});\ncluster.add('node1', {/* ... */});\nconst clusterConnection = await cluster.promise().getConnection();\n```\n\n### TypeScript in tests\n\nNever use `as unknown as` or `any` in test files.\n\nWhen reaching for an internal that the public typings do not expose, use exactly:\n\n```ts\n// @ts-expect-error: internal access\n```\n\nWhen a type in `typings/` is wrong or missing and the contribution has no relation to that type, use exactly:\n\n```ts\n// @ts-expect-error: TODO: implement typings\n```\n\nIf the type error **is** related to the contribution, fix the type in `typings/` instead of suppressing it.\n\n---\n\n## Cursor Cloud specific instructions\n\nMySQL2 is a **library**, not a long-running app. Development means installing the Node dependencies, starting MySQL for the integration tests, then running lint, typecheck, and tests against the driver.\n\n### Services\n\n| Service                          | Required?          | Notes                                                         |\n| -------------------------------- | ------------------ | ------------------------------------------------------------- |\n| **Node.js** (14 or later, CI 22) | Yes                | `npm ci` at repo root                                         |\n| **MySQL**                        | Yes for full tests | Integration and global tests need a `test` database           |\n| **Docker**                       | Recommended        | Runs MySQL the same way as CI                                 |\n| **Docusaurus** (`website/`)      | Optional           | `cd website && npm ci && npm start`, on http://localhost:3000 |\n\n### MySQL via Docker\n\nDocker is installed on the VM, but the daemon may need a manual start when systemd is not active:\n\n```sh\nsudo dockerd > /tmp/dockerd.log 2>&1 &\n```\n\nStart MySQL, which creates the `test` database with an empty root password:\n\n```sh\nsudo docker compose -f test/docker-compose.yml up -d mysql\nnode tools/wait-up.js\n```\n\nUse `sudo docker` when the socket permission error appears. CI pins **MySQL 8.3** (`mysql:8.3`), while `test/docker-compose.yml` uses `mysql:lts`, currently 9.x. For full CI parity, run:\n\n```sh\nsudo docker run -d --name mysql -e MYSQL_ALLOW_EMPTY_PASSWORD=1 -e MYSQL_DATABASE=test -p 3306:3306 mysql:8.3\n```\n\nWithout `CI=1`, tests use an empty root password, matching docker-compose. With `CI=1`, set `MYSQL_PASSWORD=root`.\n\n### Commands\n\nSee `package.json` and `Contributing.md` for the full set.\n\n| Task        | Command                                            |\n| ----------- | -------------------------------------------------- |\n| Lint        | `npm run lint`                                     |\n| Typecheck   | `npm run typecheck`                                |\n| Tests       | `npm test` (or `FILTER=path/to/test.mts npx poku`) |\n| Build check | `npm run test:build`                               |\n| Website     | `cd website && npm ci && npm test`                 |\n\n`test/global` runs sequentially and needs elevated MySQL privileges. Poku skips those files when `hasPrivileges()` fails, so a green local run does not prove they were exercised.\n</yakaai_skills>"}