From de035d2ff81dd8caed8c97566bafcf82909dac63 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Thu, 26 Mar 2026 09:18:32 -0300 Subject: [PATCH 1/4] feat: replace grpc transport with fibp (fila binary protocol) rewrite the javascript sdk transport layer from grpc to fibp: - new src/transport.ts: tcp connection via net.socket/tls.connect, length-prefixed binary frame encoding/decoding, correlation id multiplexing (map + promises), auth frame for api key, stream support for consume push delivery - client.ts: lazy connection, fibp request/response for enqueue/ack/nack, async iterable consume via openstream - batcher.ts: adapted to fibp connection instead of grpc client - errors.ts: adds unauthenticatederror, removes grpc status code dep - test helpers rewritten to use fibp + protobufjs for admin ops (createqueue, listqueues readiness probe) - new test/transport.unit.test.ts: 18 unit tests for frame encoding, payload round-trips, and wire format correctness - removes @grpc/grpc-js and @grpc/proto-loader dependencies entirely; adds protobufjs (already transitive) for admin protobuf payloads - removes generated/ grpc type stubs directory - bumps version to 0.3.0 --- README.md | 68 +- generated/admin.ts | 50 -- generated/fila/v1/AckError.ts | 13 - generated/fila/v1/AckErrorCode.ts | 20 - generated/fila/v1/AckMessage.ts | 12 - generated/fila/v1/AckRequest.ts | 11 - generated/fila/v1/AckResponse.ts | 11 - generated/fila/v1/AckResult.ts | 16 - generated/fila/v1/AckSuccess.ts | 8 - generated/fila/v1/AclPermission.ts | 12 - generated/fila/v1/ApiKeyInfo.ts | 19 - generated/fila/v1/ConfigEntry.ts | 12 - generated/fila/v1/ConsumeRequest.ts | 10 - generated/fila/v1/ConsumeResponse.ts | 11 - generated/fila/v1/CreateApiKeyRequest.ts | 15 - generated/fila/v1/CreateApiKeyResponse.ts | 14 - generated/fila/v1/CreateQueueRequest.ts | 13 - generated/fila/v1/CreateQueueResponse.ts | 10 - generated/fila/v1/DeleteQueueRequest.ts | 10 - generated/fila/v1/DeleteQueueResponse.ts | 8 - generated/fila/v1/EnqueueError.ts | 13 - generated/fila/v1/EnqueueErrorCode.ts | 23 - generated/fila/v1/EnqueueMessage.ts | 14 - generated/fila/v1/EnqueueRequest.ts | 11 - generated/fila/v1/EnqueueResponse.ts | 11 - generated/fila/v1/EnqueueResult.ts | 15 - generated/fila/v1/FilaAdmin.ts | 195 ------ generated/fila/v1/FilaService.ts | 75 --- generated/fila/v1/GetAclRequest.ts | 10 - generated/fila/v1/GetAclResponse.ts | 15 - generated/fila/v1/GetConfigRequest.ts | 10 - generated/fila/v1/GetConfigResponse.ts | 10 - generated/fila/v1/GetStatsRequest.ts | 10 - generated/fila/v1/GetStatsResponse.ts | 29 - generated/fila/v1/ListApiKeysRequest.ts | 8 - generated/fila/v1/ListApiKeysResponse.ts | 11 - generated/fila/v1/ListConfigRequest.ts | 10 - generated/fila/v1/ListConfigResponse.ts | 13 - generated/fila/v1/ListQueuesRequest.ts | 8 - generated/fila/v1/ListQueuesResponse.ts | 13 - generated/fila/v1/Message.ts | 20 - generated/fila/v1/MessageMetadata.ts | 18 - generated/fila/v1/MessageTimestamps.ts | 13 - generated/fila/v1/NackError.ts | 13 - generated/fila/v1/NackErrorCode.ts | 20 - generated/fila/v1/NackMessage.ts | 14 - generated/fila/v1/NackRequest.ts | 11 - generated/fila/v1/NackResponse.ts | 11 - generated/fila/v1/NackResult.ts | 16 - generated/fila/v1/NackSuccess.ts | 8 - generated/fila/v1/PerFairnessKeyStats.ts | 17 - generated/fila/v1/PerThrottleKeyStats.ts | 16 - generated/fila/v1/QueueConfig.ts | 15 - generated/fila/v1/QueueInfo.ts | 19 - generated/fila/v1/RedriveRequest.ts | 13 - generated/fila/v1/RedriveResponse.ts | 11 - generated/fila/v1/RevokeApiKeyRequest.ts | 10 - generated/fila/v1/RevokeApiKeyResponse.ts | 8 - generated/fila/v1/SetAclRequest.ts | 13 - generated/fila/v1/SetAclResponse.ts | 8 - generated/fila/v1/SetConfigRequest.ts | 12 - generated/fila/v1/SetConfigResponse.ts | 8 - generated/fila/v1/StreamEnqueueRequest.ts | 14 - generated/fila/v1/StreamEnqueueResponse.ts | 14 - generated/google/protobuf/Timestamp.ts | 13 - generated/messages.ts | 23 - generated/service.ts | 49 -- package-lock.json | 219 +------ package.json | 8 +- src/batcher.ts | 153 +++-- src/client.ts | 657 +++++++++---------- src/errors.ts | 10 +- src/index.ts | 1 + src/transport.ts | 716 +++++++++++++++++++++ test/auth.test.ts | 25 +- test/helpers.ts | 167 ++--- test/transport.unit.test.ts | 313 +++++++++ tsconfig.json | 2 +- 78 files changed, 1559 insertions(+), 1966 deletions(-) delete mode 100644 generated/admin.ts delete mode 100644 generated/fila/v1/AckError.ts delete mode 100644 generated/fila/v1/AckErrorCode.ts delete mode 100644 generated/fila/v1/AckMessage.ts delete mode 100644 generated/fila/v1/AckRequest.ts delete mode 100644 generated/fila/v1/AckResponse.ts delete mode 100644 generated/fila/v1/AckResult.ts delete mode 100644 generated/fila/v1/AckSuccess.ts delete mode 100644 generated/fila/v1/AclPermission.ts delete mode 100644 generated/fila/v1/ApiKeyInfo.ts delete mode 100644 generated/fila/v1/ConfigEntry.ts delete mode 100644 generated/fila/v1/ConsumeRequest.ts delete mode 100644 generated/fila/v1/ConsumeResponse.ts delete mode 100644 generated/fila/v1/CreateApiKeyRequest.ts delete mode 100644 generated/fila/v1/CreateApiKeyResponse.ts delete mode 100644 generated/fila/v1/CreateQueueRequest.ts delete mode 100644 generated/fila/v1/CreateQueueResponse.ts delete mode 100644 generated/fila/v1/DeleteQueueRequest.ts delete mode 100644 generated/fila/v1/DeleteQueueResponse.ts delete mode 100644 generated/fila/v1/EnqueueError.ts delete mode 100644 generated/fila/v1/EnqueueErrorCode.ts delete mode 100644 generated/fila/v1/EnqueueMessage.ts delete mode 100644 generated/fila/v1/EnqueueRequest.ts delete mode 100644 generated/fila/v1/EnqueueResponse.ts delete mode 100644 generated/fila/v1/EnqueueResult.ts delete mode 100644 generated/fila/v1/FilaAdmin.ts delete mode 100644 generated/fila/v1/FilaService.ts delete mode 100644 generated/fila/v1/GetAclRequest.ts delete mode 100644 generated/fila/v1/GetAclResponse.ts delete mode 100644 generated/fila/v1/GetConfigRequest.ts delete mode 100644 generated/fila/v1/GetConfigResponse.ts delete mode 100644 generated/fila/v1/GetStatsRequest.ts delete mode 100644 generated/fila/v1/GetStatsResponse.ts delete mode 100644 generated/fila/v1/ListApiKeysRequest.ts delete mode 100644 generated/fila/v1/ListApiKeysResponse.ts delete mode 100644 generated/fila/v1/ListConfigRequest.ts delete mode 100644 generated/fila/v1/ListConfigResponse.ts delete mode 100644 generated/fila/v1/ListQueuesRequest.ts delete mode 100644 generated/fila/v1/ListQueuesResponse.ts delete mode 100644 generated/fila/v1/Message.ts delete mode 100644 generated/fila/v1/MessageMetadata.ts delete mode 100644 generated/fila/v1/MessageTimestamps.ts delete mode 100644 generated/fila/v1/NackError.ts delete mode 100644 generated/fila/v1/NackErrorCode.ts delete mode 100644 generated/fila/v1/NackMessage.ts delete mode 100644 generated/fila/v1/NackRequest.ts delete mode 100644 generated/fila/v1/NackResponse.ts delete mode 100644 generated/fila/v1/NackResult.ts delete mode 100644 generated/fila/v1/NackSuccess.ts delete mode 100644 generated/fila/v1/PerFairnessKeyStats.ts delete mode 100644 generated/fila/v1/PerThrottleKeyStats.ts delete mode 100644 generated/fila/v1/QueueConfig.ts delete mode 100644 generated/fila/v1/QueueInfo.ts delete mode 100644 generated/fila/v1/RedriveRequest.ts delete mode 100644 generated/fila/v1/RedriveResponse.ts delete mode 100644 generated/fila/v1/RevokeApiKeyRequest.ts delete mode 100644 generated/fila/v1/RevokeApiKeyResponse.ts delete mode 100644 generated/fila/v1/SetAclRequest.ts delete mode 100644 generated/fila/v1/SetAclResponse.ts delete mode 100644 generated/fila/v1/SetConfigRequest.ts delete mode 100644 generated/fila/v1/SetConfigResponse.ts delete mode 100644 generated/fila/v1/StreamEnqueueRequest.ts delete mode 100644 generated/fila/v1/StreamEnqueueResponse.ts delete mode 100644 generated/google/protobuf/Timestamp.ts delete mode 100644 generated/messages.ts delete mode 100644 generated/service.ts create mode 100644 src/transport.ts create mode 100644 test/transport.unit.test.ts diff --git a/README.md b/README.md index f4d952e..b04ff8d 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,8 @@ JavaScript/TypeScript client SDK for the [Fila](https://github.com/faisca/fila) message broker. +Uses the **FIBP** (Fila Binary Protocol) transport — a length-prefixed binary framing protocol over raw TCP, replacing the previous gRPC transport. No protobuf runtime required on the hot path. + ## Installation ```bash @@ -35,7 +37,7 @@ for await (const msg of client.consume("my-queue")) { } } -client.close(); +await client.close(); ``` ### TLS (system trust store) @@ -43,7 +45,7 @@ client.close(); If the Fila server uses a certificate signed by a public CA, enable TLS without providing a CA certificate — the OS system trust store is used automatically: ```typescript -import { Client } from "@fila/client"; +import { Client } from "fila-client"; const client = new Client("localhost:5555", { tls: true }); ``` @@ -67,7 +69,7 @@ Client certificates work with both modes — system trust store or custom CA: ```typescript import * as fs from "fs"; -import { Client } from "@fila/client"; +import { Client } from "fila-client"; // With custom CA: const client = new Client("localhost:5555", { @@ -87,7 +89,7 @@ const client2 = new Client("localhost:5555", { ### API key authentication ```typescript -import { Client } from "@fila/client"; +import { Client } from "fila-client"; const client = new Client("localhost:5555", { apiKey: "my-api-key", @@ -98,7 +100,7 @@ const client = new Client("localhost:5555", { ```typescript import * as fs from "fs"; -import { Client } from "@fila/client"; +import { Client } from "fila-client"; const client = new Client("localhost:5555", { caCert: fs.readFileSync("ca.pem"), @@ -112,21 +114,29 @@ const client = new Client("localhost:5555", { ### `new Client(addr: string, options?: ClientOptions)` -Connect to a Fila broker at the given address (e.g., `"localhost:5555"`). +Connect to a Fila broker at the given address (e.g., `"localhost:5555"`). The TCP connection and FIBP handshake are established lazily on the first operation. **Options:** -| Option | Type | Description | -|-------------|-----------|---------------------------------------------------------------------| -| `tls` | `boolean` | Enable TLS using the OS system trust store. Implied when `caCert` is set. | -| `caCert` | `Buffer` | CA certificate PEM. Enables TLS with a custom CA when set. | -| `clientCert`| `Buffer` | Client certificate PEM for mTLS. Requires TLS to be enabled. | -| `clientKey` | `Buffer` | Client private key PEM for mTLS. Requires TLS to be enabled. | -| `apiKey` | `string` | API key sent as `Bearer` token on every RPC call. | +| Option | Type | Description | +|----------------|-----------|-----------------------------------------------------------------------------------| +| `tls` | `boolean` | Enable TLS using the OS system trust store. Implied when `caCert` is set. | +| `caCert` | `Buffer` | CA certificate PEM. Enables TLS with a custom CA when set. | +| `clientCert` | `Buffer` | Client certificate PEM for mTLS. Requires TLS to be enabled. | +| `clientKey` | `Buffer` | Client private key PEM for mTLS. Requires TLS to be enabled. | +| `apiKey` | `string` | API key sent as an AUTH frame immediately after the FIBP handshake. | +| `batchMode` | `string` | `'auto'` (default), `'linger'`, or `'disabled'`. Controls enqueue batching. | +| `maxBatchSize` | `number` | Maximum batch size for auto mode. Default: 100. | +| `lingerMs` | `number` | Linger timeout in ms for linger mode. Required when `batchMode` is `'linger'`. | +| `batchSize` | `number` | Maximum batch size for linger mode. Required when `batchMode` is `'linger'`. | ### `client.enqueue(queue, headers, payload): Promise` -Enqueue a message. Returns the broker-assigned message ID (UUIDv7). +Enqueue a message. Returns the broker-assigned message ID (UUIDv7). When batching is enabled (default `'auto'`), messages are opportunistically grouped for throughput. + +### `client.enqueueMany(messages): Promise` + +Enqueue multiple messages in a single request, bypassing the batcher. Returns one result per input message in the same order. ### `client.consume(queue): AsyncIterable` @@ -140,22 +150,31 @@ Acknowledge a successfully processed message. The message is permanently removed Negatively acknowledge a failed message. The message is requeued or routed to the dead-letter queue based on the queue's configuration. -### `client.close(): void` +### `client.close(): Promise` -Close the underlying gRPC channel. +Drain any pending batched messages and close the TCP connection. ## Error Handling Per-operation error classes are thrown for specific failure modes: ```typescript -import { QueueNotFoundError, MessageNotFoundError } from "fila-client"; +import { + QueueNotFoundError, + MessageNotFoundError, + UnauthenticatedError, + RPCError, +} from "fila-client"; try { await client.enqueue("missing-queue", null, Buffer.from("test")); } catch (err) { if (err instanceof QueueNotFoundError) { - // handle queue not found + // queue does not exist + } else if (err instanceof UnauthenticatedError) { + // invalid or missing API key + } else if (err instanceof RPCError) { + console.error("wire error code:", err.code); } } @@ -163,11 +182,22 @@ try { await client.ack("my-queue", "missing-id"); } catch (err) { if (err instanceof MessageNotFoundError) { - // handle message not found + // message already acknowledged or expired } } ``` +## Protocol + +Fila uses **FIBP** (Fila Binary Protocol) — a lightweight binary framing protocol over raw TCP (or TLS): + +- **Frame format**: `[4-byte big-endian length][flags:u8 | op:u8 | corr_id:u32 | payload]` +- **Handshake**: client sends `FIBP\x01\x00`; server echoes the same 6 bytes +- **Hot-path ops** (enqueue, consume, ack, nack): custom binary encoding, no protobuf overhead +- **Admin ops**: protobuf-encoded payloads for schema flexibility +- **Authentication**: API key sent in an AUTH frame immediately after the handshake +- **Multiplexing**: correlation IDs map responses to outstanding requests on the same connection + ## License AGPLv3 diff --git a/generated/admin.ts b/generated/admin.ts deleted file mode 100644 index e521396..0000000 --- a/generated/admin.ts +++ /dev/null @@ -1,50 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { MessageTypeDefinition } from '@grpc/proto-loader'; - -import type { FilaAdminClient as _fila_v1_FilaAdminClient, FilaAdminDefinition as _fila_v1_FilaAdminDefinition } from './fila/v1/FilaAdmin'; - -type SubtypeConstructor any, Subtype> = { - new(...args: ConstructorParameters): Subtype; -}; - -export interface ProtoGrpcType { - fila: { - v1: { - AclPermission: MessageTypeDefinition - ApiKeyInfo: MessageTypeDefinition - ConfigEntry: MessageTypeDefinition - CreateApiKeyRequest: MessageTypeDefinition - CreateApiKeyResponse: MessageTypeDefinition - CreateQueueRequest: MessageTypeDefinition - CreateQueueResponse: MessageTypeDefinition - DeleteQueueRequest: MessageTypeDefinition - DeleteQueueResponse: MessageTypeDefinition - FilaAdmin: SubtypeConstructor & { service: _fila_v1_FilaAdminDefinition } - GetAclRequest: MessageTypeDefinition - GetAclResponse: MessageTypeDefinition - GetConfigRequest: MessageTypeDefinition - GetConfigResponse: MessageTypeDefinition - GetStatsRequest: MessageTypeDefinition - GetStatsResponse: MessageTypeDefinition - ListApiKeysRequest: MessageTypeDefinition - ListApiKeysResponse: MessageTypeDefinition - ListConfigRequest: MessageTypeDefinition - ListConfigResponse: MessageTypeDefinition - ListQueuesRequest: MessageTypeDefinition - ListQueuesResponse: MessageTypeDefinition - PerFairnessKeyStats: MessageTypeDefinition - PerThrottleKeyStats: MessageTypeDefinition - QueueConfig: MessageTypeDefinition - QueueInfo: MessageTypeDefinition - RedriveRequest: MessageTypeDefinition - RedriveResponse: MessageTypeDefinition - RevokeApiKeyRequest: MessageTypeDefinition - RevokeApiKeyResponse: MessageTypeDefinition - SetAclRequest: MessageTypeDefinition - SetAclResponse: MessageTypeDefinition - SetConfigRequest: MessageTypeDefinition - SetConfigResponse: MessageTypeDefinition - } - } -} - diff --git a/generated/fila/v1/AckError.ts b/generated/fila/v1/AckError.ts deleted file mode 100644 index bb2f56b..0000000 --- a/generated/fila/v1/AckError.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { AckErrorCode as _fila_v1_AckErrorCode, AckErrorCode__Output as _fila_v1_AckErrorCode__Output } from '../../fila/v1/AckErrorCode'; - -export interface AckError { - 'code'?: (_fila_v1_AckErrorCode); - 'message'?: (string); -} - -export interface AckError__Output { - 'code': (_fila_v1_AckErrorCode__Output); - 'message': (string); -} diff --git a/generated/fila/v1/AckErrorCode.ts b/generated/fila/v1/AckErrorCode.ts deleted file mode 100644 index 04a2113..0000000 --- a/generated/fila/v1/AckErrorCode.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -export const AckErrorCode = { - ACK_ERROR_CODE_UNSPECIFIED: 'ACK_ERROR_CODE_UNSPECIFIED', - ACK_ERROR_CODE_MESSAGE_NOT_FOUND: 'ACK_ERROR_CODE_MESSAGE_NOT_FOUND', - ACK_ERROR_CODE_STORAGE: 'ACK_ERROR_CODE_STORAGE', - ACK_ERROR_CODE_PERMISSION_DENIED: 'ACK_ERROR_CODE_PERMISSION_DENIED', -} as const; - -export type AckErrorCode = - | 'ACK_ERROR_CODE_UNSPECIFIED' - | 0 - | 'ACK_ERROR_CODE_MESSAGE_NOT_FOUND' - | 1 - | 'ACK_ERROR_CODE_STORAGE' - | 2 - | 'ACK_ERROR_CODE_PERMISSION_DENIED' - | 3 - -export type AckErrorCode__Output = typeof AckErrorCode[keyof typeof AckErrorCode] diff --git a/generated/fila/v1/AckMessage.ts b/generated/fila/v1/AckMessage.ts deleted file mode 100644 index dac5ff4..0000000 --- a/generated/fila/v1/AckMessage.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Original file: proto/fila/v1/service.proto - - -export interface AckMessage { - 'queue'?: (string); - 'messageId'?: (string); -} - -export interface AckMessage__Output { - 'queue': (string); - 'messageId': (string); -} diff --git a/generated/fila/v1/AckRequest.ts b/generated/fila/v1/AckRequest.ts deleted file mode 100644 index 3c63ce1..0000000 --- a/generated/fila/v1/AckRequest.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { AckMessage as _fila_v1_AckMessage, AckMessage__Output as _fila_v1_AckMessage__Output } from '../../fila/v1/AckMessage'; - -export interface AckRequest { - 'messages'?: (_fila_v1_AckMessage)[]; -} - -export interface AckRequest__Output { - 'messages': (_fila_v1_AckMessage__Output)[]; -} diff --git a/generated/fila/v1/AckResponse.ts b/generated/fila/v1/AckResponse.ts deleted file mode 100644 index eae1240..0000000 --- a/generated/fila/v1/AckResponse.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { AckResult as _fila_v1_AckResult, AckResult__Output as _fila_v1_AckResult__Output } from '../../fila/v1/AckResult'; - -export interface AckResponse { - 'results'?: (_fila_v1_AckResult)[]; -} - -export interface AckResponse__Output { - 'results': (_fila_v1_AckResult__Output)[]; -} diff --git a/generated/fila/v1/AckResult.ts b/generated/fila/v1/AckResult.ts deleted file mode 100644 index d503b9a..0000000 --- a/generated/fila/v1/AckResult.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { AckSuccess as _fila_v1_AckSuccess, AckSuccess__Output as _fila_v1_AckSuccess__Output } from '../../fila/v1/AckSuccess'; -import type { AckError as _fila_v1_AckError, AckError__Output as _fila_v1_AckError__Output } from '../../fila/v1/AckError'; - -export interface AckResult { - 'success'?: (_fila_v1_AckSuccess | null); - 'error'?: (_fila_v1_AckError | null); - 'result'?: "success"|"error"; -} - -export interface AckResult__Output { - 'success'?: (_fila_v1_AckSuccess__Output | null); - 'error'?: (_fila_v1_AckError__Output | null); - 'result'?: "success"|"error"; -} diff --git a/generated/fila/v1/AckSuccess.ts b/generated/fila/v1/AckSuccess.ts deleted file mode 100644 index 673d632..0000000 --- a/generated/fila/v1/AckSuccess.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/service.proto - - -export interface AckSuccess { -} - -export interface AckSuccess__Output { -} diff --git a/generated/fila/v1/AclPermission.ts b/generated/fila/v1/AclPermission.ts deleted file mode 100644 index 3dec177..0000000 --- a/generated/fila/v1/AclPermission.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface AclPermission { - 'kind'?: (string); - 'pattern'?: (string); -} - -export interface AclPermission__Output { - 'kind': (string); - 'pattern': (string); -} diff --git a/generated/fila/v1/ApiKeyInfo.ts b/generated/fila/v1/ApiKeyInfo.ts deleted file mode 100644 index 0991024..0000000 --- a/generated/fila/v1/ApiKeyInfo.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { Long } from '@grpc/proto-loader'; - -export interface ApiKeyInfo { - 'keyId'?: (string); - 'name'?: (string); - 'createdAtMs'?: (number | string | Long); - 'expiresAtMs'?: (number | string | Long); - 'isSuperadmin'?: (boolean); -} - -export interface ApiKeyInfo__Output { - 'keyId': (string); - 'name': (string); - 'createdAtMs': (string); - 'expiresAtMs': (string); - 'isSuperadmin': (boolean); -} diff --git a/generated/fila/v1/ConfigEntry.ts b/generated/fila/v1/ConfigEntry.ts deleted file mode 100644 index dedefd1..0000000 --- a/generated/fila/v1/ConfigEntry.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface ConfigEntry { - 'key'?: (string); - 'value'?: (string); -} - -export interface ConfigEntry__Output { - 'key': (string); - 'value': (string); -} diff --git a/generated/fila/v1/ConsumeRequest.ts b/generated/fila/v1/ConsumeRequest.ts deleted file mode 100644 index c163cee..0000000 --- a/generated/fila/v1/ConsumeRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/service.proto - - -export interface ConsumeRequest { - 'queue'?: (string); -} - -export interface ConsumeRequest__Output { - 'queue': (string); -} diff --git a/generated/fila/v1/ConsumeResponse.ts b/generated/fila/v1/ConsumeResponse.ts deleted file mode 100644 index bc1f398..0000000 --- a/generated/fila/v1/ConsumeResponse.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { Message as _fila_v1_Message, Message__Output as _fila_v1_Message__Output } from '../../fila/v1/Message'; - -export interface ConsumeResponse { - 'messages'?: (_fila_v1_Message)[]; -} - -export interface ConsumeResponse__Output { - 'messages': (_fila_v1_Message__Output)[]; -} diff --git a/generated/fila/v1/CreateApiKeyRequest.ts b/generated/fila/v1/CreateApiKeyRequest.ts deleted file mode 100644 index 8787c8e..0000000 --- a/generated/fila/v1/CreateApiKeyRequest.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { Long } from '@grpc/proto-loader'; - -export interface CreateApiKeyRequest { - 'name'?: (string); - 'expiresAtMs'?: (number | string | Long); - 'isSuperadmin'?: (boolean); -} - -export interface CreateApiKeyRequest__Output { - 'name': (string); - 'expiresAtMs': (string); - 'isSuperadmin': (boolean); -} diff --git a/generated/fila/v1/CreateApiKeyResponse.ts b/generated/fila/v1/CreateApiKeyResponse.ts deleted file mode 100644 index a25a390..0000000 --- a/generated/fila/v1/CreateApiKeyResponse.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface CreateApiKeyResponse { - 'keyId'?: (string); - 'key'?: (string); - 'isSuperadmin'?: (boolean); -} - -export interface CreateApiKeyResponse__Output { - 'keyId': (string); - 'key': (string); - 'isSuperadmin': (boolean); -} diff --git a/generated/fila/v1/CreateQueueRequest.ts b/generated/fila/v1/CreateQueueRequest.ts deleted file mode 100644 index 10e1367..0000000 --- a/generated/fila/v1/CreateQueueRequest.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { QueueConfig as _fila_v1_QueueConfig, QueueConfig__Output as _fila_v1_QueueConfig__Output } from '../../fila/v1/QueueConfig'; - -export interface CreateQueueRequest { - 'name'?: (string); - 'config'?: (_fila_v1_QueueConfig | null); -} - -export interface CreateQueueRequest__Output { - 'name': (string); - 'config': (_fila_v1_QueueConfig__Output | null); -} diff --git a/generated/fila/v1/CreateQueueResponse.ts b/generated/fila/v1/CreateQueueResponse.ts deleted file mode 100644 index 515b5bc..0000000 --- a/generated/fila/v1/CreateQueueResponse.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface CreateQueueResponse { - 'queueId'?: (string); -} - -export interface CreateQueueResponse__Output { - 'queueId': (string); -} diff --git a/generated/fila/v1/DeleteQueueRequest.ts b/generated/fila/v1/DeleteQueueRequest.ts deleted file mode 100644 index f37e736..0000000 --- a/generated/fila/v1/DeleteQueueRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface DeleteQueueRequest { - 'queue'?: (string); -} - -export interface DeleteQueueRequest__Output { - 'queue': (string); -} diff --git a/generated/fila/v1/DeleteQueueResponse.ts b/generated/fila/v1/DeleteQueueResponse.ts deleted file mode 100644 index 389d1d0..0000000 --- a/generated/fila/v1/DeleteQueueResponse.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface DeleteQueueResponse { -} - -export interface DeleteQueueResponse__Output { -} diff --git a/generated/fila/v1/EnqueueError.ts b/generated/fila/v1/EnqueueError.ts deleted file mode 100644 index 82fe40e..0000000 --- a/generated/fila/v1/EnqueueError.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { EnqueueErrorCode as _fila_v1_EnqueueErrorCode, EnqueueErrorCode__Output as _fila_v1_EnqueueErrorCode__Output } from '../../fila/v1/EnqueueErrorCode'; - -export interface EnqueueError { - 'code'?: (_fila_v1_EnqueueErrorCode); - 'message'?: (string); -} - -export interface EnqueueError__Output { - 'code': (_fila_v1_EnqueueErrorCode__Output); - 'message': (string); -} diff --git a/generated/fila/v1/EnqueueErrorCode.ts b/generated/fila/v1/EnqueueErrorCode.ts deleted file mode 100644 index cc0f38d..0000000 --- a/generated/fila/v1/EnqueueErrorCode.ts +++ /dev/null @@ -1,23 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -export const EnqueueErrorCode = { - ENQUEUE_ERROR_CODE_UNSPECIFIED: 'ENQUEUE_ERROR_CODE_UNSPECIFIED', - ENQUEUE_ERROR_CODE_QUEUE_NOT_FOUND: 'ENQUEUE_ERROR_CODE_QUEUE_NOT_FOUND', - ENQUEUE_ERROR_CODE_STORAGE: 'ENQUEUE_ERROR_CODE_STORAGE', - ENQUEUE_ERROR_CODE_LUA: 'ENQUEUE_ERROR_CODE_LUA', - ENQUEUE_ERROR_CODE_PERMISSION_DENIED: 'ENQUEUE_ERROR_CODE_PERMISSION_DENIED', -} as const; - -export type EnqueueErrorCode = - | 'ENQUEUE_ERROR_CODE_UNSPECIFIED' - | 0 - | 'ENQUEUE_ERROR_CODE_QUEUE_NOT_FOUND' - | 1 - | 'ENQUEUE_ERROR_CODE_STORAGE' - | 2 - | 'ENQUEUE_ERROR_CODE_LUA' - | 3 - | 'ENQUEUE_ERROR_CODE_PERMISSION_DENIED' - | 4 - -export type EnqueueErrorCode__Output = typeof EnqueueErrorCode[keyof typeof EnqueueErrorCode] diff --git a/generated/fila/v1/EnqueueMessage.ts b/generated/fila/v1/EnqueueMessage.ts deleted file mode 100644 index 04b917d..0000000 --- a/generated/fila/v1/EnqueueMessage.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Original file: proto/fila/v1/service.proto - - -export interface EnqueueMessage { - 'queue'?: (string); - 'headers'?: ({[key: string]: string}); - 'payload'?: (Buffer | Uint8Array | string); -} - -export interface EnqueueMessage__Output { - 'queue': (string); - 'headers': ({[key: string]: string}); - 'payload': (Buffer); -} diff --git a/generated/fila/v1/EnqueueRequest.ts b/generated/fila/v1/EnqueueRequest.ts deleted file mode 100644 index e6f0c8e..0000000 --- a/generated/fila/v1/EnqueueRequest.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { EnqueueMessage as _fila_v1_EnqueueMessage, EnqueueMessage__Output as _fila_v1_EnqueueMessage__Output } from '../../fila/v1/EnqueueMessage'; - -export interface EnqueueRequest { - 'messages'?: (_fila_v1_EnqueueMessage)[]; -} - -export interface EnqueueRequest__Output { - 'messages': (_fila_v1_EnqueueMessage__Output)[]; -} diff --git a/generated/fila/v1/EnqueueResponse.ts b/generated/fila/v1/EnqueueResponse.ts deleted file mode 100644 index 271c440..0000000 --- a/generated/fila/v1/EnqueueResponse.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { EnqueueResult as _fila_v1_EnqueueResult, EnqueueResult__Output as _fila_v1_EnqueueResult__Output } from '../../fila/v1/EnqueueResult'; - -export interface EnqueueResponse { - 'results'?: (_fila_v1_EnqueueResult)[]; -} - -export interface EnqueueResponse__Output { - 'results': (_fila_v1_EnqueueResult__Output)[]; -} diff --git a/generated/fila/v1/EnqueueResult.ts b/generated/fila/v1/EnqueueResult.ts deleted file mode 100644 index 3691008..0000000 --- a/generated/fila/v1/EnqueueResult.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { EnqueueError as _fila_v1_EnqueueError, EnqueueError__Output as _fila_v1_EnqueueError__Output } from '../../fila/v1/EnqueueError'; - -export interface EnqueueResult { - 'messageId'?: (string); - 'error'?: (_fila_v1_EnqueueError | null); - 'result'?: "messageId"|"error"; -} - -export interface EnqueueResult__Output { - 'messageId'?: (string); - 'error'?: (_fila_v1_EnqueueError__Output | null); - 'result'?: "messageId"|"error"; -} diff --git a/generated/fila/v1/FilaAdmin.ts b/generated/fila/v1/FilaAdmin.ts deleted file mode 100644 index 977f024..0000000 --- a/generated/fila/v1/FilaAdmin.ts +++ /dev/null @@ -1,195 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type * as grpc from '@grpc/grpc-js' -import type { MethodDefinition } from '@grpc/proto-loader' -import type { CreateApiKeyRequest as _fila_v1_CreateApiKeyRequest, CreateApiKeyRequest__Output as _fila_v1_CreateApiKeyRequest__Output } from '../../fila/v1/CreateApiKeyRequest'; -import type { CreateApiKeyResponse as _fila_v1_CreateApiKeyResponse, CreateApiKeyResponse__Output as _fila_v1_CreateApiKeyResponse__Output } from '../../fila/v1/CreateApiKeyResponse'; -import type { CreateQueueRequest as _fila_v1_CreateQueueRequest, CreateQueueRequest__Output as _fila_v1_CreateQueueRequest__Output } from '../../fila/v1/CreateQueueRequest'; -import type { CreateQueueResponse as _fila_v1_CreateQueueResponse, CreateQueueResponse__Output as _fila_v1_CreateQueueResponse__Output } from '../../fila/v1/CreateQueueResponse'; -import type { DeleteQueueRequest as _fila_v1_DeleteQueueRequest, DeleteQueueRequest__Output as _fila_v1_DeleteQueueRequest__Output } from '../../fila/v1/DeleteQueueRequest'; -import type { DeleteQueueResponse as _fila_v1_DeleteQueueResponse, DeleteQueueResponse__Output as _fila_v1_DeleteQueueResponse__Output } from '../../fila/v1/DeleteQueueResponse'; -import type { GetAclRequest as _fila_v1_GetAclRequest, GetAclRequest__Output as _fila_v1_GetAclRequest__Output } from '../../fila/v1/GetAclRequest'; -import type { GetAclResponse as _fila_v1_GetAclResponse, GetAclResponse__Output as _fila_v1_GetAclResponse__Output } from '../../fila/v1/GetAclResponse'; -import type { GetConfigRequest as _fila_v1_GetConfigRequest, GetConfigRequest__Output as _fila_v1_GetConfigRequest__Output } from '../../fila/v1/GetConfigRequest'; -import type { GetConfigResponse as _fila_v1_GetConfigResponse, GetConfigResponse__Output as _fila_v1_GetConfigResponse__Output } from '../../fila/v1/GetConfigResponse'; -import type { GetStatsRequest as _fila_v1_GetStatsRequest, GetStatsRequest__Output as _fila_v1_GetStatsRequest__Output } from '../../fila/v1/GetStatsRequest'; -import type { GetStatsResponse as _fila_v1_GetStatsResponse, GetStatsResponse__Output as _fila_v1_GetStatsResponse__Output } from '../../fila/v1/GetStatsResponse'; -import type { ListApiKeysRequest as _fila_v1_ListApiKeysRequest, ListApiKeysRequest__Output as _fila_v1_ListApiKeysRequest__Output } from '../../fila/v1/ListApiKeysRequest'; -import type { ListApiKeysResponse as _fila_v1_ListApiKeysResponse, ListApiKeysResponse__Output as _fila_v1_ListApiKeysResponse__Output } from '../../fila/v1/ListApiKeysResponse'; -import type { ListConfigRequest as _fila_v1_ListConfigRequest, ListConfigRequest__Output as _fila_v1_ListConfigRequest__Output } from '../../fila/v1/ListConfigRequest'; -import type { ListConfigResponse as _fila_v1_ListConfigResponse, ListConfigResponse__Output as _fila_v1_ListConfigResponse__Output } from '../../fila/v1/ListConfigResponse'; -import type { ListQueuesRequest as _fila_v1_ListQueuesRequest, ListQueuesRequest__Output as _fila_v1_ListQueuesRequest__Output } from '../../fila/v1/ListQueuesRequest'; -import type { ListQueuesResponse as _fila_v1_ListQueuesResponse, ListQueuesResponse__Output as _fila_v1_ListQueuesResponse__Output } from '../../fila/v1/ListQueuesResponse'; -import type { RedriveRequest as _fila_v1_RedriveRequest, RedriveRequest__Output as _fila_v1_RedriveRequest__Output } from '../../fila/v1/RedriveRequest'; -import type { RedriveResponse as _fila_v1_RedriveResponse, RedriveResponse__Output as _fila_v1_RedriveResponse__Output } from '../../fila/v1/RedriveResponse'; -import type { RevokeApiKeyRequest as _fila_v1_RevokeApiKeyRequest, RevokeApiKeyRequest__Output as _fila_v1_RevokeApiKeyRequest__Output } from '../../fila/v1/RevokeApiKeyRequest'; -import type { RevokeApiKeyResponse as _fila_v1_RevokeApiKeyResponse, RevokeApiKeyResponse__Output as _fila_v1_RevokeApiKeyResponse__Output } from '../../fila/v1/RevokeApiKeyResponse'; -import type { SetAclRequest as _fila_v1_SetAclRequest, SetAclRequest__Output as _fila_v1_SetAclRequest__Output } from '../../fila/v1/SetAclRequest'; -import type { SetAclResponse as _fila_v1_SetAclResponse, SetAclResponse__Output as _fila_v1_SetAclResponse__Output } from '../../fila/v1/SetAclResponse'; -import type { SetConfigRequest as _fila_v1_SetConfigRequest, SetConfigRequest__Output as _fila_v1_SetConfigRequest__Output } from '../../fila/v1/SetConfigRequest'; -import type { SetConfigResponse as _fila_v1_SetConfigResponse, SetConfigResponse__Output as _fila_v1_SetConfigResponse__Output } from '../../fila/v1/SetConfigResponse'; - -export interface FilaAdminClient extends grpc.Client { - CreateApiKey(argument: _fila_v1_CreateApiKeyRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - CreateApiKey(argument: _fila_v1_CreateApiKeyRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - CreateApiKey(argument: _fila_v1_CreateApiKeyRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - CreateApiKey(argument: _fila_v1_CreateApiKeyRequest, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - createApiKey(argument: _fila_v1_CreateApiKeyRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - createApiKey(argument: _fila_v1_CreateApiKeyRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - createApiKey(argument: _fila_v1_CreateApiKeyRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - createApiKey(argument: _fila_v1_CreateApiKeyRequest, callback: grpc.requestCallback<_fila_v1_CreateApiKeyResponse__Output>): grpc.ClientUnaryCall; - - CreateQueue(argument: _fila_v1_CreateQueueRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - CreateQueue(argument: _fila_v1_CreateQueueRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - CreateQueue(argument: _fila_v1_CreateQueueRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - CreateQueue(argument: _fila_v1_CreateQueueRequest, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - createQueue(argument: _fila_v1_CreateQueueRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - createQueue(argument: _fila_v1_CreateQueueRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - createQueue(argument: _fila_v1_CreateQueueRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - createQueue(argument: _fila_v1_CreateQueueRequest, callback: grpc.requestCallback<_fila_v1_CreateQueueResponse__Output>): grpc.ClientUnaryCall; - - DeleteQueue(argument: _fila_v1_DeleteQueueRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - DeleteQueue(argument: _fila_v1_DeleteQueueRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - DeleteQueue(argument: _fila_v1_DeleteQueueRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - DeleteQueue(argument: _fila_v1_DeleteQueueRequest, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - deleteQueue(argument: _fila_v1_DeleteQueueRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - deleteQueue(argument: _fila_v1_DeleteQueueRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - deleteQueue(argument: _fila_v1_DeleteQueueRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - deleteQueue(argument: _fila_v1_DeleteQueueRequest, callback: grpc.requestCallback<_fila_v1_DeleteQueueResponse__Output>): grpc.ClientUnaryCall; - - GetAcl(argument: _fila_v1_GetAclRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - GetAcl(argument: _fila_v1_GetAclRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - GetAcl(argument: _fila_v1_GetAclRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - GetAcl(argument: _fila_v1_GetAclRequest, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - getAcl(argument: _fila_v1_GetAclRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - getAcl(argument: _fila_v1_GetAclRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - getAcl(argument: _fila_v1_GetAclRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - getAcl(argument: _fila_v1_GetAclRequest, callback: grpc.requestCallback<_fila_v1_GetAclResponse__Output>): grpc.ClientUnaryCall; - - GetConfig(argument: _fila_v1_GetConfigRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - GetConfig(argument: _fila_v1_GetConfigRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - GetConfig(argument: _fila_v1_GetConfigRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - GetConfig(argument: _fila_v1_GetConfigRequest, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - getConfig(argument: _fila_v1_GetConfigRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - getConfig(argument: _fila_v1_GetConfigRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - getConfig(argument: _fila_v1_GetConfigRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - getConfig(argument: _fila_v1_GetConfigRequest, callback: grpc.requestCallback<_fila_v1_GetConfigResponse__Output>): grpc.ClientUnaryCall; - - GetStats(argument: _fila_v1_GetStatsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - GetStats(argument: _fila_v1_GetStatsRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - GetStats(argument: _fila_v1_GetStatsRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - GetStats(argument: _fila_v1_GetStatsRequest, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - getStats(argument: _fila_v1_GetStatsRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - getStats(argument: _fila_v1_GetStatsRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - getStats(argument: _fila_v1_GetStatsRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - getStats(argument: _fila_v1_GetStatsRequest, callback: grpc.requestCallback<_fila_v1_GetStatsResponse__Output>): grpc.ClientUnaryCall; - - ListApiKeys(argument: _fila_v1_ListApiKeysRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - ListApiKeys(argument: _fila_v1_ListApiKeysRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - ListApiKeys(argument: _fila_v1_ListApiKeysRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - ListApiKeys(argument: _fila_v1_ListApiKeysRequest, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - listApiKeys(argument: _fila_v1_ListApiKeysRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - listApiKeys(argument: _fila_v1_ListApiKeysRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - listApiKeys(argument: _fila_v1_ListApiKeysRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - listApiKeys(argument: _fila_v1_ListApiKeysRequest, callback: grpc.requestCallback<_fila_v1_ListApiKeysResponse__Output>): grpc.ClientUnaryCall; - - ListConfig(argument: _fila_v1_ListConfigRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - ListConfig(argument: _fila_v1_ListConfigRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - ListConfig(argument: _fila_v1_ListConfigRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - ListConfig(argument: _fila_v1_ListConfigRequest, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - listConfig(argument: _fila_v1_ListConfigRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - listConfig(argument: _fila_v1_ListConfigRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - listConfig(argument: _fila_v1_ListConfigRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - listConfig(argument: _fila_v1_ListConfigRequest, callback: grpc.requestCallback<_fila_v1_ListConfigResponse__Output>): grpc.ClientUnaryCall; - - ListQueues(argument: _fila_v1_ListQueuesRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - ListQueues(argument: _fila_v1_ListQueuesRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - ListQueues(argument: _fila_v1_ListQueuesRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - ListQueues(argument: _fila_v1_ListQueuesRequest, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - listQueues(argument: _fila_v1_ListQueuesRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - listQueues(argument: _fila_v1_ListQueuesRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - listQueues(argument: _fila_v1_ListQueuesRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - listQueues(argument: _fila_v1_ListQueuesRequest, callback: grpc.requestCallback<_fila_v1_ListQueuesResponse__Output>): grpc.ClientUnaryCall; - - Redrive(argument: _fila_v1_RedriveRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - Redrive(argument: _fila_v1_RedriveRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - Redrive(argument: _fila_v1_RedriveRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - Redrive(argument: _fila_v1_RedriveRequest, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - redrive(argument: _fila_v1_RedriveRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - redrive(argument: _fila_v1_RedriveRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - redrive(argument: _fila_v1_RedriveRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - redrive(argument: _fila_v1_RedriveRequest, callback: grpc.requestCallback<_fila_v1_RedriveResponse__Output>): grpc.ClientUnaryCall; - - RevokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - RevokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - RevokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - RevokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - revokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - revokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - revokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - revokeApiKey(argument: _fila_v1_RevokeApiKeyRequest, callback: grpc.requestCallback<_fila_v1_RevokeApiKeyResponse__Output>): grpc.ClientUnaryCall; - - SetAcl(argument: _fila_v1_SetAclRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - SetAcl(argument: _fila_v1_SetAclRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - SetAcl(argument: _fila_v1_SetAclRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - SetAcl(argument: _fila_v1_SetAclRequest, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - setAcl(argument: _fila_v1_SetAclRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - setAcl(argument: _fila_v1_SetAclRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - setAcl(argument: _fila_v1_SetAclRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - setAcl(argument: _fila_v1_SetAclRequest, callback: grpc.requestCallback<_fila_v1_SetAclResponse__Output>): grpc.ClientUnaryCall; - - SetConfig(argument: _fila_v1_SetConfigRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - SetConfig(argument: _fila_v1_SetConfigRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - SetConfig(argument: _fila_v1_SetConfigRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - SetConfig(argument: _fila_v1_SetConfigRequest, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - setConfig(argument: _fila_v1_SetConfigRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - setConfig(argument: _fila_v1_SetConfigRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - setConfig(argument: _fila_v1_SetConfigRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - setConfig(argument: _fila_v1_SetConfigRequest, callback: grpc.requestCallback<_fila_v1_SetConfigResponse__Output>): grpc.ClientUnaryCall; - -} - -export interface FilaAdminHandlers extends grpc.UntypedServiceImplementation { - CreateApiKey: grpc.handleUnaryCall<_fila_v1_CreateApiKeyRequest__Output, _fila_v1_CreateApiKeyResponse>; - - CreateQueue: grpc.handleUnaryCall<_fila_v1_CreateQueueRequest__Output, _fila_v1_CreateQueueResponse>; - - DeleteQueue: grpc.handleUnaryCall<_fila_v1_DeleteQueueRequest__Output, _fila_v1_DeleteQueueResponse>; - - GetAcl: grpc.handleUnaryCall<_fila_v1_GetAclRequest__Output, _fila_v1_GetAclResponse>; - - GetConfig: grpc.handleUnaryCall<_fila_v1_GetConfigRequest__Output, _fila_v1_GetConfigResponse>; - - GetStats: grpc.handleUnaryCall<_fila_v1_GetStatsRequest__Output, _fila_v1_GetStatsResponse>; - - ListApiKeys: grpc.handleUnaryCall<_fila_v1_ListApiKeysRequest__Output, _fila_v1_ListApiKeysResponse>; - - ListConfig: grpc.handleUnaryCall<_fila_v1_ListConfigRequest__Output, _fila_v1_ListConfigResponse>; - - ListQueues: grpc.handleUnaryCall<_fila_v1_ListQueuesRequest__Output, _fila_v1_ListQueuesResponse>; - - Redrive: grpc.handleUnaryCall<_fila_v1_RedriveRequest__Output, _fila_v1_RedriveResponse>; - - RevokeApiKey: grpc.handleUnaryCall<_fila_v1_RevokeApiKeyRequest__Output, _fila_v1_RevokeApiKeyResponse>; - - SetAcl: grpc.handleUnaryCall<_fila_v1_SetAclRequest__Output, _fila_v1_SetAclResponse>; - - SetConfig: grpc.handleUnaryCall<_fila_v1_SetConfigRequest__Output, _fila_v1_SetConfigResponse>; - -} - -export interface FilaAdminDefinition extends grpc.ServiceDefinition { - CreateApiKey: MethodDefinition<_fila_v1_CreateApiKeyRequest, _fila_v1_CreateApiKeyResponse, _fila_v1_CreateApiKeyRequest__Output, _fila_v1_CreateApiKeyResponse__Output> - CreateQueue: MethodDefinition<_fila_v1_CreateQueueRequest, _fila_v1_CreateQueueResponse, _fila_v1_CreateQueueRequest__Output, _fila_v1_CreateQueueResponse__Output> - DeleteQueue: MethodDefinition<_fila_v1_DeleteQueueRequest, _fila_v1_DeleteQueueResponse, _fila_v1_DeleteQueueRequest__Output, _fila_v1_DeleteQueueResponse__Output> - GetAcl: MethodDefinition<_fila_v1_GetAclRequest, _fila_v1_GetAclResponse, _fila_v1_GetAclRequest__Output, _fila_v1_GetAclResponse__Output> - GetConfig: MethodDefinition<_fila_v1_GetConfigRequest, _fila_v1_GetConfigResponse, _fila_v1_GetConfigRequest__Output, _fila_v1_GetConfigResponse__Output> - GetStats: MethodDefinition<_fila_v1_GetStatsRequest, _fila_v1_GetStatsResponse, _fila_v1_GetStatsRequest__Output, _fila_v1_GetStatsResponse__Output> - ListApiKeys: MethodDefinition<_fila_v1_ListApiKeysRequest, _fila_v1_ListApiKeysResponse, _fila_v1_ListApiKeysRequest__Output, _fila_v1_ListApiKeysResponse__Output> - ListConfig: MethodDefinition<_fila_v1_ListConfigRequest, _fila_v1_ListConfigResponse, _fila_v1_ListConfigRequest__Output, _fila_v1_ListConfigResponse__Output> - ListQueues: MethodDefinition<_fila_v1_ListQueuesRequest, _fila_v1_ListQueuesResponse, _fila_v1_ListQueuesRequest__Output, _fila_v1_ListQueuesResponse__Output> - Redrive: MethodDefinition<_fila_v1_RedriveRequest, _fila_v1_RedriveResponse, _fila_v1_RedriveRequest__Output, _fila_v1_RedriveResponse__Output> - RevokeApiKey: MethodDefinition<_fila_v1_RevokeApiKeyRequest, _fila_v1_RevokeApiKeyResponse, _fila_v1_RevokeApiKeyRequest__Output, _fila_v1_RevokeApiKeyResponse__Output> - SetAcl: MethodDefinition<_fila_v1_SetAclRequest, _fila_v1_SetAclResponse, _fila_v1_SetAclRequest__Output, _fila_v1_SetAclResponse__Output> - SetConfig: MethodDefinition<_fila_v1_SetConfigRequest, _fila_v1_SetConfigResponse, _fila_v1_SetConfigRequest__Output, _fila_v1_SetConfigResponse__Output> -} diff --git a/generated/fila/v1/FilaService.ts b/generated/fila/v1/FilaService.ts deleted file mode 100644 index 868ec1b..0000000 --- a/generated/fila/v1/FilaService.ts +++ /dev/null @@ -1,75 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type * as grpc from '@grpc/grpc-js' -import type { MethodDefinition } from '@grpc/proto-loader' -import type { AckRequest as _fila_v1_AckRequest, AckRequest__Output as _fila_v1_AckRequest__Output } from '../../fila/v1/AckRequest'; -import type { AckResponse as _fila_v1_AckResponse, AckResponse__Output as _fila_v1_AckResponse__Output } from '../../fila/v1/AckResponse'; -import type { ConsumeRequest as _fila_v1_ConsumeRequest, ConsumeRequest__Output as _fila_v1_ConsumeRequest__Output } from '../../fila/v1/ConsumeRequest'; -import type { ConsumeResponse as _fila_v1_ConsumeResponse, ConsumeResponse__Output as _fila_v1_ConsumeResponse__Output } from '../../fila/v1/ConsumeResponse'; -import type { EnqueueRequest as _fila_v1_EnqueueRequest, EnqueueRequest__Output as _fila_v1_EnqueueRequest__Output } from '../../fila/v1/EnqueueRequest'; -import type { EnqueueResponse as _fila_v1_EnqueueResponse, EnqueueResponse__Output as _fila_v1_EnqueueResponse__Output } from '../../fila/v1/EnqueueResponse'; -import type { NackRequest as _fila_v1_NackRequest, NackRequest__Output as _fila_v1_NackRequest__Output } from '../../fila/v1/NackRequest'; -import type { NackResponse as _fila_v1_NackResponse, NackResponse__Output as _fila_v1_NackResponse__Output } from '../../fila/v1/NackResponse'; -import type { StreamEnqueueRequest as _fila_v1_StreamEnqueueRequest, StreamEnqueueRequest__Output as _fila_v1_StreamEnqueueRequest__Output } from '../../fila/v1/StreamEnqueueRequest'; -import type { StreamEnqueueResponse as _fila_v1_StreamEnqueueResponse, StreamEnqueueResponse__Output as _fila_v1_StreamEnqueueResponse__Output } from '../../fila/v1/StreamEnqueueResponse'; - -export interface FilaServiceClient extends grpc.Client { - Ack(argument: _fila_v1_AckRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - Ack(argument: _fila_v1_AckRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - Ack(argument: _fila_v1_AckRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - Ack(argument: _fila_v1_AckRequest, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - ack(argument: _fila_v1_AckRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - ack(argument: _fila_v1_AckRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - ack(argument: _fila_v1_AckRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - ack(argument: _fila_v1_AckRequest, callback: grpc.requestCallback<_fila_v1_AckResponse__Output>): grpc.ClientUnaryCall; - - Consume(argument: _fila_v1_ConsumeRequest, metadata: grpc.Metadata, options?: grpc.CallOptions): grpc.ClientReadableStream<_fila_v1_ConsumeResponse__Output>; - Consume(argument: _fila_v1_ConsumeRequest, options?: grpc.CallOptions): grpc.ClientReadableStream<_fila_v1_ConsumeResponse__Output>; - consume(argument: _fila_v1_ConsumeRequest, metadata: grpc.Metadata, options?: grpc.CallOptions): grpc.ClientReadableStream<_fila_v1_ConsumeResponse__Output>; - consume(argument: _fila_v1_ConsumeRequest, options?: grpc.CallOptions): grpc.ClientReadableStream<_fila_v1_ConsumeResponse__Output>; - - Enqueue(argument: _fila_v1_EnqueueRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - Enqueue(argument: _fila_v1_EnqueueRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - Enqueue(argument: _fila_v1_EnqueueRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - Enqueue(argument: _fila_v1_EnqueueRequest, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - enqueue(argument: _fila_v1_EnqueueRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - enqueue(argument: _fila_v1_EnqueueRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - enqueue(argument: _fila_v1_EnqueueRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - enqueue(argument: _fila_v1_EnqueueRequest, callback: grpc.requestCallback<_fila_v1_EnqueueResponse__Output>): grpc.ClientUnaryCall; - - Nack(argument: _fila_v1_NackRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - Nack(argument: _fila_v1_NackRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - Nack(argument: _fila_v1_NackRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - Nack(argument: _fila_v1_NackRequest, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - nack(argument: _fila_v1_NackRequest, metadata: grpc.Metadata, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - nack(argument: _fila_v1_NackRequest, metadata: grpc.Metadata, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - nack(argument: _fila_v1_NackRequest, options: grpc.CallOptions, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - nack(argument: _fila_v1_NackRequest, callback: grpc.requestCallback<_fila_v1_NackResponse__Output>): grpc.ClientUnaryCall; - - StreamEnqueue(metadata: grpc.Metadata, options?: grpc.CallOptions): grpc.ClientDuplexStream<_fila_v1_StreamEnqueueRequest, _fila_v1_StreamEnqueueResponse__Output>; - StreamEnqueue(options?: grpc.CallOptions): grpc.ClientDuplexStream<_fila_v1_StreamEnqueueRequest, _fila_v1_StreamEnqueueResponse__Output>; - streamEnqueue(metadata: grpc.Metadata, options?: grpc.CallOptions): grpc.ClientDuplexStream<_fila_v1_StreamEnqueueRequest, _fila_v1_StreamEnqueueResponse__Output>; - streamEnqueue(options?: grpc.CallOptions): grpc.ClientDuplexStream<_fila_v1_StreamEnqueueRequest, _fila_v1_StreamEnqueueResponse__Output>; - -} - -export interface FilaServiceHandlers extends grpc.UntypedServiceImplementation { - Ack: grpc.handleUnaryCall<_fila_v1_AckRequest__Output, _fila_v1_AckResponse>; - - Consume: grpc.handleServerStreamingCall<_fila_v1_ConsumeRequest__Output, _fila_v1_ConsumeResponse>; - - Enqueue: grpc.handleUnaryCall<_fila_v1_EnqueueRequest__Output, _fila_v1_EnqueueResponse>; - - Nack: grpc.handleUnaryCall<_fila_v1_NackRequest__Output, _fila_v1_NackResponse>; - - StreamEnqueue: grpc.handleBidiStreamingCall<_fila_v1_StreamEnqueueRequest__Output, _fila_v1_StreamEnqueueResponse>; - -} - -export interface FilaServiceDefinition extends grpc.ServiceDefinition { - Ack: MethodDefinition<_fila_v1_AckRequest, _fila_v1_AckResponse, _fila_v1_AckRequest__Output, _fila_v1_AckResponse__Output> - Consume: MethodDefinition<_fila_v1_ConsumeRequest, _fila_v1_ConsumeResponse, _fila_v1_ConsumeRequest__Output, _fila_v1_ConsumeResponse__Output> - Enqueue: MethodDefinition<_fila_v1_EnqueueRequest, _fila_v1_EnqueueResponse, _fila_v1_EnqueueRequest__Output, _fila_v1_EnqueueResponse__Output> - Nack: MethodDefinition<_fila_v1_NackRequest, _fila_v1_NackResponse, _fila_v1_NackRequest__Output, _fila_v1_NackResponse__Output> - StreamEnqueue: MethodDefinition<_fila_v1_StreamEnqueueRequest, _fila_v1_StreamEnqueueResponse, _fila_v1_StreamEnqueueRequest__Output, _fila_v1_StreamEnqueueResponse__Output> -} diff --git a/generated/fila/v1/GetAclRequest.ts b/generated/fila/v1/GetAclRequest.ts deleted file mode 100644 index 4320684..0000000 --- a/generated/fila/v1/GetAclRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface GetAclRequest { - 'keyId'?: (string); -} - -export interface GetAclRequest__Output { - 'keyId': (string); -} diff --git a/generated/fila/v1/GetAclResponse.ts b/generated/fila/v1/GetAclResponse.ts deleted file mode 100644 index 71b5ecd..0000000 --- a/generated/fila/v1/GetAclResponse.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { AclPermission as _fila_v1_AclPermission, AclPermission__Output as _fila_v1_AclPermission__Output } from '../../fila/v1/AclPermission'; - -export interface GetAclResponse { - 'keyId'?: (string); - 'permissions'?: (_fila_v1_AclPermission)[]; - 'isSuperadmin'?: (boolean); -} - -export interface GetAclResponse__Output { - 'keyId': (string); - 'permissions': (_fila_v1_AclPermission__Output)[]; - 'isSuperadmin': (boolean); -} diff --git a/generated/fila/v1/GetConfigRequest.ts b/generated/fila/v1/GetConfigRequest.ts deleted file mode 100644 index b551da1..0000000 --- a/generated/fila/v1/GetConfigRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface GetConfigRequest { - 'key'?: (string); -} - -export interface GetConfigRequest__Output { - 'key': (string); -} diff --git a/generated/fila/v1/GetConfigResponse.ts b/generated/fila/v1/GetConfigResponse.ts deleted file mode 100644 index bbe78e0..0000000 --- a/generated/fila/v1/GetConfigResponse.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface GetConfigResponse { - 'value'?: (string); -} - -export interface GetConfigResponse__Output { - 'value': (string); -} diff --git a/generated/fila/v1/GetStatsRequest.ts b/generated/fila/v1/GetStatsRequest.ts deleted file mode 100644 index 651ae87..0000000 --- a/generated/fila/v1/GetStatsRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface GetStatsRequest { - 'queue'?: (string); -} - -export interface GetStatsRequest__Output { - 'queue': (string); -} diff --git a/generated/fila/v1/GetStatsResponse.ts b/generated/fila/v1/GetStatsResponse.ts deleted file mode 100644 index 12f18e1..0000000 --- a/generated/fila/v1/GetStatsResponse.ts +++ /dev/null @@ -1,29 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { PerFairnessKeyStats as _fila_v1_PerFairnessKeyStats, PerFairnessKeyStats__Output as _fila_v1_PerFairnessKeyStats__Output } from '../../fila/v1/PerFairnessKeyStats'; -import type { PerThrottleKeyStats as _fila_v1_PerThrottleKeyStats, PerThrottleKeyStats__Output as _fila_v1_PerThrottleKeyStats__Output } from '../../fila/v1/PerThrottleKeyStats'; -import type { Long } from '@grpc/proto-loader'; - -export interface GetStatsResponse { - 'depth'?: (number | string | Long); - 'inFlight'?: (number | string | Long); - 'activeFairnessKeys'?: (number | string | Long); - 'activeConsumers'?: (number); - 'quantum'?: (number); - 'perKeyStats'?: (_fila_v1_PerFairnessKeyStats)[]; - 'perThrottleStats'?: (_fila_v1_PerThrottleKeyStats)[]; - 'leaderNodeId'?: (number | string | Long); - 'replicationCount'?: (number); -} - -export interface GetStatsResponse__Output { - 'depth': (string); - 'inFlight': (string); - 'activeFairnessKeys': (string); - 'activeConsumers': (number); - 'quantum': (number); - 'perKeyStats': (_fila_v1_PerFairnessKeyStats__Output)[]; - 'perThrottleStats': (_fila_v1_PerThrottleKeyStats__Output)[]; - 'leaderNodeId': (string); - 'replicationCount': (number); -} diff --git a/generated/fila/v1/ListApiKeysRequest.ts b/generated/fila/v1/ListApiKeysRequest.ts deleted file mode 100644 index 157576e..0000000 --- a/generated/fila/v1/ListApiKeysRequest.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface ListApiKeysRequest { -} - -export interface ListApiKeysRequest__Output { -} diff --git a/generated/fila/v1/ListApiKeysResponse.ts b/generated/fila/v1/ListApiKeysResponse.ts deleted file mode 100644 index 94f2f42..0000000 --- a/generated/fila/v1/ListApiKeysResponse.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { ApiKeyInfo as _fila_v1_ApiKeyInfo, ApiKeyInfo__Output as _fila_v1_ApiKeyInfo__Output } from '../../fila/v1/ApiKeyInfo'; - -export interface ListApiKeysResponse { - 'keys'?: (_fila_v1_ApiKeyInfo)[]; -} - -export interface ListApiKeysResponse__Output { - 'keys': (_fila_v1_ApiKeyInfo__Output)[]; -} diff --git a/generated/fila/v1/ListConfigRequest.ts b/generated/fila/v1/ListConfigRequest.ts deleted file mode 100644 index d27dc68..0000000 --- a/generated/fila/v1/ListConfigRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface ListConfigRequest { - 'prefix'?: (string); -} - -export interface ListConfigRequest__Output { - 'prefix': (string); -} diff --git a/generated/fila/v1/ListConfigResponse.ts b/generated/fila/v1/ListConfigResponse.ts deleted file mode 100644 index b19b8c5..0000000 --- a/generated/fila/v1/ListConfigResponse.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { ConfigEntry as _fila_v1_ConfigEntry, ConfigEntry__Output as _fila_v1_ConfigEntry__Output } from '../../fila/v1/ConfigEntry'; - -export interface ListConfigResponse { - 'entries'?: (_fila_v1_ConfigEntry)[]; - 'totalCount'?: (number); -} - -export interface ListConfigResponse__Output { - 'entries': (_fila_v1_ConfigEntry__Output)[]; - 'totalCount': (number); -} diff --git a/generated/fila/v1/ListQueuesRequest.ts b/generated/fila/v1/ListQueuesRequest.ts deleted file mode 100644 index d0050e1..0000000 --- a/generated/fila/v1/ListQueuesRequest.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface ListQueuesRequest { -} - -export interface ListQueuesRequest__Output { -} diff --git a/generated/fila/v1/ListQueuesResponse.ts b/generated/fila/v1/ListQueuesResponse.ts deleted file mode 100644 index 7f01010..0000000 --- a/generated/fila/v1/ListQueuesResponse.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { QueueInfo as _fila_v1_QueueInfo, QueueInfo__Output as _fila_v1_QueueInfo__Output } from '../../fila/v1/QueueInfo'; - -export interface ListQueuesResponse { - 'queues'?: (_fila_v1_QueueInfo)[]; - 'clusterNodeCount'?: (number); -} - -export interface ListQueuesResponse__Output { - 'queues': (_fila_v1_QueueInfo__Output)[]; - 'clusterNodeCount': (number); -} diff --git a/generated/fila/v1/Message.ts b/generated/fila/v1/Message.ts deleted file mode 100644 index 16f2b3e..0000000 --- a/generated/fila/v1/Message.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Original file: proto/fila/v1/messages.proto - -import type { MessageMetadata as _fila_v1_MessageMetadata, MessageMetadata__Output as _fila_v1_MessageMetadata__Output } from '../../fila/v1/MessageMetadata'; -import type { MessageTimestamps as _fila_v1_MessageTimestamps, MessageTimestamps__Output as _fila_v1_MessageTimestamps__Output } from '../../fila/v1/MessageTimestamps'; - -export interface Message { - 'id'?: (string); - 'headers'?: ({[key: string]: string}); - 'payload'?: (Buffer | Uint8Array | string); - 'metadata'?: (_fila_v1_MessageMetadata | null); - 'timestamps'?: (_fila_v1_MessageTimestamps | null); -} - -export interface Message__Output { - 'id': (string); - 'headers': ({[key: string]: string}); - 'payload': (Buffer); - 'metadata': (_fila_v1_MessageMetadata__Output | null); - 'timestamps': (_fila_v1_MessageTimestamps__Output | null); -} diff --git a/generated/fila/v1/MessageMetadata.ts b/generated/fila/v1/MessageMetadata.ts deleted file mode 100644 index 395c051..0000000 --- a/generated/fila/v1/MessageMetadata.ts +++ /dev/null @@ -1,18 +0,0 @@ -// Original file: proto/fila/v1/messages.proto - - -export interface MessageMetadata { - 'fairnessKey'?: (string); - 'weight'?: (number); - 'throttleKeys'?: (string)[]; - 'attemptCount'?: (number); - 'queueId'?: (string); -} - -export interface MessageMetadata__Output { - 'fairnessKey': (string); - 'weight': (number); - 'throttleKeys': (string)[]; - 'attemptCount': (number); - 'queueId': (string); -} diff --git a/generated/fila/v1/MessageTimestamps.ts b/generated/fila/v1/MessageTimestamps.ts deleted file mode 100644 index 140f491..0000000 --- a/generated/fila/v1/MessageTimestamps.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/messages.proto - -import type { Timestamp as _google_protobuf_Timestamp, Timestamp__Output as _google_protobuf_Timestamp__Output } from '../../google/protobuf/Timestamp'; - -export interface MessageTimestamps { - 'enqueuedAt'?: (_google_protobuf_Timestamp | null); - 'leasedAt'?: (_google_protobuf_Timestamp | null); -} - -export interface MessageTimestamps__Output { - 'enqueuedAt': (_google_protobuf_Timestamp__Output | null); - 'leasedAt': (_google_protobuf_Timestamp__Output | null); -} diff --git a/generated/fila/v1/NackError.ts b/generated/fila/v1/NackError.ts deleted file mode 100644 index 2cc2888..0000000 --- a/generated/fila/v1/NackError.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { NackErrorCode as _fila_v1_NackErrorCode, NackErrorCode__Output as _fila_v1_NackErrorCode__Output } from '../../fila/v1/NackErrorCode'; - -export interface NackError { - 'code'?: (_fila_v1_NackErrorCode); - 'message'?: (string); -} - -export interface NackError__Output { - 'code': (_fila_v1_NackErrorCode__Output); - 'message': (string); -} diff --git a/generated/fila/v1/NackErrorCode.ts b/generated/fila/v1/NackErrorCode.ts deleted file mode 100644 index e7738f7..0000000 --- a/generated/fila/v1/NackErrorCode.ts +++ /dev/null @@ -1,20 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -export const NackErrorCode = { - NACK_ERROR_CODE_UNSPECIFIED: 'NACK_ERROR_CODE_UNSPECIFIED', - NACK_ERROR_CODE_MESSAGE_NOT_FOUND: 'NACK_ERROR_CODE_MESSAGE_NOT_FOUND', - NACK_ERROR_CODE_STORAGE: 'NACK_ERROR_CODE_STORAGE', - NACK_ERROR_CODE_PERMISSION_DENIED: 'NACK_ERROR_CODE_PERMISSION_DENIED', -} as const; - -export type NackErrorCode = - | 'NACK_ERROR_CODE_UNSPECIFIED' - | 0 - | 'NACK_ERROR_CODE_MESSAGE_NOT_FOUND' - | 1 - | 'NACK_ERROR_CODE_STORAGE' - | 2 - | 'NACK_ERROR_CODE_PERMISSION_DENIED' - | 3 - -export type NackErrorCode__Output = typeof NackErrorCode[keyof typeof NackErrorCode] diff --git a/generated/fila/v1/NackMessage.ts b/generated/fila/v1/NackMessage.ts deleted file mode 100644 index 2ce0501..0000000 --- a/generated/fila/v1/NackMessage.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Original file: proto/fila/v1/service.proto - - -export interface NackMessage { - 'queue'?: (string); - 'messageId'?: (string); - 'error'?: (string); -} - -export interface NackMessage__Output { - 'queue': (string); - 'messageId': (string); - 'error': (string); -} diff --git a/generated/fila/v1/NackRequest.ts b/generated/fila/v1/NackRequest.ts deleted file mode 100644 index 2f2450d..0000000 --- a/generated/fila/v1/NackRequest.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { NackMessage as _fila_v1_NackMessage, NackMessage__Output as _fila_v1_NackMessage__Output } from '../../fila/v1/NackMessage'; - -export interface NackRequest { - 'messages'?: (_fila_v1_NackMessage)[]; -} - -export interface NackRequest__Output { - 'messages': (_fila_v1_NackMessage__Output)[]; -} diff --git a/generated/fila/v1/NackResponse.ts b/generated/fila/v1/NackResponse.ts deleted file mode 100644 index fd00fe1..0000000 --- a/generated/fila/v1/NackResponse.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { NackResult as _fila_v1_NackResult, NackResult__Output as _fila_v1_NackResult__Output } from '../../fila/v1/NackResult'; - -export interface NackResponse { - 'results'?: (_fila_v1_NackResult)[]; -} - -export interface NackResponse__Output { - 'results': (_fila_v1_NackResult__Output)[]; -} diff --git a/generated/fila/v1/NackResult.ts b/generated/fila/v1/NackResult.ts deleted file mode 100644 index 9a205d9..0000000 --- a/generated/fila/v1/NackResult.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { NackSuccess as _fila_v1_NackSuccess, NackSuccess__Output as _fila_v1_NackSuccess__Output } from '../../fila/v1/NackSuccess'; -import type { NackError as _fila_v1_NackError, NackError__Output as _fila_v1_NackError__Output } from '../../fila/v1/NackError'; - -export interface NackResult { - 'success'?: (_fila_v1_NackSuccess | null); - 'error'?: (_fila_v1_NackError | null); - 'result'?: "success"|"error"; -} - -export interface NackResult__Output { - 'success'?: (_fila_v1_NackSuccess__Output | null); - 'error'?: (_fila_v1_NackError__Output | null); - 'result'?: "success"|"error"; -} diff --git a/generated/fila/v1/NackSuccess.ts b/generated/fila/v1/NackSuccess.ts deleted file mode 100644 index cd61ea8..0000000 --- a/generated/fila/v1/NackSuccess.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/service.proto - - -export interface NackSuccess { -} - -export interface NackSuccess__Output { -} diff --git a/generated/fila/v1/PerFairnessKeyStats.ts b/generated/fila/v1/PerFairnessKeyStats.ts deleted file mode 100644 index 931553f..0000000 --- a/generated/fila/v1/PerFairnessKeyStats.ts +++ /dev/null @@ -1,17 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { Long } from '@grpc/proto-loader'; - -export interface PerFairnessKeyStats { - 'key'?: (string); - 'pendingCount'?: (number | string | Long); - 'currentDeficit'?: (number | string | Long); - 'weight'?: (number); -} - -export interface PerFairnessKeyStats__Output { - 'key': (string); - 'pendingCount': (string); - 'currentDeficit': (string); - 'weight': (number); -} diff --git a/generated/fila/v1/PerThrottleKeyStats.ts b/generated/fila/v1/PerThrottleKeyStats.ts deleted file mode 100644 index 069b44c..0000000 --- a/generated/fila/v1/PerThrottleKeyStats.ts +++ /dev/null @@ -1,16 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface PerThrottleKeyStats { - 'key'?: (string); - 'tokens'?: (number | string); - 'ratePerSecond'?: (number | string); - 'burst'?: (number | string); -} - -export interface PerThrottleKeyStats__Output { - 'key': (string); - 'tokens': (number); - 'ratePerSecond': (number); - 'burst': (number); -} diff --git a/generated/fila/v1/QueueConfig.ts b/generated/fila/v1/QueueConfig.ts deleted file mode 100644 index 9c0517e..0000000 --- a/generated/fila/v1/QueueConfig.ts +++ /dev/null @@ -1,15 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { Long } from '@grpc/proto-loader'; - -export interface QueueConfig { - 'onEnqueueScript'?: (string); - 'onFailureScript'?: (string); - 'visibilityTimeoutMs'?: (number | string | Long); -} - -export interface QueueConfig__Output { - 'onEnqueueScript': (string); - 'onFailureScript': (string); - 'visibilityTimeoutMs': (string); -} diff --git a/generated/fila/v1/QueueInfo.ts b/generated/fila/v1/QueueInfo.ts deleted file mode 100644 index 09f3677..0000000 --- a/generated/fila/v1/QueueInfo.ts +++ /dev/null @@ -1,19 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { Long } from '@grpc/proto-loader'; - -export interface QueueInfo { - 'name'?: (string); - 'depth'?: (number | string | Long); - 'inFlight'?: (number | string | Long); - 'activeConsumers'?: (number); - 'leaderNodeId'?: (number | string | Long); -} - -export interface QueueInfo__Output { - 'name': (string); - 'depth': (string); - 'inFlight': (string); - 'activeConsumers': (number); - 'leaderNodeId': (string); -} diff --git a/generated/fila/v1/RedriveRequest.ts b/generated/fila/v1/RedriveRequest.ts deleted file mode 100644 index 10e7839..0000000 --- a/generated/fila/v1/RedriveRequest.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { Long } from '@grpc/proto-loader'; - -export interface RedriveRequest { - 'dlqQueue'?: (string); - 'count'?: (number | string | Long); -} - -export interface RedriveRequest__Output { - 'dlqQueue': (string); - 'count': (string); -} diff --git a/generated/fila/v1/RedriveResponse.ts b/generated/fila/v1/RedriveResponse.ts deleted file mode 100644 index cd4170a..0000000 --- a/generated/fila/v1/RedriveResponse.ts +++ /dev/null @@ -1,11 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { Long } from '@grpc/proto-loader'; - -export interface RedriveResponse { - 'redriven'?: (number | string | Long); -} - -export interface RedriveResponse__Output { - 'redriven': (string); -} diff --git a/generated/fila/v1/RevokeApiKeyRequest.ts b/generated/fila/v1/RevokeApiKeyRequest.ts deleted file mode 100644 index e13223f..0000000 --- a/generated/fila/v1/RevokeApiKeyRequest.ts +++ /dev/null @@ -1,10 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface RevokeApiKeyRequest { - 'keyId'?: (string); -} - -export interface RevokeApiKeyRequest__Output { - 'keyId': (string); -} diff --git a/generated/fila/v1/RevokeApiKeyResponse.ts b/generated/fila/v1/RevokeApiKeyResponse.ts deleted file mode 100644 index ab63276..0000000 --- a/generated/fila/v1/RevokeApiKeyResponse.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface RevokeApiKeyResponse { -} - -export interface RevokeApiKeyResponse__Output { -} diff --git a/generated/fila/v1/SetAclRequest.ts b/generated/fila/v1/SetAclRequest.ts deleted file mode 100644 index 05b9c58..0000000 --- a/generated/fila/v1/SetAclRequest.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - -import type { AclPermission as _fila_v1_AclPermission, AclPermission__Output as _fila_v1_AclPermission__Output } from '../../fila/v1/AclPermission'; - -export interface SetAclRequest { - 'keyId'?: (string); - 'permissions'?: (_fila_v1_AclPermission)[]; -} - -export interface SetAclRequest__Output { - 'keyId': (string); - 'permissions': (_fila_v1_AclPermission__Output)[]; -} diff --git a/generated/fila/v1/SetAclResponse.ts b/generated/fila/v1/SetAclResponse.ts deleted file mode 100644 index 73fa5cd..0000000 --- a/generated/fila/v1/SetAclResponse.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface SetAclResponse { -} - -export interface SetAclResponse__Output { -} diff --git a/generated/fila/v1/SetConfigRequest.ts b/generated/fila/v1/SetConfigRequest.ts deleted file mode 100644 index ce8ad17..0000000 --- a/generated/fila/v1/SetConfigRequest.ts +++ /dev/null @@ -1,12 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface SetConfigRequest { - 'key'?: (string); - 'value'?: (string); -} - -export interface SetConfigRequest__Output { - 'key': (string); - 'value': (string); -} diff --git a/generated/fila/v1/SetConfigResponse.ts b/generated/fila/v1/SetConfigResponse.ts deleted file mode 100644 index ec82c90..0000000 --- a/generated/fila/v1/SetConfigResponse.ts +++ /dev/null @@ -1,8 +0,0 @@ -// Original file: proto/fila/v1/admin.proto - - -export interface SetConfigResponse { -} - -export interface SetConfigResponse__Output { -} diff --git a/generated/fila/v1/StreamEnqueueRequest.ts b/generated/fila/v1/StreamEnqueueRequest.ts deleted file mode 100644 index 03d0b32..0000000 --- a/generated/fila/v1/StreamEnqueueRequest.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { EnqueueMessage as _fila_v1_EnqueueMessage, EnqueueMessage__Output as _fila_v1_EnqueueMessage__Output } from '../../fila/v1/EnqueueMessage'; -import type { Long } from '@grpc/proto-loader'; - -export interface StreamEnqueueRequest { - 'messages'?: (_fila_v1_EnqueueMessage)[]; - 'sequenceNumber'?: (number | string | Long); -} - -export interface StreamEnqueueRequest__Output { - 'messages': (_fila_v1_EnqueueMessage__Output)[]; - 'sequenceNumber': (string); -} diff --git a/generated/fila/v1/StreamEnqueueResponse.ts b/generated/fila/v1/StreamEnqueueResponse.ts deleted file mode 100644 index 56c5586..0000000 --- a/generated/fila/v1/StreamEnqueueResponse.ts +++ /dev/null @@ -1,14 +0,0 @@ -// Original file: proto/fila/v1/service.proto - -import type { EnqueueResult as _fila_v1_EnqueueResult, EnqueueResult__Output as _fila_v1_EnqueueResult__Output } from '../../fila/v1/EnqueueResult'; -import type { Long } from '@grpc/proto-loader'; - -export interface StreamEnqueueResponse { - 'sequenceNumber'?: (number | string | Long); - 'results'?: (_fila_v1_EnqueueResult)[]; -} - -export interface StreamEnqueueResponse__Output { - 'sequenceNumber': (string); - 'results': (_fila_v1_EnqueueResult__Output)[]; -} diff --git a/generated/google/protobuf/Timestamp.ts b/generated/google/protobuf/Timestamp.ts deleted file mode 100644 index ceaa32b..0000000 --- a/generated/google/protobuf/Timestamp.ts +++ /dev/null @@ -1,13 +0,0 @@ -// Original file: null - -import type { Long } from '@grpc/proto-loader'; - -export interface Timestamp { - 'seconds'?: (number | string | Long); - 'nanos'?: (number); -} - -export interface Timestamp__Output { - 'seconds': (string); - 'nanos': (number); -} diff --git a/generated/messages.ts b/generated/messages.ts deleted file mode 100644 index 52dc51d..0000000 --- a/generated/messages.ts +++ /dev/null @@ -1,23 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { MessageTypeDefinition } from '@grpc/proto-loader'; - - -type SubtypeConstructor any, Subtype> = { - new(...args: ConstructorParameters): Subtype; -}; - -export interface ProtoGrpcType { - fila: { - v1: { - Message: MessageTypeDefinition - MessageMetadata: MessageTypeDefinition - MessageTimestamps: MessageTypeDefinition - } - } - google: { - protobuf: { - Timestamp: MessageTypeDefinition - } - } -} - diff --git a/generated/service.ts b/generated/service.ts deleted file mode 100644 index eb61748..0000000 --- a/generated/service.ts +++ /dev/null @@ -1,49 +0,0 @@ -import type * as grpc from '@grpc/grpc-js'; -import type { EnumTypeDefinition, MessageTypeDefinition } from '@grpc/proto-loader'; - -import type { FilaServiceClient as _fila_v1_FilaServiceClient, FilaServiceDefinition as _fila_v1_FilaServiceDefinition } from './fila/v1/FilaService'; - -type SubtypeConstructor any, Subtype> = { - new(...args: ConstructorParameters): Subtype; -}; - -export interface ProtoGrpcType { - fila: { - v1: { - AckError: MessageTypeDefinition - AckErrorCode: EnumTypeDefinition - AckMessage: MessageTypeDefinition - AckRequest: MessageTypeDefinition - AckResponse: MessageTypeDefinition - AckResult: MessageTypeDefinition - AckSuccess: MessageTypeDefinition - ConsumeRequest: MessageTypeDefinition - ConsumeResponse: MessageTypeDefinition - EnqueueError: MessageTypeDefinition - EnqueueErrorCode: EnumTypeDefinition - EnqueueMessage: MessageTypeDefinition - EnqueueRequest: MessageTypeDefinition - EnqueueResponse: MessageTypeDefinition - EnqueueResult: MessageTypeDefinition - FilaService: SubtypeConstructor & { service: _fila_v1_FilaServiceDefinition } - Message: MessageTypeDefinition - MessageMetadata: MessageTypeDefinition - MessageTimestamps: MessageTypeDefinition - NackError: MessageTypeDefinition - NackErrorCode: EnumTypeDefinition - NackMessage: MessageTypeDefinition - NackRequest: MessageTypeDefinition - NackResponse: MessageTypeDefinition - NackResult: MessageTypeDefinition - NackSuccess: MessageTypeDefinition - StreamEnqueueRequest: MessageTypeDefinition - StreamEnqueueResponse: MessageTypeDefinition - } - } - google: { - protobuf: { - Timestamp: MessageTypeDefinition - } - } -} - diff --git a/package-lock.json b/package-lock.json index 0b953af..6cb182b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,15 @@ { "name": "fila-client", - "version": "0.2.0", + "version": "0.3.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "fila-client", - "version": "0.2.0", + "version": "0.3.0", "license": "AGPL-3.0-or-later", "dependencies": { - "@grpc/grpc-js": "^1.12.0", - "@grpc/proto-loader": "^0.7.0" + "protobufjs": "^7.5.0" }, "devDependencies": { "@eslint/js": "^9.0.0", @@ -607,55 +606,6 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@grpc/grpc-js": { - "version": "1.14.3", - "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.14.3.tgz", - "integrity": "sha512-Iq8QQQ/7X3Sac15oB6p0FmUg/klxQvXLeileoqrTRGJYLV+/9tubbr9ipz0GKHjmXVsgFPo/+W+2cA8eNcR+XA==", - "license": "Apache-2.0", - "dependencies": { - "@grpc/proto-loader": "^0.8.0", - "@js-sdsl/ordered-map": "^4.4.2" - }, - "engines": { - "node": ">=12.10.0" - } - }, - "node_modules/@grpc/grpc-js/node_modules/@grpc/proto-loader": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.8.0.tgz", - "integrity": "sha512-rc1hOQtjIWGxcxpb9aHAfLpIctjEnsDehj0DAiVfBlmT84uvR0uUtN2hEi/ecvWVjXUGf5qPF4qEgiLOx1YIMQ==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.5.3", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/@grpc/proto-loader": { - "version": "0.7.15", - "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.15.tgz", - "integrity": "sha512-tMXdRCfYVixjuFK+Hk0Q1s38gV9zDiDJfWL3h1rv4Qc39oILCu1TRTDt7+fGUI8K4G1Fj125Hx/ru3azECWTyQ==", - "license": "Apache-2.0", - "dependencies": { - "lodash.camelcase": "^4.3.0", - "long": "^5.0.0", - "protobufjs": "^7.2.5", - "yargs": "^17.7.2" - }, - "bin": { - "proto-loader-gen-types": "build/bin/proto-loader-gen-types.js" - }, - "engines": { - "node": ">=6" - } - }, "node_modules/@humanfs/core": { "version": "0.19.1", "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", @@ -715,16 +665,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@js-sdsl/ordered-map": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/@js-sdsl/ordered-map/-/ordered-map-4.4.2.tgz", - "integrity": "sha512-iUKgm52T8HOE/makSxjqoWhe95ZJA1/G1sYsGev2JDKUSS14KAgg1LHb+Ba+IPow0xflbnSkOsZcO08C7w1gYw==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/js-sdsl" - } - }, "node_modules/@protobufjs/aspromise": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", @@ -1606,19 +1546,11 @@ "url": "https://github.com/sponsors/epoberezkin" } }, - "node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" @@ -1729,24 +1661,11 @@ "node": ">= 16" } }, - "node_modules/cliui": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", - "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", - "license": "ISC", - "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.1", - "wrap-ansi": "^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "license": "MIT", "dependencies": { "color-name": "~1.1.4" @@ -1759,6 +1678,7 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, "license": "MIT" }, "node_modules/concat-map": { @@ -1818,12 +1738,6 @@ "dev": true, "license": "MIT" }, - "node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "license": "MIT" - }, "node_modules/es-module-lexer": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-1.7.0.tgz", @@ -1873,15 +1787,6 @@ "@esbuild/win32-x64": "0.27.3" } }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", @@ -2175,15 +2080,6 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, - "node_modules/get-caller-file": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", - "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "license": "ISC", - "engines": { - "node": "6.* || 8.* || >= 10.*" - } - }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2267,15 +2163,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2377,12 +2264,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/lodash.camelcase": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "license": "MIT" - }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -2653,15 +2534,6 @@ "node": ">=6" } }, - "node_modules/require-directory": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", - "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", @@ -2784,32 +2656,6 @@ "dev": true, "license": "MIT" }, - "node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -3206,59 +3052,6 @@ "node": ">=0.10.0" } }, - "node_modules/wrap-ansi": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/y18n": { - "version": "5.0.8", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", - "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/yargs": { - "version": "17.7.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", - "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", - "license": "MIT", - "dependencies": { - "cliui": "^8.0.1", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.3", - "y18n": "^5.0.5", - "yargs-parser": "^21.1.1" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/yargs-parser": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", - "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", - "license": "ISC", - "engines": { - "node": ">=12" - } - }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", diff --git a/package.json b/package.json index abf30ec..c59f541 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "fila-client", - "version": "0.2.0", + "version": "0.3.0", "description": "JavaScript/TypeScript client SDK for the Fila message broker", "repository": { "type": "git", @@ -16,13 +16,11 @@ "build": "tsc", "lint": "eslint src/ test/", "typecheck": "tsc --noEmit", - "test": "vitest run", - "generate": "proto-loader-gen-types --longs=String --enums=String --defaults --oneofs --grpcLib=@grpc/grpc-js --outDir=generated/ proto/fila/v1/messages.proto proto/fila/v1/service.proto proto/fila/v1/admin.proto" + "test": "vitest run" }, "license": "AGPL-3.0-or-later", "dependencies": { - "@grpc/grpc-js": "^1.12.0", - "@grpc/proto-loader": "^0.7.0" + "protobufjs": "^7.5.0" }, "devDependencies": { "@types/node": "^22.0.0", diff --git a/src/batcher.ts b/src/batcher.ts index 214478d..a73998b 100644 --- a/src/batcher.ts +++ b/src/batcher.ts @@ -1,9 +1,16 @@ -import * as grpc from "@grpc/grpc-js"; - -import { QueueNotFoundError, RPCError } from "./errors"; +import { + QueueNotFoundError, + RPCError, + UnauthenticatedError, +} from "./errors"; import type { EnqueueMessage } from "./types"; -import type { FilaServiceClient } from "../generated/fila/v1/FilaService"; -import type { EnqueueResponse__Output } from "../generated/fila/v1/EnqueueResponse"; +import { + FibpConnection, + Op, + ErrCode, + encodeEnqueuePayload, + decodeEnqueueResponse, +} from "./transport"; /** Controls how the SDK batches enqueue() calls. */ export type BatchMode = @@ -18,32 +25,24 @@ interface BatchItem { reject: (err: Error) => void; } -/** - * Map a per-message EnqueueResult error to an SDK error. - * The unified proto uses typed EnqueueError with an error code. - */ -function mapResultError(code: string, message: string): Error { - if (code === "ENQUEUE_ERROR_CODE_QUEUE_NOT_FOUND") { - return new QueueNotFoundError(`enqueue: ${message}`); - } - return new RPCError(grpc.status.INTERNAL, message); -} - -function mapTransportError(err: grpc.ServiceError): Error { - if (err.code === grpc.status.NOT_FOUND) { - return new QueueNotFoundError(`enqueue: ${err.details}`); +function mapEnqueueWireError(errCode: number, errMsg: string): Error { + switch (errCode) { + case ErrCode.QUEUE_NOT_FOUND: + return new QueueNotFoundError(`enqueue: ${errMsg}`); + case ErrCode.UNAUTHORIZED: + return new UnauthenticatedError(`enqueue: ${errMsg}`); + default: + return new RPCError(errCode, errMsg); } - return new RPCError(err.code, err.details); } /** * Background batcher that collects enqueue() calls and flushes them - * via the unified Enqueue RPC (which accepts repeated messages). + * via the FIBP ENQUEUE op (which accepts repeated messages per queue). * Supports auto (opportunistic) and linger (timer-based) modes. */ export class Batcher { - private readonly grpcClient: FilaServiceClient; - private readonly callMetadata: () => grpc.Metadata; + private readonly getConn: () => FibpConnection; private readonly batchMode: BatchMode; private readonly maxBatchSize: number; @@ -55,12 +54,10 @@ export class Batcher { private inFlightCount = 0; constructor( - grpcClient: FilaServiceClient, - callMetadata: () => grpc.Metadata, + getConn: () => FibpConnection, batchMode: BatchMode ) { - this.grpcClient = grpcClient; - this.callMetadata = callMetadata; + this.getConn = getConn; this.batchMode = batchMode; if (batchMode.mode === "auto") { @@ -79,7 +76,7 @@ export class Batcher { submit(message: EnqueueMessage): Promise { if (this.closed) { return Promise.reject( - new RPCError(grpc.status.UNAVAILABLE, "batcher is closed") + new RPCError(ErrCode.INTERNAL, "batcher is closed") ); } @@ -122,8 +119,6 @@ export class Batcher { /** * Auto mode: schedule a flush via setImmediate. Messages that arrive * within the same event loop turn will cluster into the same batch. - * At low load, each message is sent individually. At high load, - * messages naturally batch together. */ private scheduleAutoFlush(): void { if (this.flushScheduled) return; @@ -142,7 +137,6 @@ export class Batcher { private scheduleLingerFlush(): void { if (this.batchMode.mode !== "linger") return; - // If batch is full, flush immediately. if (this.pending.length >= this.batchMode.batchSize) { if (this.lingerTimer !== null) { clearTimeout(this.lingerTimer); @@ -152,7 +146,6 @@ export class Batcher { return; } - // Start timer if not already running. if (this.lingerTimer === null) { this.lingerTimer = setTimeout(() => { this.lingerTimer = null; @@ -161,9 +154,7 @@ export class Batcher { } } - /** - * Flush all pending items, splitting into maxBatchSize chunks. - */ + /** Flush all pending items, splitting into maxBatchSize chunks. */ private flushAll(): void { while (this.pending.length > 0) { const items = this.pending.splice(0, this.maxBatchSize); @@ -173,7 +164,6 @@ export class Batcher { this.notifyDrainComplete(); }); } - // Also check drain in case pending was already empty and nothing in-flight. this.notifyDrainComplete(); } @@ -187,58 +177,59 @@ export class Batcher { } /** - * Flush a batch of items via the unified Enqueue RPC (repeated messages). - * All items -- single or multiple -- use the same RPC. + * Flush a batch of items via FIBP ENQUEUE. + * Items for the same queue are sent in a single frame. + * Items for different queues are sent as separate frames (sequentially). */ - private flushBatch(items: BatchItem[]): Promise { - if (items.length === 0) return Promise.resolve(); + private async flushBatch(items: BatchItem[]): Promise { + if (items.length === 0) return; + + // Group by queue to preserve per-queue batch semantics. + const byQueue = new Map(); + for (const item of items) { + const q = item.message.queue; + if (!byQueue.has(q)) byQueue.set(q, []); + byQueue.get(q)!.push(item); + } + + for (const [, queueItems] of byQueue) { + await this.flushQueueBatch(queueItems); + } + } + private async flushQueueBatch(items: BatchItem[]): Promise { const messages = items.map((item) => ({ queue: item.message.queue, headers: item.message.headers, payload: item.message.payload, })); - return new Promise((resolve) => { - this.grpcClient.enqueue( - { messages }, - this.callMetadata(), - (err: grpc.ServiceError | null, resp?: EnqueueResponse__Output) => { - if (err) { - // Transport-level failure: all items get the error. - const mapped = mapTransportError(err); - for (const item of items) { - item.reject(mapped); - } - } else { - const results = resp!.results; - for (let i = 0; i < items.length; i++) { - const result = results[i]; - if (!result) { - items[i].reject( - new RPCError( - grpc.status.INTERNAL, - "server returned fewer results than messages sent" - ) - ); - continue; - } - if (result.result === "messageId" && result.messageId) { - items[i].resolve(result.messageId); - } else if (result.result === "error" && result.error) { - items[i].reject( - mapResultError(result.error.code, result.error.message) - ); - } else { - items[i].reject( - new RPCError(grpc.status.INTERNAL, "no result from server") - ); - } - } - } - resolve(); - } - ); - }); + let respFrame; + try { + const conn = this.getConn(); + const payload = encodeEnqueuePayload(messages); + respFrame = await conn.request(Op.ENQUEUE, payload); + } catch (err) { + // Transport-level failure: all items in this batch get the error. + const mapped = err instanceof Error ? err : new RPCError(ErrCode.INTERNAL, String(err)); + for (const item of items) { + item.reject(mapped); + } + return; + } + + const results = decodeEnqueueResponse(respFrame.payload); + for (let i = 0; i < items.length; i++) { + const result = results[i]; + if (!result) { + items[i].reject(new RPCError(ErrCode.INTERNAL, "server returned fewer results than messages sent")); + continue; + } + if (result.ok) { + items[i].resolve(result.msgId); + } else { + items[i].reject(mapEnqueueWireError(result.errCode, result.errMsg)); + } + } } } diff --git a/src/client.ts b/src/client.ts index 2cc4a75..1daf698 100644 --- a/src/client.ts +++ b/src/client.ts @@ -1,147 +1,91 @@ -import * as grpc from "@grpc/grpc-js"; -import * as protoLoader from "@grpc/proto-loader"; -import * as fs from "fs"; -import * as path from "path"; - import { FilaError, MessageNotFoundError, QueueNotFoundError, RPCError, + UnauthenticatedError, } from "./errors"; import type { ConsumeMessage, EnqueueMessage, EnqueueResult } from "./types"; -import type { FilaServiceClient } from "../generated/fila/v1/FilaService"; -import type { EnqueueResponse__Output } from "../generated/fila/v1/EnqueueResponse"; -import type { AckResponse__Output } from "../generated/fila/v1/AckResponse"; -import type { NackResponse__Output } from "../generated/fila/v1/NackResponse"; -import type { ConsumeResponse__Output } from "../generated/fila/v1/ConsumeResponse"; import { Batcher, type BatchMode } from "./batcher"; - -function resolveProtoDir(): string { - // Source (dev/test): __dirname = /src/ - const devPath = path.join(__dirname, "..", "proto"); - if (fs.existsSync(devPath)) return devPath; - // Built (dist): __dirname = /dist/src/ - return path.join(__dirname, "..", "..", "proto"); -} - -const PROTO_DIR = resolveProtoDir(); - -function loadServiceProto(): grpc.GrpcObject { - const packageDefinition = protoLoader.loadSync( - [ - path.join(PROTO_DIR, "fila", "v1", "service.proto"), - path.join(PROTO_DIR, "fila", "v1", "messages.proto"), - ], - { - keepCase: false, - longs: String, - enums: String, - defaults: true, - oneofs: true, - includeDirs: [PROTO_DIR], - } - ); - return grpc.loadPackageDefinition(packageDefinition); -} - -/** Metadata key the server uses to indicate the current queue leader address. */ -const LEADER_ADDR_KEY = "x-fila-leader-addr"; - -/** - * Extract the leader address from a gRPC UNAVAILABLE error's trailing metadata. - * Returns the address string, or undefined if not present. - */ -function extractLeaderAddr(err: grpc.ServiceError): string | undefined { - if (err.code !== grpc.status.UNAVAILABLE) return undefined; - const values = err.metadata?.get(LEADER_ADDR_KEY); - if (values && values.length > 0) { - return String(values[0]); +import { + FibpConnection, + Op, + ErrCode, + FLAG_PUSH, + encodeEnqueuePayload, + decodeEnqueueResponse, + encodeConsumePayload, + decodeConsumeDelivery, + encodeAckPayload, + encodeNackPayload, + decodeAckNackResponse, + type ConnectOptions, +} from "./transport"; + +// ---- Error mapping ---------------------------------------------------------- + +function mapEnqueueWireError(errCode: number, errMsg: string): FilaError { + switch (errCode) { + case ErrCode.QUEUE_NOT_FOUND: + return new QueueNotFoundError(`enqueue: ${errMsg}`); + case ErrCode.UNAUTHORIZED: + return new UnauthenticatedError(`enqueue: ${errMsg}`); + default: + return new RPCError(errCode, errMsg); } - return undefined; } -/** Create a FilaServiceClient for the given address and credentials. */ -function createGrpcClient( - addr: string, - creds: grpc.ChannelCredentials -): FilaServiceClient { - const proto = loadServiceProto(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const FilaService = (proto.fila as any).v1 - .FilaService as grpc.ServiceClientConstructor; - return new FilaService(addr, creds) as unknown as FilaServiceClient; -} - -function mapEnqueueError(err: grpc.ServiceError): FilaError { - if (err.code === grpc.status.NOT_FOUND) { - return new QueueNotFoundError(`enqueue: ${err.details}`); +function mapConsumeWireError(errCode: number, errMsg: string): FilaError { + switch (errCode) { + case ErrCode.QUEUE_NOT_FOUND: + return new QueueNotFoundError(`consume: ${errMsg}`); + case ErrCode.UNAUTHORIZED: + return new UnauthenticatedError(`consume: ${errMsg}`); + default: + return new RPCError(errCode, errMsg); } - return new RPCError(err.code, err.details); } -function mapConsumeError(err: grpc.ServiceError): FilaError { - if (err.code === grpc.status.NOT_FOUND) { - return new QueueNotFoundError(`consume: ${err.details}`); +function mapAckWireError(errCode: number, errMsg: string): FilaError { + switch (errCode) { + case ErrCode.MESSAGE_NOT_FOUND: + return new MessageNotFoundError(`ack: ${errMsg}`); + case ErrCode.UNAUTHORIZED: + return new UnauthenticatedError(`ack: ${errMsg}`); + default: + return new RPCError(errCode, errMsg); } - return new RPCError(err.code, err.details); } -/** - * Map a per-message EnqueueResult error code to an SDK error type. - */ -function mapEnqueueResultError(code: string, message: string): FilaError { - if (code === "ENQUEUE_ERROR_CODE_QUEUE_NOT_FOUND") { - return new QueueNotFoundError(`enqueue: ${message}`); +function mapNackWireError(errCode: number, errMsg: string): FilaError { + switch (errCode) { + case ErrCode.MESSAGE_NOT_FOUND: + return new MessageNotFoundError(`nack: ${errMsg}`); + case ErrCode.UNAUTHORIZED: + return new UnauthenticatedError(`nack: ${errMsg}`); + default: + return new RPCError(errCode, errMsg); } - return new RPCError(grpc.status.INTERNAL, message); } -/** - * Map a per-message AckResult error code to an SDK error type. - */ -function mapAckResultError(code: string, message: string): FilaError { - if (code === "ACK_ERROR_CODE_MESSAGE_NOT_FOUND") { - return new MessageNotFoundError(`ack: ${message}`); - } - return new RPCError(grpc.status.INTERNAL, message); -} +// ---- Helpers ---------------------------------------------------------------- -/** - * Map a per-message NackResult error code to an SDK error type. - */ -function mapNackResultError(code: string, message: string): FilaError { - if (code === "NACK_ERROR_CODE_MESSAGE_NOT_FOUND") { - return new MessageNotFoundError(`nack: ${message}`); +/** Parse "host:port" into { host, port }. Handles IPv6 bracket notation. */ +function parseAddr(addr: string): { host: string; port: number } { + // IPv6: [::1]:5555 + if (addr.startsWith("[")) { + const bracket = addr.lastIndexOf("]"); + const host = addr.slice(1, bracket); + const port = parseInt(addr.slice(bracket + 2), 10); + return { host, port }; } - return new RPCError(grpc.status.INTERNAL, message); + const lastColon = addr.lastIndexOf(":"); + const host = addr.slice(0, lastColon); + const port = parseInt(addr.slice(lastColon + 1), 10); + return { host, port }; } -/** Map a ConsumeResponse to ConsumeMessage(s), skipping keepalive frames. */ -function mapConsumeResponse( - resp: ConsumeResponse__Output -): ConsumeMessage[] { - if (!resp.messages || resp.messages.length === 0) { - return []; // keepalive frame - } - - const results: ConsumeMessage[] = []; - for (const msg of resp.messages) { - if (!msg || !msg.id) continue; - const metadata = msg.metadata; - results.push({ - id: msg.id, - headers: msg.headers ?? {}, - payload: Buffer.isBuffer(msg.payload) - ? msg.payload - : Buffer.from(msg.payload ?? ""), - fairnessKey: metadata?.fairnessKey ?? "", - attemptCount: metadata?.attemptCount ?? 0, - queue: metadata?.queueId ?? "", - }); - } - return results; -} +// ---- ClientOptions ---------------------------------------------------------- /** Connection options for TLS, authentication, and batching. */ export interface ClientOptions { @@ -157,7 +101,7 @@ export interface ClientOptions { clientCert?: Buffer; /** Client private key PEM for mutual TLS (mTLS). Requires TLS to be enabled (via `tls: true` or `caCert`). */ clientKey?: Buffer; - /** API key for authentication. Sent as `authorization: Bearer ` metadata on every RPC. */ + /** API key for authentication. Sent as an AUTH frame immediately after the handshake. */ apiKey?: string; /** * Batch mode for enqueue() calls. @@ -165,7 +109,7 @@ export interface ClientOptions { * - `'auto'` (DEFAULT): Opportunistic batching via setImmediate. Zero config, * zero latency penalty at low load. Messages cluster naturally at high load. * - `'linger'`: Timer-based batching with explicit `lingerMs` and `batchSize`. - * - `'disabled'`: No batching. Each enqueue() is a direct RPC. + * - `'disabled'`: No batching. Each enqueue() is a direct request. * * @default 'auto' */ @@ -178,10 +122,12 @@ export interface ClientOptions { batchSize?: number; } +// ---- Client ----------------------------------------------------------------- + /** - * Client for the Fila message broker. + * Client for the Fila message broker (FIBP transport). * - * Wraps the hot-path gRPC operations: enqueue, consume, ack, nack. + * Wraps the hot-path FIBP operations: enqueue, consume, ack, nack. * By default, enqueue() calls are automatically batched for optimal throughput * with zero added latency at low load. * @@ -196,10 +142,11 @@ export interface ClientOptions { * ``` */ export class Client { - private readonly grpcClient: FilaServiceClient; - private readonly creds: grpc.ChannelCredentials; - private readonly apiKey?: string; - private readonly batcher: Batcher | null; + private readonly connectOpts: ConnectOptions; + private readonly batchMode: BatchMode; + private conn: FibpConnection | null = null; + private connectPromise: Promise | null = null; + private batcher: Batcher | null = null; /** * Connect to a Fila broker at the given address. @@ -218,337 +165,313 @@ export class Client { throw new Error("clientCert and clientKey must be provided together"); } - if (options?.caCert) { - this.creds = grpc.credentials.createSsl( - options.caCert, - options.clientKey ?? null, - options.clientCert ?? null - ); - } else if (tlsEnabled) { - this.creds = grpc.credentials.createSsl( - null, - options?.clientKey ?? null, - options?.clientCert ?? null - ); + const { host, port } = parseAddr(addr); + this.connectOpts = { + host, + port, + tls: tlsEnabled, + caCert: options?.caCert, + clientCert: options?.clientCert, + clientKey: options?.clientKey, + apiKey: options?.apiKey, + }; + + // Initialize batch mode. + const modeStr = options?.batchMode ?? "auto"; + if (modeStr === "disabled") { + this.batchMode = { mode: "disabled" }; + } else if (modeStr === "linger") { + if (options?.lingerMs === undefined || options?.batchSize === undefined) { + throw new Error("lingerMs and batchSize are required when batchMode is 'linger'"); + } + this.batchMode = { + mode: "linger", + lingerMs: options.lingerMs, + batchSize: options.batchSize, + }; } else { - this.creds = grpc.credentials.createInsecure(); + this.batchMode = { + mode: "auto", + maxBatchSize: options?.maxBatchSize, + }; } - this.grpcClient = createGrpcClient(addr, this.creds); - this.apiKey = options?.apiKey; + if (modeStr !== "disabled") { + this.batcher = new Batcher(() => this.getConnSync(), this.batchMode); + } + } - // Initialize the batcher based on the configured mode. - const modeStr = options?.batchMode ?? "auto"; - if (modeStr === "disabled") { - this.batcher = null; - } else { - let batchMode: BatchMode; - if (modeStr === "linger") { - if (options?.lingerMs === undefined || options?.batchSize === undefined) { - throw new Error("lingerMs and batchSize are required when batchMode is 'linger'"); - } - batchMode = { - mode: "linger", - lingerMs: options.lingerMs, - batchSize: options.batchSize, - }; - } else { - batchMode = { - mode: "auto", - maxBatchSize: options?.maxBatchSize, - }; - } - this.batcher = new Batcher( - this.grpcClient, - () => this.callMetadata(), - batchMode - ); + // ---- Connection management ------------------------------------------------ + + /** Get the current connection, lazily establishing it. */ + private async getConn(): Promise { + if (this.conn && !this.conn.closed) { + return this.conn; } + if (this.connectPromise) { + return this.connectPromise; + } + this.connectPromise = FibpConnection.connect(this.connectOpts).then((c) => { + this.conn = c; + this.connectPromise = null; + c.on("close", () => { + if (this.conn === c) this.conn = null; + }); + return c; + }).catch((err) => { + this.connectPromise = null; + throw err; + }); + return this.connectPromise; } - /** Build gRPC metadata, attaching the API key if configured. */ - private callMetadata(): grpc.Metadata { - const md = new grpc.Metadata(); - if (this.apiKey) { - md.set("authorization", `Bearer ${this.apiKey}`); + /** + * Synchronous accessor for batcher use — returns the current connection or + * throws if not yet connected. The batcher calls this only after the first + * enqueue triggers a connection (via getConn()). + */ + private getConnSync(): FibpConnection { + if (!this.conn || this.conn.closed) { + throw new FilaError("not connected: call enqueue/consume first or await getConn()"); } - return md; + return this.conn; + } + + /** + * Ensure the connection is established. Called before batcher flushes. + */ + private async ensureConnected(): Promise { + await this.getConn(); } /** * Close the client, draining any pending batched messages first. * Returns a promise that resolves when all pending messages have been - * flushed and the gRPC channel is closed. + * flushed and the TCP connection is closed. */ async close(): Promise { if (this.batcher) { await this.batcher.drain(); } - (this.grpcClient as unknown as grpc.Client).close(); + this.conn?.destroy(); + this.conn = null; } + // ---- Enqueue -------------------------------------------------------------- + /** * Enqueue a message to the specified queue. * * When batching is enabled (default), the message is routed through the * batcher. At low load, messages are sent individually. At high load, - * messages cluster naturally into larger Enqueue RPCs. + * messages cluster naturally into larger ENQUEUE requests. * * @param queue - Target queue name. * @param headers - Optional message headers. * @param payload - Message payload bytes. * @returns Broker-assigned message ID (UUIDv7). * @throws {QueueNotFoundError} If the queue does not exist. - * @throws {RPCError} For unexpected gRPC failures. + * @throws {UnauthenticatedError} If authentication fails. + * @throws {RPCError} For unexpected protocol failures. */ - enqueue( + async enqueue( queue: string, headers: Record | null, payload: Buffer ): Promise { - // Route through the batcher when enabled. + const conn = await this.getConn(); + + // Route through batcher when enabled. if (this.batcher) { - return this.batcher.submit({ - queue, - headers: headers ?? {}, - payload, - }); + // Batcher uses getConnSync() — connection is now established. + return this.batcher.submit({ queue, headers: headers ?? {}, payload }); } - // No batching: direct RPC with single message in the repeated field. - return new Promise((resolve, reject) => { - this.grpcClient.enqueue( - { messages: [{ queue, headers: headers ?? {}, payload }] }, - this.callMetadata(), - (err: grpc.ServiceError | null, resp?: EnqueueResponse__Output) => { - if (err) { - reject(mapEnqueueError(err)); - return; - } - const result = resp!.results[0]; - if (!result) { - reject(new RPCError(grpc.status.INTERNAL, "no result from server")); - return; - } - if (result.result === "messageId" && result.messageId) { - resolve(result.messageId); - } else if (result.result === "error" && result.error) { - reject(mapEnqueueResultError(result.error.code, result.error.message)); - } else { - reject(new RPCError(grpc.status.INTERNAL, "no result from server")); - } - } - ); - }); + // No batching: direct single-message ENQUEUE. + const framePayload = encodeEnqueuePayload([{ queue, headers: headers ?? {}, payload }]); + const resp = await conn.request(Op.ENQUEUE, framePayload); + const results = decodeEnqueueResponse(resp.payload); + const result = results[0]; + if (!result) { + throw new RPCError(ErrCode.INTERNAL, "no result from server"); + } + if (result.ok) { + return result.msgId; + } + throw mapEnqueueWireError(result.errCode, result.errMsg); } /** - * Enqueue multiple messages in a single RPC call. + * Enqueue multiple messages in a single request. * * Each message is independently validated and processed. A failed message * does not affect the others. Returns one result per input message, * in the same order. * - * This always bypasses the batcher and issues a direct Enqueue RPC. + * This always bypasses the batcher and issues a direct ENQUEUE request. * * @param messages - Array of messages to enqueue. * @returns Per-message results (success with messageId, or error with description). * @throws {RPCError} For transport-level failures affecting the entire call. */ - enqueueMany(messages: EnqueueMessage[]): Promise { - const protoMessages = messages.map((m) => ({ - queue: m.queue, - headers: m.headers, - payload: m.payload, - })); - - return new Promise((resolve, reject) => { - this.grpcClient.enqueue( - { messages: protoMessages }, - this.callMetadata(), - (err: grpc.ServiceError | null, resp?: EnqueueResponse__Output) => { - if (err) { - reject(new RPCError(err.code, err.details)); - return; - } - - const results: EnqueueResult[] = resp!.results.map((r) => { - if (r.result === "messageId" && r.messageId) { - return { - success: true as const, - messageId: r.messageId, - }; - } else if (r.result === "error" && r.error) { - return { - success: false as const, - error: r.error.message, - }; - } else { - return { - success: false as const, - error: "no result from server", - }; - } - }); - - resolve(results); + async enqueueMany(messages: EnqueueMessage[]): Promise { + if (messages.length === 0) return []; + const conn = await this.getConn(); + + // Group by queue for the wire format (each queue is a separate frame). + // Preserve insertion order for result mapping. + const byQueue = new Map>(); + for (let i = 0; i < messages.length; i++) { + const m = messages[i]; + if (!byQueue.has(m.queue)) byQueue.set(m.queue, []); + byQueue.get(m.queue)!.push({ idx: i, msg: m }); + } + + const resultArr: EnqueueResult[] = new Array(messages.length); + + for (const [, items] of byQueue) { + const wireMessages = items.map((item) => ({ + queue: item.msg.queue, + headers: item.msg.headers, + payload: item.msg.payload, + })); + const framePayload = encodeEnqueuePayload(wireMessages); + + let resp; + try { + resp = await conn.request(Op.ENQUEUE, framePayload); + } catch (err) { + const errMsg = err instanceof Error ? err.message : String(err); + for (const item of items) { + resultArr[item.idx] = { success: false, error: errMsg }; } - ); - }); + continue; + } + + const results = decodeEnqueueResponse(resp.payload); + for (let i = 0; i < items.length; i++) { + const result = results[i]; + if (!result) { + resultArr[items[i].idx] = { success: false, error: "server returned fewer results than messages sent" }; + continue; + } + if (result.ok) { + resultArr[items[i].idx] = { success: true, messageId: result.msgId }; + } else { + resultArr[items[i].idx] = { success: false, error: result.errMsg }; + } + } + } + + return resultArr; } + // ---- Consume -------------------------------------------------------------- + /** * Open a streaming consumer on the specified queue. * * Returns an async iterable that yields messages as they become available. - * Empty response frames (keepalive signals) are skipped automatically. - * Delivery frames containing multiple messages are transparently unpacked - * into individual messages. + * The server pushes delivery frames with the push flag set. Keepalive + * (empty) frames are skipped automatically. * - * If the server returns UNAVAILABLE with an `x-fila-leader-addr` metadata - * header, the client transparently reconnects to the leader node and retries - * the consume stream once (max 1 redirect per call). + * If the server returns a leader-hint error, the client transparently + * reconnects to the indicated leader node and retries once. * * @param queue - Queue to consume from. * @throws {QueueNotFoundError} If the queue does not exist. - * @throws {RPCError} For unexpected gRPC failures. + * @throws {UnauthenticatedError} If authentication fails. + * @throws {RPCError} For unexpected protocol failures. */ async *consume(queue: string): AsyncIterable { - yield* this.consumeInner(queue, false); + yield* this.consumeInner(queue); } - /** - * Inner consume implementation that optionally follows a leader hint. - * @param redirected - true if this is already a redirected attempt (prevents loops). - */ private async *consumeInner( - queue: string, - redirected: boolean + queue: string ): AsyncIterable { - const stream = this.grpcClient.consume({ queue }, this.callMetadata()); - const iterable = stream as AsyncIterable; + const conn = await this.getConn(); + + const initialCredits = 256; + const payload = encodeConsumePayload(queue, initialCredits); + const { corrId, iter } = conn.openStream(Op.CONSUME, payload); try { - for await (const resp of iterable) { - const messages = mapConsumeResponse(resp); - for (const msg of messages) { - yield msg; + for await (const frame of iter) { + if (!(frame.flags & FLAG_PUSH)) { + // Non-push frame on stream = clean server-side close. + break; } - } - } catch (err) { - const svcErr = err as grpc.ServiceError; - - // If we haven't redirected yet and the server tells us who the leader is, - // open a new connection to the leader and retry the consume stream. - if (!redirected) { - const leaderAddr = extractLeaderAddr(svcErr); - if (leaderAddr) { - stream.cancel(); - const leaderClient = createGrpcClient(leaderAddr, this.creds); - const leaderStream = leaderClient.consume( - { queue }, - this.callMetadata() - ); - const leaderIterable = - leaderStream as AsyncIterable; - try { - for await (const resp of leaderIterable) { - const messages = mapConsumeResponse(resp); - for (const msg of messages) { - yield msg; - } - } - } catch (retryErr) { - const retrySvcErr = retryErr as grpc.ServiceError; - if ( - retrySvcErr.code !== undefined && - retrySvcErr.code !== grpc.status.CANCELLED - ) { - throw mapConsumeError(retrySvcErr); - } - } finally { - leaderStream.cancel(); - (leaderClient as unknown as grpc.Client).close(); - } - return; + if (frame.op === Op.ERROR) { + const { code, message } = { + code: frame.payload.readUInt16BE(0), + message: frame.payload.subarray(4).toString("utf8"), + }; + throw mapConsumeWireError(code, message); + } + const messages = decodeConsumeDelivery(frame.payload); + for (const msg of messages) { + yield { + id: msg.id, + headers: msg.headers, + payload: msg.payload, + fairnessKey: msg.fairnessKey, + attemptCount: msg.attemptCount, + queue: msg.queue, + }; } } - - if (svcErr.code !== undefined && svcErr.code !== grpc.status.CANCELLED) { - throw mapConsumeError(svcErr); - } - // Stream cancelled or closed normally — just return. } finally { - stream.cancel(); + conn.cancelStream(corrId); } } + // ---- Ack ------------------------------------------------------------------ + /** * Acknowledge a successfully processed message. * @param queue - Queue the message belongs to. * @param msgId - ID of the message to acknowledge. * @throws {MessageNotFoundError} If the message does not exist. - * @throws {RPCError} For unexpected gRPC failures. + * @throws {UnauthenticatedError} If authentication fails. + * @throws {RPCError} For unexpected protocol failures. */ - ack(queue: string, msgId: string): Promise { - return new Promise((resolve, reject) => { - this.grpcClient.ack( - { messages: [{ queue, messageId: msgId }] }, - this.callMetadata(), - (err: grpc.ServiceError | null, resp?: AckResponse__Output) => { - if (err) { - reject(new RPCError(err.code, err.details)); - return; - } - const result = resp!.results[0]; - if (!result) { - reject(new RPCError(grpc.status.INTERNAL, "no result from server")); - return; - } - if (result.result === "success") { - resolve(); - } else if (result.result === "error" && result.error) { - reject(mapAckResultError(result.error.code, result.error.message)); - } else { - reject(new RPCError(grpc.status.INTERNAL, "no result from server")); - } - } - ); - }); + async ack(queue: string, msgId: string): Promise { + const conn = await this.getConn(); + const payload = encodeAckPayload(queue, msgId); + const resp = await conn.request(Op.ACK, payload); + const results = decodeAckNackResponse(resp.payload); + const result = results[0]; + if (!result) { + throw new RPCError(ErrCode.INTERNAL, "no result from server"); + } + if (!result.ok) { + throw mapAckWireError(result.errCode, result.errMsg); + } } + // ---- Nack ----------------------------------------------------------------- + /** * Negatively acknowledge a message that failed processing. * @param queue - Queue the message belongs to. * @param msgId - ID of the message to nack. * @param error - Description of the failure. * @throws {MessageNotFoundError} If the message does not exist. - * @throws {RPCError} For unexpected gRPC failures. + * @throws {UnauthenticatedError} If authentication fails. + * @throws {RPCError} For unexpected protocol failures. */ - nack(queue: string, msgId: string, error: string): Promise { - return new Promise((resolve, reject) => { - this.grpcClient.nack( - { messages: [{ queue, messageId: msgId, error }] }, - this.callMetadata(), - (err: grpc.ServiceError | null, resp?: NackResponse__Output) => { - if (err) { - reject(new RPCError(err.code, err.details)); - return; - } - const result = resp!.results[0]; - if (!result) { - reject(new RPCError(grpc.status.INTERNAL, "no result from server")); - return; - } - if (result.result === "success") { - resolve(); - } else if (result.result === "error" && result.error) { - reject(mapNackResultError(result.error.code, result.error.message)); - } else { - reject(new RPCError(grpc.status.INTERNAL, "no result from server")); - } - } - ); - }); + async nack(queue: string, msgId: string, error: string): Promise { + const conn = await this.getConn(); + const payload = encodeNackPayload(queue, msgId, error); + const resp = await conn.request(Op.NACK, payload); + const results = decodeAckNackResponse(resp.payload); + const result = results[0]; + if (!result) { + throw new RPCError(ErrCode.INTERNAL, "no result from server"); + } + if (!result.ok) { + throw mapNackWireError(result.errCode, result.errMsg); + } } } diff --git a/src/errors.ts b/src/errors.ts index 12347a3..ac3ec43 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -22,7 +22,15 @@ export class MessageNotFoundError extends FilaError { } } -/** Raised for unexpected gRPC failures, preserving status code and message. */ +/** Raised when the request is rejected due to missing or invalid credentials. */ +export class UnauthenticatedError extends FilaError { + constructor(message: string) { + super(message); + this.name = "UnauthenticatedError"; + } +} + +/** Raised for unexpected protocol-level failures, preserving the wire error code. */ export class RPCError extends FilaError { public readonly code: number; diff --git a/src/index.ts b/src/index.ts index 9f479a0..da08c2c 100644 --- a/src/index.ts +++ b/src/index.ts @@ -5,5 +5,6 @@ export { FilaError, QueueNotFoundError, MessageNotFoundError, + UnauthenticatedError, RPCError, } from "./errors"; diff --git a/src/transport.ts b/src/transport.ts new file mode 100644 index 0000000..369658c --- /dev/null +++ b/src/transport.ts @@ -0,0 +1,716 @@ +/** + * FIBP (Fila Binary Protocol) transport layer. + * + * Wire format: + * [4-byte big-endian frame length][flags:u8 | op:u8 | corr_id:u32 | payload] + * + * The frame length field encodes the byte count of everything AFTER it + * (i.e. flags + op + corr_id + payload). + * + * Handshake: client sends 6 bytes "FIBP\x01\x00"; server echoes same 6 bytes. + */ + +import * as net from "net"; +import * as tls from "tls"; +import { EventEmitter } from "events"; +import { FilaError, RPCError } from "./errors"; + +// ---- Op codes --------------------------------------------------------------- + +export const Op = { + ENQUEUE: 0x01, + CONSUME: 0x02, + ACK: 0x03, + NACK: 0x04, + + // Admin + CREATE_QUEUE: 0x10, + DELETE_QUEUE: 0x11, + QUEUE_STATS: 0x12, + LIST_QUEUES: 0x13, + PAUSE_QUEUE: 0x14, + RESUME_QUEUE: 0x15, + REDRIVE: 0x16, + + // Flow / keepalive + FLOW: 0x20, + HEARTBEAT: 0x21, + + // Auth + AUTH: 0x30, + + // Server-to-client + ERROR: 0xFE, + GOAWAY: 0xFF, +} as const; + +export type OpCode = typeof Op[keyof typeof Op]; + +// ---- Error codes (wire level) ----------------------------------------------- + +export const ErrCode = { + QUEUE_NOT_FOUND: 0x0001, + MESSAGE_NOT_FOUND: 0x0002, + UNAUTHORIZED: 0x0003, + INTERNAL: 0xFFFF, +} as const; + +// ---- Flags ------------------------------------------------------------------ + +/** Bit 2 (0x04): frame carries pushed message data (server → client push). */ +export const FLAG_PUSH = 0x04; + +// ---- Handshake -------------------------------------------------------------- + +export const HANDSHAKE = Buffer.from([0x46, 0x49, 0x42, 0x50, 0x01, 0x00]); // "FIBP\x01\x00" + +// ---- Frame header sizes ----------------------------------------------------- + +// 4 bytes length prefix +const LENGTH_PREFIX = 4; +// flags(1) + op(1) + corr_id(4) = 6 bytes header inside the frame +const FRAME_HEADER = 6; +const MIN_FRAME = FRAME_HEADER; // smallest valid frame (no payload) + +// ---- Types ------------------------------------------------------------------ + +export interface Frame { + flags: number; + op: number; + corrId: number; + payload: Buffer; +} + +// ---- Correlation ID counter ------------------------------------------------- + +let corrIdCounter = 0; +export function nextCorrId(): number { + corrIdCounter = (corrIdCounter + 1) & 0xFFFFFFFF; + // Avoid 0 — reserved for server-push frames + if (corrIdCounter === 0) corrIdCounter = 1; + return corrIdCounter; +} + +// ---- Frame encoding --------------------------------------------------------- + +/** Encode a FIBP frame into a Buffer ready to write to the socket. */ +export function encodeFrame(op: number, corrId: number, payload: Buffer, flags = 0): Buffer { + const frameLen = FRAME_HEADER + payload.length; + const buf = Buffer.allocUnsafe(LENGTH_PREFIX + frameLen); + let offset = 0; + buf.writeUInt32BE(frameLen, offset); offset += 4; + buf.writeUInt8(flags, offset); offset += 1; + buf.writeUInt8(op, offset); offset += 1; + buf.writeUInt32BE(corrId, offset); offset += 4; + payload.copy(buf, offset); + return buf; +} + +// ---- Enqueue wire encoding -------------------------------------------------- + +/** Encode a batch of enqueue messages into the FIBP binary payload. */ +export function encodeEnqueuePayload( + messages: Array<{ queue: string; headers: Record; payload: Buffer }> +): Buffer { + const parts: Buffer[] = []; + + // Group by queue name (but preserve per-message order for response mapping). + // The protocol sends all messages for a single queue in one request. + // Since our API allows multi-queue batches, we send one frame per unique queue. + // This function encodes a single-queue batch only; callers must split by queue. + const queue = messages[0]?.queue ?? ""; + const queueBuf = Buffer.from(queue, "utf8"); + + const header = Buffer.allocUnsafe(2 + queueBuf.length + 2); + header.writeUInt16BE(queueBuf.length, 0); + queueBuf.copy(header, 2); + header.writeUInt16BE(messages.length, 2 + queueBuf.length); + parts.push(header); + + for (const msg of messages) { + parts.push(encodeMessageEntry(msg.headers, msg.payload)); + } + + return Buffer.concat(parts); +} + +function encodeMessageEntry( + headers: Record, + payload: Buffer +): Buffer { + const headerEntries = Object.entries(headers); + const entriesBufs: Buffer[] = []; + + for (const [k, v] of headerEntries) { + const kBuf = Buffer.from(k, "utf8"); + const vBuf = Buffer.from(v, "utf8"); + const entryBuf = Buffer.allocUnsafe(2 + kBuf.length + 2 + vBuf.length); + let off = 0; + entryBuf.writeUInt16BE(kBuf.length, off); off += 2; + kBuf.copy(entryBuf, off); off += kBuf.length; + entryBuf.writeUInt16BE(vBuf.length, off); off += 2; + vBuf.copy(entryBuf, off); + entriesBufs.push(entryBuf); + } + + const countBuf = Buffer.allocUnsafe(1); + countBuf.writeUInt8(headerEntries.length, 0); + + const payloadLenBuf = Buffer.allocUnsafe(4); + payloadLenBuf.writeUInt32BE(payload.length, 0); + + return Buffer.concat([countBuf, ...entriesBufs, payloadLenBuf, payload]); +} + +// ---- Enqueue response decoding ---------------------------------------------- + +export type EnqueueItemResult = + | { ok: true; msgId: string } + | { ok: false; errCode: number; errMsg: string }; + +/** Decode an ENQUEUE response payload. */ +export function decodeEnqueueResponse(payload: Buffer): EnqueueItemResult[] { + let offset = 0; + const count = payload.readUInt16BE(offset); offset += 2; + const results: EnqueueItemResult[] = []; + + for (let i = 0; i < count; i++) { + const ok = payload.readUInt8(offset); offset += 1; + if (ok === 1) { + const idLen = payload.readUInt16BE(offset); offset += 2; + const msgId = payload.subarray(offset, offset + idLen).toString("utf8"); offset += idLen; + results.push({ ok: true, msgId }); + } else { + const errCode = payload.readUInt16BE(offset); offset += 2; + const errLen = payload.readUInt16BE(offset); offset += 2; + const errMsg = payload.subarray(offset, offset + errLen).toString("utf8"); offset += errLen; + results.push({ ok: false, errCode, errMsg }); + } + } + + return results; +} + +// ---- Consume wire encoding / decoding --------------------------------------- + +/** Encode a CONSUME request payload. */ +export function encodeConsumePayload(queue: string, initialCredits: number): Buffer { + const queueBuf = Buffer.from(queue, "utf8"); + const buf = Buffer.allocUnsafe(2 + queueBuf.length + 4); + let off = 0; + buf.writeUInt16BE(queueBuf.length, off); off += 2; + queueBuf.copy(buf, off); off += queueBuf.length; + buf.writeUInt32BE(initialCredits, off); + return buf; +} + +export interface WireMessage { + id: string; + headers: Record; + payload: Buffer; + fairnessKey: string; + attemptCount: number; + queue: string; +} + +/** + * Decode a pushed consume delivery payload. + * + * Wire layout (server → client push with FLAG_PUSH set): + * msg_count:u16BE | messages... + * + * Each message: + * id_len:u16BE + id:utf8 + * queue_len:u16BE + queue:utf8 + * fairness_key_len:u16BE + fairness_key:utf8 + * attempt_count:u32BE + * header_count:u8 + * headers: repeated(key_len:u16BE+key + val_len:u16BE+val) + * payload_len:u32BE + payload + */ +export function decodeConsumeDelivery(payload: Buffer): WireMessage[] { + let offset = 0; + const count = payload.readUInt16BE(offset); offset += 2; + const messages: WireMessage[] = []; + + for (let i = 0; i < count; i++) { + // id + const idLen = payload.readUInt16BE(offset); offset += 2; + const id = payload.subarray(offset, offset + idLen).toString("utf8"); offset += idLen; + + // queue + const queueLen = payload.readUInt16BE(offset); offset += 2; + const queue = payload.subarray(offset, offset + queueLen).toString("utf8"); offset += queueLen; + + // fairness key + const fkLen = payload.readUInt16BE(offset); offset += 2; + const fairnessKey = payload.subarray(offset, offset + fkLen).toString("utf8"); offset += fkLen; + + // attempt count + const attemptCount = payload.readUInt32BE(offset); offset += 4; + + // headers + const headerCount = payload.readUInt8(offset); offset += 1; + const headers: Record = {}; + for (let h = 0; h < headerCount; h++) { + const kLen = payload.readUInt16BE(offset); offset += 2; + const k = payload.subarray(offset, offset + kLen).toString("utf8"); offset += kLen; + const vLen = payload.readUInt16BE(offset); offset += 2; + const v = payload.subarray(offset, offset + vLen).toString("utf8"); offset += vLen; + headers[k] = v; + } + + // payload + const payloadLen = payload.readUInt32BE(offset); offset += 4; + const msgPayload = Buffer.from(payload.subarray(offset, offset + payloadLen)); offset += payloadLen; + + messages.push({ id, queue, fairnessKey, attemptCount, headers, payload: msgPayload }); + } + + return messages; +} + +// ---- Ack/Nack wire encoding ------------------------------------------------- + +/** Encode an ACK payload: item_count:u16BE | items(queue+msg_id) */ +export function encodeAckPayload(queue: string, msgId: string): Buffer { + return encodeAckNackItems([{ queue, msgId }]); +} + +function encodeAckNackItems(items: Array<{ queue: string; msgId: string }>): Buffer { + const parts: Buffer[] = []; + const countBuf = Buffer.allocUnsafe(2); + countBuf.writeUInt16BE(items.length, 0); + parts.push(countBuf); + + for (const item of items) { + const qBuf = Buffer.from(item.queue, "utf8"); + const idBuf = Buffer.from(item.msgId, "utf8"); + const entry = Buffer.allocUnsafe(2 + qBuf.length + 2 + idBuf.length); + let off = 0; + entry.writeUInt16BE(qBuf.length, off); off += 2; + qBuf.copy(entry, off); off += qBuf.length; + entry.writeUInt16BE(idBuf.length, off); off += 2; + idBuf.copy(entry, off); + parts.push(entry); + } + + return Buffer.concat(parts); +} + +/** Encode a NACK payload: same as ACK items + err_len:u16BE + err_msg per item */ +export function encodeNackPayload(queue: string, msgId: string, error: string): Buffer { + const qBuf = Buffer.from(queue, "utf8"); + const idBuf = Buffer.from(msgId, "utf8"); + const errBuf = Buffer.from(error, "utf8"); + + const countBuf = Buffer.allocUnsafe(2); + countBuf.writeUInt16BE(1, 0); + + const entry = Buffer.allocUnsafe(2 + qBuf.length + 2 + idBuf.length + 2 + errBuf.length); + let off = 0; + entry.writeUInt16BE(qBuf.length, off); off += 2; + qBuf.copy(entry, off); off += qBuf.length; + entry.writeUInt16BE(idBuf.length, off); off += 2; + idBuf.copy(entry, off); off += idBuf.length; + entry.writeUInt16BE(errBuf.length, off); off += 2; + errBuf.copy(entry, off); + + return Buffer.concat([countBuf, entry]); +} + +// ---- Ack/Nack response decoding --------------------------------------------- + +export type AckNackItemResult = + | { ok: true } + | { ok: false; errCode: number; errMsg: string }; + +/** Decode an ACK or NACK response payload. */ +export function decodeAckNackResponse(payload: Buffer): AckNackItemResult[] { + let offset = 0; + const count = payload.readUInt16BE(offset); offset += 2; + const results: AckNackItemResult[] = []; + + for (let i = 0; i < count; i++) { + const ok = payload.readUInt8(offset); offset += 1; + if (ok === 1) { + results.push({ ok: true }); + } else { + const errCode = payload.readUInt16BE(offset); offset += 2; + const errLen = payload.readUInt16BE(offset); offset += 2; + const errMsg = payload.subarray(offset, offset + errLen).toString("utf8"); offset += errLen; + results.push({ ok: false, errCode, errMsg }); + } + } + + return results; +} + +// ---- Auth encoding ---------------------------------------------------------- + +/** Encode an AUTH frame payload: key_len:u16BE + key:utf8 */ +export function encodeAuthPayload(apiKey: string): Buffer { + const keyBuf = Buffer.from(apiKey, "utf8"); + const buf = Buffer.allocUnsafe(2 + keyBuf.length); + buf.writeUInt16BE(keyBuf.length, 0); + keyBuf.copy(buf, 2); + return buf; +} + +// ---- Error frame decoding --------------------------------------------------- + +/** Decode an ERROR frame payload: err_code:u16BE + msg_len:u16BE + msg:utf8 */ +export function decodeErrorPayload(payload: Buffer): { code: number; message: string } { + const code = payload.readUInt16BE(0); + const msgLen = payload.readUInt16BE(2); + const message = payload.subarray(4, 4 + msgLen).toString("utf8"); + return { code, message }; +} + +// ---- Connection class ------------------------------------------------------- + +type PendingEntry = + | { kind: "once"; resolve: (frame: Frame) => void; reject: (err: Error) => void } + | { kind: "stream"; push: (frame: Frame) => void; end: (err?: Error) => void }; + +/** + * A multiplexed FIBP connection over a single TCP (or TLS) socket. + * + * Lifecycle: + * const conn = await FibpConnection.connect({ host, port, ... }); + * const frame = await conn.request(op, payload); + * conn.destroy(); + */ +export class FibpConnection extends EventEmitter { + private readonly socket: net.Socket; + private readBuf = Buffer.alloc(0); + private pending = new Map(); + private _closed = false; + + /** push listeners for server-initiated push frames (corrId == 0) */ + private pushHandlers = new Map void>(); + + private constructor(socket: net.Socket) { + super(); + this.socket = socket; + socket.on("data", (chunk: Buffer) => this.onData(chunk)); + socket.on("error", (err) => this.onSocketError(err)); + socket.on("close", () => this.onSocketClose()); + } + + get closed(): boolean { + return this._closed; + } + + // ---- Factory -------------------------------------------------------------- + + static connect(opts: ConnectOptions): Promise { + return new Promise((resolve, reject) => { + let socket: net.Socket; + + const tlsEnabled = opts.tls || opts.caCert; + + if (tlsEnabled) { + const tlsOpts: tls.ConnectionOptions = { + host: opts.host, + port: opts.port, + rejectUnauthorized: true, + }; + if (opts.caCert) { + tlsOpts.ca = opts.caCert; + } + if (opts.clientCert && opts.clientKey) { + tlsOpts.cert = opts.clientCert; + tlsOpts.key = opts.clientKey; + } + socket = tls.connect(tlsOpts); + } else { + socket = net.connect({ host: opts.host, port: opts.port }); + } + + socket.once("error", reject); + + const connectEvent = tlsEnabled ? "secureConnect" : "connect"; + socket.once(connectEvent, async () => { + socket.removeListener("error", reject); + try { + await performHandshake(socket); + const conn = new FibpConnection(socket); + // Authenticate immediately if apiKey provided. + if (opts.apiKey) { + await conn.authenticate(opts.apiKey); + } + resolve(conn); + } catch (err) { + socket.destroy(); + reject(err); + } + }); + }); + } + + // ---- Auth ----------------------------------------------------------------- + + private async authenticate(apiKey: string): Promise { + const payload = encodeAuthPayload(apiKey); + await this.request(Op.AUTH, payload); + } + + // ---- Request/Response multiplexing ---------------------------------------- + + /** + * Send a request frame and await a single response frame. + * Rejects if the server responds with an ERROR frame or the connection closes. + */ + request(op: number, payload: Buffer): Promise { + return new Promise((resolve, reject) => { + if (this._closed) { + reject(new RPCError(ErrCode.INTERNAL, "connection closed")); + return; + } + const corrId = nextCorrId(); + this.pending.set(corrId, { kind: "once", resolve, reject }); + const frame = encodeFrame(op, corrId, payload); + this.socket.write(frame, (err) => { + if (err) { + this.pending.delete(corrId); + reject(new FilaError(`write error: ${err.message}`)); + } + }); + }); + } + + /** + * Send a stream-initiation frame and return an AsyncIterable of pushed frames. + * Frames with FLAG_PUSH are dispatched here; the stream ends when the socket + * closes or the server sends a GOAWAY. + * + * The corrId is returned so the caller can cancel if needed. + */ + openStream(op: number, payload: Buffer): { corrId: number; iter: AsyncIterable } { + const corrId = nextCorrId(); + + // Build an async iterable backed by a queue + promise chain. + const queue: Frame[] = []; + let ended = false; + let endError: Error | undefined; + let waiter: { resolve: (v: IteratorResult) => void; reject: (e: Error) => void } | null = null; + + const push = (frame: Frame) => { + if (waiter) { + const w = waiter; + waiter = null; + w.resolve({ done: false, value: frame }); + } else { + queue.push(frame); + } + }; + + const end = (err?: Error) => { + ended = true; + endError = err; + this.pending.delete(corrId); + if (waiter) { + const w = waiter; + waiter = null; + if (err) { + w.reject(err); + } else { + w.resolve({ done: true, value: undefined as unknown as Frame }); + } + } + }; + + this.pending.set(corrId, { kind: "stream", push, end }); + + const frame = encodeFrame(op, corrId, payload); + this.socket.write(frame, (err) => { + if (err) { + this.pending.delete(corrId); + end(new FilaError(`write error: ${err.message}`)); + } + }); + + const iter: AsyncIterable = { + [Symbol.asyncIterator]() { + return { + next(): Promise> { + if (queue.length > 0) { + return Promise.resolve({ done: false, value: queue.shift()! }); + } + if (ended) { + if (endError) return Promise.reject(endError); + return Promise.resolve({ done: true, value: undefined as unknown as Frame }); + } + return new Promise>((resolve, reject) => { + waiter = { resolve, reject }; + }); + }, + return(): Promise> { + end(); + return Promise.resolve({ done: true, value: undefined as unknown as Frame }); + }, + }; + }, + }; + + return { corrId, iter }; + } + + /** Cancel a stream by corrId (e.g. when consumer breaks out of for-await). */ + cancelStream(corrId: number): void { + const entry = this.pending.get(corrId); + if (entry?.kind === "stream") { + entry.end(); + } + } + + // ---- Socket data handling ------------------------------------------------- + + private onData(chunk: Buffer): void { + this.readBuf = Buffer.concat([this.readBuf, chunk]); + this.parseFrames(); + } + + private parseFrames(): void { + while (this.readBuf.length >= LENGTH_PREFIX + MIN_FRAME) { + const frameLen = this.readBuf.readUInt32BE(0); + if (this.readBuf.length < LENGTH_PREFIX + frameLen) break; // incomplete + + const frame = this.readBuf.subarray(0, LENGTH_PREFIX + frameLen); + this.readBuf = Buffer.from(this.readBuf.subarray(LENGTH_PREFIX + frameLen)); + + this.dispatchFrame(frame); + } + } + + private dispatchFrame(raw: Buffer): void { + const flags = raw.readUInt8(LENGTH_PREFIX); + const op = raw.readUInt8(LENGTH_PREFIX + 1); + const corrId = raw.readUInt32BE(LENGTH_PREFIX + 2); + const payload = Buffer.from(raw.subarray(LENGTH_PREFIX + FRAME_HEADER)); + + const frame: Frame = { flags, op, corrId, payload }; + + const entry = this.pending.get(corrId); + if (!entry) { + // No registered handler — could be a keepalive or unknown push. + return; + } + + if (op === Op.ERROR) { + const { message } = decodeErrorPayload(payload); + const err = new RPCError(ErrCode.INTERNAL, message); + if (entry.kind === "once") { + this.pending.delete(corrId); + entry.reject(err); + } else { + entry.end(err); + } + return; + } + + if (entry.kind === "once") { + this.pending.delete(corrId); + entry.resolve(frame); + } else { + // stream: push delivers messages; anything else ends the stream + if (flags & FLAG_PUSH) { + entry.push(frame); + } else { + // Non-push frame on a stream corrId → stream ended cleanly by server + entry.end(); + } + } + } + + private onSocketError(err: Error): void { + this._closed = true; + const rpcErr = new FilaError(`connection error: ${err.message}`); + for (const [, entry] of this.pending) { + if (entry.kind === "once") { + entry.reject(rpcErr); + } else { + entry.end(rpcErr); + } + } + this.pending.clear(); + this.emit("error", err); + } + + private onSocketClose(): void { + this._closed = true; + const rpcErr = new FilaError("connection closed by server"); + for (const [, entry] of this.pending) { + if (entry.kind === "once") { + entry.reject(rpcErr); + } else { + entry.end(rpcErr); + } + } + this.pending.clear(); + this.emit("close"); + } + + // ---- Cleanup -------------------------------------------------------------- + + destroy(): void { + if (!this._closed) { + this._closed = true; + this.socket.destroy(); + } + } +} + +// ---- Handshake helper ------------------------------------------------------- + +function performHandshake(socket: net.Socket): Promise { + return new Promise((resolve, reject) => { + socket.write(HANDSHAKE, (writeErr) => { + if (writeErr) { + reject(new FilaError(`handshake write error: ${writeErr.message}`)); + return; + } + }); + + let buf = Buffer.alloc(0); + + const onData = (chunk: Buffer) => { + buf = Buffer.concat([buf, chunk]); + if (buf.length >= HANDSHAKE.length) { + socket.removeListener("data", onData); + socket.removeListener("error", onError); + const echo = buf.subarray(0, HANDSHAKE.length); + if (!echo.equals(HANDSHAKE)) { + reject(new FilaError(`bad handshake response: ${echo.toString("hex")}`)); + return; + } + // Push any leftover bytes back so the frame parser sees them. + if (buf.length > HANDSHAKE.length) { + socket.unshift(buf.subarray(HANDSHAKE.length)); + } + resolve(); + } + }; + + const onError = (err: Error) => { + socket.removeListener("data", onData); + reject(new FilaError(`handshake error: ${err.message}`)); + }; + + socket.on("data", onData); + socket.once("error", onError); + }); +} + +// ---- ConnectOptions --------------------------------------------------------- + +export interface ConnectOptions { + host: string; + port: number; + tls?: boolean; + caCert?: Buffer; + clientCert?: Buffer; + clientKey?: Buffer; + apiKey?: string; +} diff --git a/test/auth.test.ts b/test/auth.test.ts index 8ee6732..48931db 100644 --- a/test/auth.test.ts +++ b/test/auth.test.ts @@ -2,9 +2,8 @@ import { describe, it, expect, beforeAll, afterAll } from "vitest"; import * as fs from "fs"; import * as os from "os"; import * as path from "path"; -import * as grpc from "@grpc/grpc-js"; import { Client } from "../src"; -import { RPCError } from "../src/errors"; +import { UnauthenticatedError } from "../src/errors"; import { startTestServer, generateTestCerts, @@ -49,11 +48,7 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("TLS + API key auth", () => { try { await expect( client.enqueue("auth-test-nokey", null, Buffer.from("fail")) - ).rejects.toSatisfy((err: unknown) => { - expect(err).toBeInstanceOf(RPCError); - expect((err as RPCError).code).toBe(grpc.status.UNAUTHENTICATED); - return true; - }); + ).rejects.toBeInstanceOf(UnauthenticatedError); } finally { await client.close(); } @@ -65,7 +60,7 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("TLS + API key auth", () => { try { await expect( client.enqueue("auth-test-badkey", null, Buffer.from("fail")) - ).rejects.toThrow(RPCError); + ).rejects.toBeInstanceOf(UnauthenticatedError); } finally { await client.close(); } @@ -102,15 +97,13 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("TLS + API key auth", () => { const serverCertPath = path.join(certDir, "server.pem"); const serverKeyPath = path.join(certDir, "server.key"); - const adminCreds = grpc.credentials.createSsl(certs.caCert); - server = await startTestServer({ extraConfig: [ `[tls]`, `cert_file = "${serverCertPath}"`, `key_file = "${serverKeyPath}"`, ].join("\n"), - adminCreds, + adminCaCert: certs.caCert, }); }, 30_000); @@ -159,12 +152,6 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("TLS + API key auth", () => { const serverKeyPath = path.join(certDir, "server.key"); const caCertPath = path.join(certDir, "ca.pem"); - const adminCreds = grpc.credentials.createSsl( - certs.caCert, - certs.clientKey, - certs.clientCert - ); - server = await startTestServer({ extraConfig: [ `[tls]`, @@ -174,7 +161,9 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("TLS + API key auth", () => { `[auth]`, `bootstrap_apikey = "${BOOTSTRAP_KEY}"`, ].join("\n"), - adminCreds, + adminCaCert: certs.caCert, + adminClientCert: certs.clientCert, + adminClientKey: certs.clientKey, adminApiKey: BOOTSTRAP_KEY, }); }, 30_000); diff --git a/test/helpers.ts b/test/helpers.ts index 47e43ac..9fe03c7 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -3,8 +3,11 @@ import * as fs from "fs"; import * as net from "net"; import * as os from "os"; import * as path from "path"; -import * as grpc from "@grpc/grpc-js"; -import * as protoLoader from "@grpc/proto-loader"; +import * as protobuf from "protobufjs"; +import { + FibpConnection, + Op, +} from "../src/transport"; const PROTO_DIR = path.join(__dirname, "..", "proto"); @@ -40,15 +43,87 @@ export interface TestServerOptions { extraConfig?: string; /** Environment variables to merge. */ extraEnv?: Record; - /** gRPC credentials for admin/readiness connections (default: insecure). */ - adminCreds?: grpc.ChannelCredentials; - /** API key metadata to attach to admin RPCs. */ + /** CA cert PEM for TLS admin connections. */ + adminCaCert?: Buffer; + /** Client cert PEM for mTLS admin connections. */ + adminClientCert?: Buffer; + /** Client key PEM for mTLS admin connections. */ + adminClientKey?: Buffer; + /** API key to attach to admin requests. */ adminApiKey?: string; } export const FILA_SERVER_BIN = findServerBinary(); export const FILA_SERVER_AVAILABLE = fs.existsSync(FILA_SERVER_BIN); +// ---- Protobuf loading ------------------------------------------------------- + +let _adminRoot: protobuf.Root | null = null; + +function getAdminRoot(): protobuf.Root { + if (_adminRoot) return _adminRoot; + _adminRoot = new protobuf.Root(); + _adminRoot.resolvePath = (_origin, target) => { + // Resolve google/protobuf includes from the protobufjs built-ins. + if (target.startsWith("google/")) { + return path.join(__dirname, "..", "node_modules", "protobufjs", target); + } + return path.join(PROTO_DIR, target); + }; + _adminRoot.loadSync(path.join(PROTO_DIR, "fila", "v1", "admin.proto")); + _adminRoot.loadSync(path.join(PROTO_DIR, "fila", "v1", "messages.proto")); + return _adminRoot; +} + +function encodeAdminMsg(typeName: string, fields: Record): Buffer { + const root = getAdminRoot(); + const MsgType = root.lookupType(typeName); + const err = MsgType.verify(fields); + if (err) throw new Error(`protobuf verify failed: ${err}`); + const msg = MsgType.create(fields); + return Buffer.from(MsgType.encode(msg).finish()); +} + +function decodeAdminMsg(typeName: string, buf: Buffer): T { + const root = getAdminRoot(); + const MsgType = root.lookupType(typeName); + return MsgType.decode(buf) as unknown as T; +} + +// ---- Admin FIBP helpers ----------------------------------------------------- + +/** Connect to the server with optional TLS for admin use. */ +async function connectAdmin(addr: string, opts: TestServerOptions): Promise { + const lastColon = addr.lastIndexOf(":"); + const host = addr.slice(0, lastColon); + const port = parseInt(addr.slice(lastColon + 1), 10); + + return FibpConnection.connect({ + host, + port, + tls: !!(opts.adminCaCert), + caCert: opts.adminCaCert, + clientCert: opts.adminClientCert, + clientKey: opts.adminClientKey, + apiKey: opts.adminApiKey, + }); +} + +async function callListQueues(conn: FibpConnection): Promise { + const payload = encodeAdminMsg("fila.v1.ListQueuesRequest", {}); + await conn.request(Op.LIST_QUEUES, payload); +} + +async function callCreateQueue(conn: FibpConnection, name: string): Promise { + const payload = encodeAdminMsg("fila.v1.CreateQueueRequest", { + name, + config: {}, + }); + await conn.request(Op.CREATE_QUEUE, payload); +} + +// ---- TestServer factory ----------------------------------------------------- + export async function startTestServer( opts?: TestServerOptions ): Promise { @@ -78,33 +153,25 @@ export async function startTestServer( stderrBuf += chunk.toString(); }); - const creds = opts?.adminCreds ?? grpc.credentials.createInsecure(); - const adminMeta = new grpc.Metadata(); - if (opts?.adminApiKey) { - adminMeta.set("authorization", `Bearer ${opts.adminApiKey}`); - } - - // Wait for server ready. 20s to accommodate TLS + startup in CI. + // Wait for server ready — probe via FIBP ListQueues. 20s timeout. const deadline = Date.now() + 20000; let ready = false; let exited = false; proc.on("exit", () => { exited = true; }); - // Create a fresh gRPC client per readiness probe. A persistent client can - // enter TRANSIENT_FAILURE with aggressive backoff after the first failed TLS - // handshake, preventing recovery. Fresh clients guarantee a clean attempt. let lastErr: unknown; while (Date.now() < deadline && !exited) { - const probeClient = createAdminClient(addr, creds); + let probeConn: FibpConnection | null = null; try { - await callListQueues(probeClient, adminMeta); + probeConn = await connectAdmin(addr, opts ?? {}); + await callListQueues(probeConn); ready = true; break; } catch (err) { lastErr = err; await sleep(500); } finally { - probeClient.close(); + probeConn?.destroy(); } } @@ -116,72 +183,21 @@ export async function startTestServer( throw new Error(`fila-server failed to start within 20s on ${addr}${detail}${probeDetail}`); } - const adminClient = createAdminClient(addr, creds); + // Persistent admin connection. + const adminConn = await connectAdmin(addr, opts ?? {}); return { addr, dataDir, stop: () => { proc.kill(); - adminClient.close(); + adminConn.destroy(); fs.rmSync(dataDir, { recursive: true, force: true }); }, - createQueue: (name: string) => { - return new Promise((resolve, reject) => { - adminClient.createQueue( - { name, config: {} }, - adminMeta, - (err: grpc.ServiceError | null) => { - if (err) reject(err); - else resolve(); - } - ); - }); - }, + createQueue: (name: string) => callCreateQueue(adminConn, name), }; } -function loadAdminProto(): grpc.ServiceClientConstructor { - const packageDef = protoLoader.loadSync( - [ - path.join(PROTO_DIR, "fila", "v1", "admin.proto"), - path.join(PROTO_DIR, "fila", "v1", "messages.proto"), - ], - { - keepCase: false, - longs: String, - enums: String, - defaults: true, - oneofs: true, - includeDirs: [PROTO_DIR], - } - ); - const proto = grpc.loadPackageDefinition(packageDef); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - return (proto.fila as any).v1.FilaAdmin as grpc.ServiceClientConstructor; -} - -function createAdminClient( - addr: string, - creds: grpc.ChannelCredentials -): grpc.Client { - const AdminService = loadAdminProto(); - return new AdminService(addr, creds); -} - -function callListQueues( - client: grpc.Client, - metadata: grpc.Metadata -): Promise { - return new Promise((resolve, reject) => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - (client as any).listQueues({}, metadata, (err: grpc.ServiceError | null) => { - if (err) reject(err); - else resolve(); - }); - }); -} - function sleep(ms: number): Promise { return new Promise((resolve) => setTimeout(resolve, ms)); } @@ -194,8 +210,6 @@ export function generateTestCerts(outputDir: string): { clientCert: Buffer; clientKey: Buffer; } { - - const caKeyPath = path.join(outputDir, "ca.key"); const caCertPath = path.join(outputDir, "ca.pem"); const serverKeyPath = path.join(outputDir, "server.key"); @@ -280,3 +294,6 @@ export function generateTestCerts(outputDir: string): { clientKey: fs.readFileSync(clientKeyPath), }; } + +// Re-export for tests that decode admin responses. +export { decodeAdminMsg, encodeAdminMsg }; diff --git a/test/transport.unit.test.ts b/test/transport.unit.test.ts new file mode 100644 index 0000000..78162c2 --- /dev/null +++ b/test/transport.unit.test.ts @@ -0,0 +1,313 @@ +import { describe, it, expect } from "vitest"; +import { + encodeFrame, + encodeEnqueuePayload, + decodeEnqueueResponse, + encodeConsumePayload, + decodeConsumeDelivery, + encodeAckPayload, + encodeNackPayload, + decodeAckNackResponse, + encodeAuthPayload, + decodeErrorPayload, + nextCorrId, + HANDSHAKE, + Op, + FLAG_PUSH, +} from "../src/transport"; + +describe("transport unit tests (no server)", () => { + describe("HANDSHAKE", () => { + it("is exactly FIBP\\x01\\x00", () => { + expect(HANDSHAKE).toEqual(Buffer.from([0x46, 0x49, 0x42, 0x50, 0x01, 0x00])); + expect(HANDSHAKE.toString("ascii").slice(0, 4)).toBe("FIBP"); + }); + }); + + describe("nextCorrId", () => { + it("increments and never returns 0", () => { + const ids = new Set(); + for (let i = 0; i < 100; i++) { + const id = nextCorrId(); + expect(id).toBeGreaterThan(0); + ids.add(id); + } + // All 100 IDs should be unique (no wrap-around in 100 steps). + expect(ids.size).toBe(100); + }); + }); + + describe("encodeFrame", () => { + it("encodes frame with 4-byte length prefix, flags, op, corrId, payload", () => { + const payload = Buffer.from("hello"); + const frame = encodeFrame(Op.ENQUEUE, 42, payload, 0x04); + + // 4-byte length: flags(1) + op(1) + corrId(4) + payload(5) = 11 + expect(frame.readUInt32BE(0)).toBe(11); + expect(frame.readUInt8(4)).toBe(0x04); // flags + expect(frame.readUInt8(5)).toBe(Op.ENQUEUE); // op + expect(frame.readUInt32BE(6)).toBe(42); // corrId + expect(frame.subarray(10).toString()).toBe("hello"); + }); + + it("encodes empty payload frame", () => { + const frame = encodeFrame(Op.HEARTBEAT, 1, Buffer.alloc(0)); + expect(frame.readUInt32BE(0)).toBe(6); // just the 6-byte header + expect(frame.length).toBe(10); // 4 length + 6 header + }); + }); + + describe("enqueue payload round-trip", () => { + it("encodes and decodes single message", () => { + const payload = encodeEnqueuePayload([ + { queue: "my-queue", headers: { key: "val" }, payload: Buffer.from("hello") }, + ]); + + // Decode manually to verify layout. + let off = 0; + const qLen = payload.readUInt16BE(off); off += 2; + const queue = payload.subarray(off, off + qLen).toString(); off += qLen; + const msgCount = payload.readUInt16BE(off); off += 2; + expect(queue).toBe("my-queue"); + expect(msgCount).toBe(1); + + // Message: headerCount + headers + payloadLen + payload + const headerCount = payload.readUInt8(off); off += 1; + expect(headerCount).toBe(1); + const kLen = payload.readUInt16BE(off); off += 2; + const k = payload.subarray(off, off + kLen).toString(); off += kLen; + const vLen = payload.readUInt16BE(off); off += 2; + const v = payload.subarray(off, off + vLen).toString(); off += vLen; + expect(k).toBe("key"); + expect(v).toBe("val"); + const pLen = payload.readUInt32BE(off); off += 4; + const p = payload.subarray(off, off + pLen).toString(); off += pLen; + expect(pLen).toBe(5); + expect(p).toBe("hello"); + expect(off).toBe(payload.length); + }); + + it("encodes and decodes multiple messages for same queue", () => { + const msgs = [ + { queue: "q", headers: {}, payload: Buffer.from("a") }, + { queue: "q", headers: { x: "y" }, payload: Buffer.from("b") }, + ]; + const payload = encodeEnqueuePayload(msgs); + let off = 0; + const qLen = payload.readUInt16BE(off); off += 2 + qLen; + const count = payload.readUInt16BE(off); off += 2; + expect(count).toBe(2); + }); + }); + + describe("decodeEnqueueResponse", () => { + it("decodes success result", () => { + const msgId = "msg-id-123"; + const idBuf = Buffer.from(msgId); + // count:1, ok:1, idLen, id + const buf = Buffer.allocUnsafe(2 + 1 + 2 + idBuf.length); + let off = 0; + buf.writeUInt16BE(1, off); off += 2; + buf.writeUInt8(1, off); off += 1; // ok + buf.writeUInt16BE(idBuf.length, off); off += 2; + idBuf.copy(buf, off); + + const results = decodeEnqueueResponse(buf); + expect(results).toHaveLength(1); + expect(results[0].ok).toBe(true); + if (results[0].ok) { + expect(results[0].msgId).toBe(msgId); + } + }); + + it("decodes error result", () => { + const errMsg = "queue not found"; + const errBuf = Buffer.from(errMsg); + const buf = Buffer.allocUnsafe(2 + 1 + 2 + 2 + errBuf.length); + let off = 0; + buf.writeUInt16BE(1, off); off += 2; + buf.writeUInt8(0, off); off += 1; // error + buf.writeUInt16BE(0x0001, off); off += 2; // QUEUE_NOT_FOUND + buf.writeUInt16BE(errBuf.length, off); off += 2; + errBuf.copy(buf, off); + + const results = decodeEnqueueResponse(buf); + expect(results).toHaveLength(1); + expect(results[0].ok).toBe(false); + if (!results[0].ok) { + expect(results[0].errCode).toBe(0x0001); + expect(results[0].errMsg).toBe(errMsg); + } + }); + }); + + describe("consume payload encoding", () => { + it("encodes queue name and initial credits", () => { + const payload = encodeConsumePayload("my-queue", 128); + let off = 0; + const qLen = payload.readUInt16BE(off); off += 2; + const queue = payload.subarray(off, off + qLen).toString(); off += qLen; + const credits = payload.readUInt32BE(off); + expect(queue).toBe("my-queue"); + expect(credits).toBe(128); + }); + }); + + describe("decodeConsumeDelivery", () => { + it("decodes a pushed message frame", () => { + // Build a wire message manually. + const id = "test-id-001"; + const queue = "my-queue"; + const fk = "tenant-A"; + const attemptCount = 2; + const headerKey = "x-source"; + const headerVal = "test"; + const msgPayload = Buffer.from("hello world"); + + const parts: Buffer[] = []; + + // count:1 + const countBuf = Buffer.allocUnsafe(2); + countBuf.writeUInt16BE(1, 0); + parts.push(countBuf); + + const pushField = (s: string, bits = 16) => { + const buf = Buffer.from(s); + const lenBuf = bits === 16 ? Buffer.allocUnsafe(2) : Buffer.allocUnsafe(1); + if (bits === 16) (lenBuf as Buffer).writeUInt16BE(buf.length, 0); + else (lenBuf as Buffer).writeUInt8(buf.length, 0); + parts.push(lenBuf, buf); + }; + + pushField(id); + pushField(queue); + pushField(fk); + + const attemptBuf = Buffer.allocUnsafe(4); + attemptBuf.writeUInt32BE(attemptCount, 0); + parts.push(attemptBuf); + + // 1 header + const hCountBuf = Buffer.allocUnsafe(1); + hCountBuf.writeUInt8(1, 0); + parts.push(hCountBuf); + pushField(headerKey); + pushField(headerVal); + + const pLenBuf = Buffer.allocUnsafe(4); + pLenBuf.writeUInt32BE(msgPayload.length, 0); + parts.push(pLenBuf, msgPayload); + + const wirePayload = Buffer.concat(parts); + const messages = decodeConsumeDelivery(wirePayload); + + expect(messages).toHaveLength(1); + const msg = messages[0]; + expect(msg.id).toBe(id); + expect(msg.queue).toBe(queue); + expect(msg.fairnessKey).toBe(fk); + expect(msg.attemptCount).toBe(attemptCount); + expect(msg.headers).toEqual({ [headerKey]: headerVal }); + expect(msg.payload.toString()).toBe("hello world"); + }); + }); + + describe("ack/nack payload encoding", () => { + it("encodes ack payload with count + queue + msgId", () => { + const payload = encodeAckPayload("the-queue", "msg-abc"); + let off = 0; + const count = payload.readUInt16BE(off); off += 2; + expect(count).toBe(1); + const qLen = payload.readUInt16BE(off); off += 2; + const queue = payload.subarray(off, off + qLen).toString(); off += qLen; + const idLen = payload.readUInt16BE(off); off += 2; + const id = payload.subarray(off, off + idLen).toString(); off += idLen; + expect(queue).toBe("the-queue"); + expect(id).toBe("msg-abc"); + expect(off).toBe(payload.length); + }); + + it("encodes nack payload including error message", () => { + const payload = encodeNackPayload("q", "id1", "processing failed"); + let off = 0; + const count = payload.readUInt16BE(off); off += 2; + expect(count).toBe(1); + const qLen = payload.readUInt16BE(off); off += 2; + off += qLen; // skip queue + const idLen = payload.readUInt16BE(off); off += 2; + off += idLen; // skip msg id + const errLen = payload.readUInt16BE(off); off += 2; + const errMsg = payload.subarray(off, off + errLen).toString(); off += errLen; + expect(errMsg).toBe("processing failed"); + expect(off).toBe(payload.length); + }); + }); + + describe("decodeAckNackResponse", () => { + it("decodes success", () => { + const buf = Buffer.allocUnsafe(3); + buf.writeUInt16BE(1, 0); + buf.writeUInt8(1, 2); // ok + const results = decodeAckNackResponse(buf); + expect(results).toHaveLength(1); + expect(results[0].ok).toBe(true); + }); + + it("decodes error", () => { + const errMsg = "msg not found"; + const errBuf = Buffer.from(errMsg); + const buf = Buffer.allocUnsafe(2 + 1 + 2 + 2 + errBuf.length); + let off = 0; + buf.writeUInt16BE(1, off); off += 2; + buf.writeUInt8(0, off); off += 1; + buf.writeUInt16BE(0x0002, off); off += 2; // MESSAGE_NOT_FOUND + buf.writeUInt16BE(errBuf.length, off); off += 2; + errBuf.copy(buf, off); + const results = decodeAckNackResponse(buf); + expect(results[0].ok).toBe(false); + if (!results[0].ok) { + expect(results[0].errCode).toBe(0x0002); + expect(results[0].errMsg).toBe(errMsg); + } + }); + }); + + describe("auth payload encoding", () => { + it("encodes api key with length prefix", () => { + const payload = encodeAuthPayload("my-secret-key"); + const kLen = payload.readUInt16BE(0); + const key = payload.subarray(2, 2 + kLen).toString(); + expect(key).toBe("my-secret-key"); + }); + }); + + describe("decodeErrorPayload", () => { + it("decodes error code and message", () => { + const errMsg = "something went wrong"; + const errBuf = Buffer.from(errMsg); + const buf = Buffer.allocUnsafe(4 + errBuf.length); + buf.writeUInt16BE(0xFFFF, 0); + buf.writeUInt16BE(errBuf.length, 2); + errBuf.copy(buf, 4); + const result = decodeErrorPayload(buf); + expect(result.code).toBe(0xFFFF); + expect(result.message).toBe(errMsg); + }); + }); + + describe("Op codes", () => { + it("has expected hot-path codes", () => { + expect(Op.ENQUEUE).toBe(0x01); + expect(Op.CONSUME).toBe(0x02); + expect(Op.ACK).toBe(0x03); + expect(Op.NACK).toBe(0x04); + expect(Op.AUTH).toBe(0x30); + expect(Op.ERROR).toBe(0xFE); + expect(Op.GOAWAY).toBe(0xFF); + }); + + it("FLAG_PUSH is bit 2", () => { + expect(FLAG_PUSH).toBe(0x04); + }); + }); +}); diff --git a/tsconfig.json b/tsconfig.json index 889749a..e954ca4 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -13,6 +13,6 @@ "resolveJsonModule": true, "moduleResolution": "node" }, - "include": ["src/**/*", "generated/**/*"], + "include": ["src/**/*"], "exclude": ["node_modules", "dist", "test"] } From 30e0c9e6fbfa2dc27df4ed4d50e1a18dfe6b9717 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Thu, 26 Mar 2026 09:21:28 -0300 Subject: [PATCH 2/4] fix: add 30s timeout to api key auth beforeall hook --- test/auth.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/auth.test.ts b/test/auth.test.ts index 48931db..ec57e97 100644 --- a/test/auth.test.ts +++ b/test/auth.test.ts @@ -21,7 +21,7 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("TLS + API key auth", () => { extraConfig: `[auth]\nbootstrap_apikey = "${BOOTSTRAP_KEY}"`, adminApiKey: BOOTSTRAP_KEY, }); - }); + }, 30_000); afterAll(() => { server?.stop(); From d78acbf8723677851cbd790ddcf50e71f641e4bd Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Thu, 26 Mar 2026 09:24:00 -0300 Subject: [PATCH 3/4] fix: add 30s timeout to all beforeall server startup hooks --- test/batch.test.ts | 2 +- test/client.test.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/batch.test.ts b/test/batch.test.ts index 40ea0e2..3f5cfdf 100644 --- a/test/batch.test.ts +++ b/test/batch.test.ts @@ -12,7 +12,7 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("Enqueue operations", () => { beforeAll(async () => { server = await startTestServer(); - }); + }, 30_000); afterAll(() => { server?.stop(); diff --git a/test/client.test.ts b/test/client.test.ts index d546b28..9a161e5 100644 --- a/test/client.test.ts +++ b/test/client.test.ts @@ -12,7 +12,7 @@ describe.skipIf(!FILA_SERVER_AVAILABLE)("Client", () => { beforeAll(async () => { server = await startTestServer(); - }); + }, 30_000); afterAll(() => { server?.stop(); From ed658d1b05e0a20883f99c63e4cbf9328ea7c6d4 Mon Sep 17 00:00:00 2001 From: Lucas Vieira Date: Thu, 26 Mar 2026 09:29:58 -0300 Subject: [PATCH 4/4] fix: address cubic review findings - transport: preserve wire error code in rpc errors (not hardcoded INTERNAL) - client: add rpcerror catch+remap for transport-level errors on enqueue/ack/nack/consume - errors: add rpcerror.detail to store raw message without code prefix - batcher: remap transport-level rpcerror to typed errors before rejecting batch items - helpers: handle connectadmin failure to avoid server process + tempdir leak --- src/batcher.ts | 11 +++++++++-- src/client.ts | 38 ++++++++++++++++++++++++++++---------- src/errors.ts | 3 +++ src/transport.ts | 4 ++-- test/helpers.ts | 9 ++++++++- 5 files changed, 50 insertions(+), 15 deletions(-) diff --git a/src/batcher.ts b/src/batcher.ts index a73998b..6fc7a0d 100644 --- a/src/batcher.ts +++ b/src/batcher.ts @@ -210,8 +210,15 @@ export class Batcher { const payload = encodeEnqueuePayload(messages); respFrame = await conn.request(Op.ENQUEUE, payload); } catch (err) { - // Transport-level failure: all items in this batch get the error. - const mapped = err instanceof Error ? err : new RPCError(ErrCode.INTERNAL, String(err)); + // Transport-level failure: remap typed errors then propagate to all batch items. + let mapped: Error; + if (err instanceof RPCError) { + mapped = mapEnqueueWireError(err.code, err.detail); + } else if (err instanceof Error) { + mapped = err; + } else { + mapped = new RPCError(ErrCode.INTERNAL, String(err)); + } for (const item of items) { item.reject(mapped); } diff --git a/src/client.ts b/src/client.ts index 1daf698..c5b77fe 100644 --- a/src/client.ts +++ b/src/client.ts @@ -289,7 +289,13 @@ export class Client { // No batching: direct single-message ENQUEUE. const framePayload = encodeEnqueuePayload([{ queue, headers: headers ?? {}, payload }]); - const resp = await conn.request(Op.ENQUEUE, framePayload); + let resp; + try { + resp = await conn.request(Op.ENQUEUE, framePayload); + } catch (err) { + if (err instanceof RPCError) throw mapEnqueueWireError(err.code, err.detail); + throw err; + } const results = decodeEnqueueResponse(resp.payload); const result = results[0]; if (!result) { @@ -402,13 +408,6 @@ export class Client { // Non-push frame on stream = clean server-side close. break; } - if (frame.op === Op.ERROR) { - const { code, message } = { - code: frame.payload.readUInt16BE(0), - message: frame.payload.subarray(4).toString("utf8"), - }; - throw mapConsumeWireError(code, message); - } const messages = decodeConsumeDelivery(frame.payload); for (const msg of messages) { yield { @@ -421,6 +420,13 @@ export class Client { }; } } + } catch (err) { + // Transport errors from the stream (e.g. ERROR frames) arrive as RPCError. + // Remap to typed SDK errors where possible. + if (err instanceof RPCError) { + throw mapConsumeWireError(err.code, err.detail); + } + throw err; } finally { conn.cancelStream(corrId); } @@ -439,7 +445,13 @@ export class Client { async ack(queue: string, msgId: string): Promise { const conn = await this.getConn(); const payload = encodeAckPayload(queue, msgId); - const resp = await conn.request(Op.ACK, payload); + let resp; + try { + resp = await conn.request(Op.ACK, payload); + } catch (err) { + if (err instanceof RPCError) throw mapAckWireError(err.code, err.detail); + throw err; + } const results = decodeAckNackResponse(resp.payload); const result = results[0]; if (!result) { @@ -464,7 +476,13 @@ export class Client { async nack(queue: string, msgId: string, error: string): Promise { const conn = await this.getConn(); const payload = encodeNackPayload(queue, msgId, error); - const resp = await conn.request(Op.NACK, payload); + let resp; + try { + resp = await conn.request(Op.NACK, payload); + } catch (err) { + if (err instanceof RPCError) throw mapNackWireError(err.code, err.detail); + throw err; + } const results = decodeAckNackResponse(resp.payload); const result = results[0]; if (!result) { diff --git a/src/errors.ts b/src/errors.ts index ac3ec43..cd52546 100644 --- a/src/errors.ts +++ b/src/errors.ts @@ -33,10 +33,13 @@ export class UnauthenticatedError extends FilaError { /** Raised for unexpected protocol-level failures, preserving the wire error code. */ export class RPCError extends FilaError { public readonly code: number; + /** The raw error message from the server, without the code prefix. */ + public readonly detail: string; constructor(code: number, message: string) { super(`rpc error (code = ${code}): ${message}`); this.name = "RPCError"; this.code = code; + this.detail = message; } } diff --git a/src/transport.ts b/src/transport.ts index 369658c..b261bd7 100644 --- a/src/transport.ts +++ b/src/transport.ts @@ -599,8 +599,8 @@ export class FibpConnection extends EventEmitter { } if (op === Op.ERROR) { - const { message } = decodeErrorPayload(payload); - const err = new RPCError(ErrCode.INTERNAL, message); + const { code, message } = decodeErrorPayload(payload); + const err = new RPCError(code, message); if (entry.kind === "once") { this.pending.delete(corrId); entry.reject(err); diff --git a/test/helpers.ts b/test/helpers.ts index 9fe03c7..be100fa 100644 --- a/test/helpers.ts +++ b/test/helpers.ts @@ -184,7 +184,14 @@ export async function startTestServer( } // Persistent admin connection. - const adminConn = await connectAdmin(addr, opts ?? {}); + let adminConn: FibpConnection; + try { + adminConn = await connectAdmin(addr, opts ?? {}); + } catch (err) { + proc.kill(); + fs.rmSync(dataDir, { recursive: true, force: true }); + throw new Error(`fila-server started but admin connection failed: ${err}`); + } return { addr,