UNPKG

@tak-ps/node-tak

Version:

Lightweight JavaScript library for communicating with TAK Server

155 lines (154 loc) 6.3 kB
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 };