@tak-ps/node-tak
Version:
Lightweight JavaScript library for communicating with TAK Server
155 lines (154 loc) • 6.3 kB
TypeScript
import type { Static } from '@sinclair/typebox';
import type { CoTOptions } from '@tak-ps/node-cot';
import CoT from '@tak-ps/node-cot';
import EventEmitter from 'node:events';
import type { TLSSocket } from 'node:tls';
import TAKAPI from './lib/api.js';
import { TAKAuth } from './lib/auth.js';
import { Queue } from './lib/utils/queue.js';
export * from './lib/auth.js';
export declare const REGEX_CONTROL: RegExp;
export declare const REGEX_EVENT: RegExp;
export interface PartialCoT {
event: string;
remainder: string;
}
/**
* Configuration options for a TAK connection.
*
* Performance-related options control the write pipeline:
*
* ```
* write(cots) process()
* ─────────── ─────────
* for each CoT: while queue has items:
* serialize to XML ──push()──► Ring pop socketBatchSize items
* (returns false ◄──────── Buffer join into one string
* when full) (capacity = socket.write(batch)
* writeQueueSize) stop if backpressure
* when full:
* setImmediate() yield triggered by:
* (lets process() drain) - write() calling process()
* - socket 'drain' event
* ```
*
* @example High-throughput bulk ingestion
* ```typescript
* const tak = await TAK.connect(url, auth, {
* writeQueueSize: 50_000, // large buffer absorbs bursts
* socketBatchSize: 128, // 128 strings per socket.write()
* });
* ```
*
* @example Low-latency real-time streams
* ```typescript
* const tak = await TAK.connect(url, auth, {
* writeQueueSize: 400, // small buffer keeps memory minimal
* socketBatchSize: 10, // flush to socket every 10 items
* });
* ```
*/
export type TAKOptions = {
/** Unique connection identifier. Appears in log messages for debugging.
* Useful when running multiple TAK connections in a single process.
* @default crypto.randomUUID() */
id?: number | string;
/** Connection type label. Informational only — helps distinguish
* connections in logs when multiple are active.
* @default 'unknown' */
type?: string;
/** Options passed through to `@tak-ps/node-cot` for CoT parsing
* (e.g., on incoming `'cot'` events). Does not affect the write pipeline. */
cot?: CoTOptions;
/** Capacity of the ring buffer that sits between `write()` and `process()`.
* When the queue is full, `write()` yields via `setImmediate()` until
* `process()` drains space. Larger values allow more XML strings to be
* buffered, increasing throughput at the cost of higher peak memory.
* @default 10_000 */
writeQueueSize?: number;
/** How many pre-serialized XML strings are popped from the ring buffer
* and joined into a single `socket.write()` call in `process()`. Higher
* values reduce syscall overhead and improve TLS frame packing, but
* increase per-write latency and the size of each socket write.
* @default 64 */
socketBatchSize?: number;
};
export type WriteOptions = {
/** Remove any existing flow tags and replace them with a fresh
* `NodeCoT-*` entry before serializing to XML.
* Useful when re-submitting CoTs back to TAK Server over 8089.
* @default false */
stripFlow?: boolean;
};
export default class TAK extends EventEmitter {
id: number | string;
type: string;
url: URL;
auth: Static<typeof TAKAuth>;
open: boolean;
destroyed: boolean;
writing: boolean;
writeQueueSize: number;
socketBatchSize: number;
cotOptions: CoTOptions;
pingInterval?: ReturnType<typeof setTimeout>;
client?: TLSSocket;
version?: string;
queue: Queue<string>;
/**
* @param url - Full URL of Streaming COT Endpoint IE: "https://ops.cotak.gov:8089"
* @param auth - TAK Certificate Pair
* @param opts - Options Object
* @param opts.id - When using multiple connections in a script, allows a unique ID per connection
* @param opts.type - When using multiple connections in a script, allows specifying a script provided connection type
*/
constructor(url: URL, auth: Static<typeof TAKAuth>, opts?: TAKOptions);
static connect(url: URL, auth: Static<typeof TAKAuth>, opts?: TAKOptions): Promise<TAK>;
connect_ssl(): Promise<TAK>;
reconnect(): Promise<void>;
destroy(): void;
ping(): Promise<void>;
/**
* Drain the queue to the socket.
*
* Pops pre-serialized XML strings from the ring buffer, batches them
* (up to `socketBatchSize` per call), and writes to the socket. Runs
* synchronously in a single event loop tick until the socket signals
* backpressure or the queue is empty.
*
* Called when the socket signals readiness:
* - `'drain'` event (socket buffer cleared, ready for more)
* - After `write()` enqueues new items
*
* Emits `'_flushed'` when the queue drains to zero, waking any
* pending `flush()` calls.
*/
process(): void;
/**
* Write CoTs to the TAK connection.
*
* Serializes each CoT to XML upfront and stores the string in a bounded
* ring buffer. Fully caller-safe: CoT objects can be mutated or GC'd
* immediately after this returns.
* Resolves when all items are queued (not when sent over the wire).
* Use flush() to wait for delivery.
*
* @param cots Array of CoT objects to send
*/
write(cots: CoT[], opts?: WriteOptions): Promise<void>;
/**
* Wait until all queued CoTs have been flushed to the socket.
*
* write() is a fast "enqueue" — it returns once items are in the queue,
* NOT once they've been sent over the wire.
*
* Resolves immediately if nothing is queued.
* Rejects if the connection is destroyed before flush completes.
*/
flush(): Promise<void>;
write_xml(body: string): void;
static findCoT(str: string): null | PartialCoT;
}
export * from './lib/api.js';
export { CommandOutputFormat } from './lib/commands.js';
export { CoT, TAKAPI };