# CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

## What this is

`cmd-control-client-lib` (npm package name) — a TypeScript client library that implements the **cmd-control protocol** used by camPoint's CmdControl server. It provides:

-   The full protocol definition as TypeScript types/classes (commands, responses, params, enums).
-   `CmdConnection` — a WebSocket (with JSONP fallback) transport with request/response correlation, auto-reconnect, keepalive (NOOP), and transparent server-switch.
-   `CmdControlSession` — a thin convenience wrapper over `CmdConnection` for common login/logout flows.

It is a browser-targeted library (`lib: ["dom", ...]`, `target: es5`, UMD bundle). It ships as a single bundled `dist/cmd-control-client-lib.js` plus `.d.ts`.

## Commands

```bash
yarn install              # deps (runs husky install via postinstall)
yarn build                # production bundle -> dist/ (webpack.prod.js)
yarn build:dev            # dev bundle
yarn watch                # dev bundle in watch mode
yarn lint                 # eslint src --ext .ts   (CI gate)
yarn style                # prettier --write over src & test
yarn test                 # jest (all specs)
yarn test -- test/result.spec.ts          # single test file
yarn test -- -t "Should accept ENUM OK"   # single test by name
yarn generate:docs        # typedoc markdown -> doc/
```

CI (Bitbucket Pipelines) runs: install → `yarn lint` → `yarn build`. Tests are **not** in CI; lint failures break the build. A husky pre-commit hook runs lint-staged (`eslint --cache` on staged `*.ts`).

Note: on newer Node you may need `export NODE_OPTIONS=--openssl-legacy-provider` for webpack (see README).

## Architecture

### Layering

-   `src/cmd-control-client-lib.ts` — public barrel / package entry. Re-exports the protocol, `@types`, and the runtime classes (`CmdConnection`, `CmdControlSession`, `setLogger`, `LogCollector`).
-   `src/cmd-protocol.ts` — barrel that re-exports every protocol module under `src/protocol/`. **When you add a new protocol file, you must add its `export *` line here** or its types won't be part of the public API.
-   `src/protocol/` — the protocol surface, one file per feature area (login, message, media, channel, live/_, messenger/_, b2b/\*, …).
-   `src/@types/` — cross-cutting primitive types and enums (stringified/digitized booleans, currency, channel flags, `JSONString<T>`, helper conditional types). Barrel: `src/@types/index.ts`.
-   `src/cmd-connection.ts` — the transport engine (the only substantial runtime logic; ~670 lines).
-   `src/cmd-session.ts` — convenience wrapper.
-   `src/logger.ts` — pluggable logger; default is a no-op. `setLogger()` / `LogCollector` let consumers capture logs.

### Protocol modeling convention (important — follow it for new commands)

Each command is modeled as a set of classes/types in one `src/protocol/**` file:

-   A command class `CMDP_XXX implements ICOMMAND` with `public action: ACTION = ACTION.CMDP_XXX` and a `params` type composed via intersection from `baseParamsType` plus feature-specific fields (e.g. `params: baseParamsType & channelIdType & { token: string }`).
-   A matching `CMDP_XXX_RESPONSE extends CMDP_XXX implements IRESPONSE` adding `result: RESULT`, `commands: ICOMMAND[]`, `values: …`.
-   Server-initiated pushes are modeled as `CMDC_XXX` classes.

Every action name must be registered in the `ACTION` enum in `src/protocol/command/action.ts`. `CMDP_*` = client→server request, `CMDP_S*` = "session/secure" variant, `CMDC_*` = server→client command/push. `ResultCode` (`src/protocol/command/resultcode.ts`) is the response status enum.

`values` fields that carry nested objects over the wire are typed as `JSONString<T>` — they are literally `string` at runtime (the server sends stringified JSON), so consumers must `JSON.parse` them.

### Transport engine (`CmdConnection`)

Key mechanics to understand before touching this file:

-   **Request/response correlation**: every outgoing command gets an auto-incremented `_uniq` (plus `_iid` instance id) injected by `_getDefaultParams`. Responses are matched back to their queued entry by `_uniq` in `_processReply`. There is one send queue (`_queue` of `IQEntry`); `_dequeue`/`_sendEntry` send one queued entry at a time over the socket.
-   **Default params**: `_getDefaultParams` stamps `format: json`, `strip`, `agent`, `version` (`<consumer version>/<lib VERSION>`), `language`, `webtoken`, `deviceId`, and conditionally `sessionID`/`clientID` onto every command. Chat commands (those carrying `clientID`) deliberately omit `sessionID`.
-   **Keepalive**: `CMDP_NOOP` is sent on open and re-armed from `_processCmdpNoop`. Do not call `send()` with `CMDP_NOOP` — it throws; NOOP is managed internally.
-   **Reconnect & backoff**: `_onclose`/`_onerror` drive reconnection with an attempt counter; `onError` reports `ReconnectionError` (with `isFatal` at attempt 6). `pause`/`resume` gate reconnection.
-   **Server switch (`CMDC_DSRELOAD`)**: server can order the client to reconnect to a new host/port/path (and `PlatformUrl`). `_processCmdcDsReload` closes the socket, re-queues in-flight entries, rewrites `_settings.host/wssport/wsspath`, and reconnects — transparent to the consumer.
-   **Jump table**: `_jumpTable` maps certain actions (INIT, LOGIN, NOOP, LOGOUT, DSRELOAD, UPDATE, CINIT/CLOGOUT) to internal handlers that maintain `_sessionID`, `_clientID`, and `webtoken` state. `CMDC_UPDATE` can refresh the webtoken mid-session. All responses are also forwarded to the consumer's `commandHandler`.
-   **Transports**: primary is WebSocket (`_onmessage` does `JSON.parse`). JSONP fallback (`_fetchJsonp`, `jsonp: true`, `useWS: false`) exists for environments without WS and uses `_parsePlainResponse` to parse the legacy plain-text (non-JSON) response format.
-   **Network-error retry**: replies with `ResultCode.NETWORK_ERROR` re-enqueue the command (up to 10 retries via `_retry` param) instead of surfacing immediately.

### Version injection

`src/version.ts` exports `VERSION = "__VERSION__"`. The webpack `string-replace-loader` (see `webpack.common.js`) replaces `__VERSION__` with `package.json` version at build time. It is **not** substituted in jest (babel), so `VERSION` is the literal placeholder in tests.

## Conventions & tooling

-   Prettier: 120 cols, double quotes, trailing commas, semicolons, always-parenthesized arrow args. Run `yarn style` before committing formatting changes.
-   ESLint is strict and enforced: explicit function return types and member accessibility are **errors**; private members must be `_camelCase` (leading underscore required); parameter properties and `require` imports are banned; sonarjs + unicorn rulesets are on. `no-explicit-any` is intentionally off (the protocol uses `any` for dynamic param bags).
-   Tests live in `test/` as `*.spec.ts` (jest + babel via `babel.config.js`). They are largely type-shape assertions plus a few WebSocket tests. `*.spec.ts` is excluded from the tsconfig build.

## Publishing / deploy

Publishing is manual by the maintainer (`npm version patch` → `npm publish`; see README). Per org policy, **do not push or publish** — the maintainer does that after review.
