@qualithm/arrow-flight-client 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/LICENSE +190 -0
  2. package/README.md +258 -0
  3. package/dist/client/create-flight-client.d.ts +28 -0
  4. package/dist/client/create-flight-client.d.ts.map +1 -0
  5. package/dist/client/create-flight-client.js +29 -0
  6. package/dist/client/create-flight-client.js.map +1 -0
  7. package/dist/client/create-flight-sql-client.d.ts +28 -0
  8. package/dist/client/create-flight-sql-client.d.ts.map +1 -0
  9. package/dist/client/create-flight-sql-client.js +29 -0
  10. package/dist/client/create-flight-sql-client.js.map +1 -0
  11. package/dist/client/errors.d.ts +81 -0
  12. package/dist/client/errors.d.ts.map +1 -0
  13. package/dist/client/errors.js +106 -0
  14. package/dist/client/errors.js.map +1 -0
  15. package/dist/client/flight-client.d.ts +161 -0
  16. package/dist/client/flight-client.d.ts.map +1 -0
  17. package/dist/client/flight-client.js +403 -0
  18. package/dist/client/flight-client.js.map +1 -0
  19. package/dist/client/flight-sql-client.d.ts +348 -0
  20. package/dist/client/flight-sql-client.d.ts.map +1 -0
  21. package/dist/client/flight-sql-client.js +689 -0
  22. package/dist/client/flight-sql-client.js.map +1 -0
  23. package/dist/client/index.d.ts +11 -0
  24. package/dist/client/index.d.ts.map +1 -0
  25. package/dist/client/index.js +10 -0
  26. package/dist/client/index.js.map +1 -0
  27. package/dist/client/ipc.d.ts +131 -0
  28. package/dist/client/ipc.d.ts.map +1 -0
  29. package/dist/client/ipc.js +246 -0
  30. package/dist/client/ipc.js.map +1 -0
  31. package/dist/client/types.d.ts +153 -0
  32. package/dist/client/types.d.ts.map +1 -0
  33. package/dist/client/types.js +26 -0
  34. package/dist/client/types.js.map +1 -0
  35. package/dist/gen/arrow/flight/FlightSql_pb.d.ts +3173 -0
  36. package/dist/gen/arrow/flight/FlightSql_pb.d.ts.map +1 -0
  37. package/dist/gen/arrow/flight/FlightSql_pb.js +2258 -0
  38. package/dist/gen/arrow/flight/FlightSql_pb.js.map +1 -0
  39. package/dist/gen/arrow/flight/Flight_pb.d.ts +1159 -0
  40. package/dist/gen/arrow/flight/Flight_pb.d.ts.map +1 -0
  41. package/dist/gen/arrow/flight/Flight_pb.js +397 -0
  42. package/dist/gen/arrow/flight/Flight_pb.js.map +1 -0
  43. package/dist/index.d.ts +12 -0
  44. package/dist/index.d.ts.map +1 -0
  45. package/dist/index.js +14 -0
  46. package/dist/index.js.map +1 -0
  47. package/package.json +96 -0
@@ -0,0 +1,106 @@
1
+ /**
2
+ * Base error class for all Flight client errors.
3
+ */
4
+ export class FlightError extends Error {
5
+ /** Error name for identification. */
6
+ name = "FlightError";
7
+ /** Original error that caused this error, if any. */
8
+ cause;
9
+ constructor(message, cause) {
10
+ super(message, { cause });
11
+ this.cause = cause;
12
+ }
13
+ /**
14
+ * Type guard to check if an error is a FlightError.
15
+ */
16
+ static isError(error) {
17
+ return error instanceof FlightError;
18
+ }
19
+ }
20
+ /**
21
+ * Error thrown when a connection to the Flight server fails.
22
+ */
23
+ export class FlightConnectionError extends FlightError {
24
+ /** Error name for identification. */
25
+ name = "FlightConnectionError";
26
+ /** URL that failed to connect. */
27
+ url;
28
+ constructor(message, url, cause) {
29
+ super(message, cause);
30
+ this.url = url;
31
+ }
32
+ /**
33
+ * Type guard to check if an error is a FlightConnectionError.
34
+ */
35
+ static isError(error) {
36
+ return error instanceof FlightConnectionError;
37
+ }
38
+ }
39
+ /**
40
+ * Error thrown when authentication fails.
41
+ */
42
+ export class FlightAuthError extends FlightError {
43
+ /** Error name for identification. */
44
+ name = "FlightAuthError";
45
+ /**
46
+ * Type guard to check if an error is a FlightAuthError.
47
+ */
48
+ static isError(error) {
49
+ return error instanceof FlightAuthError;
50
+ }
51
+ }
52
+ /**
53
+ * Error thrown when a request times out.
54
+ */
55
+ export class FlightTimeoutError extends FlightError {
56
+ /** Error name for identification. */
57
+ name = "FlightTimeoutError";
58
+ /** Timeout duration in milliseconds that was exceeded. */
59
+ timeoutMs;
60
+ constructor(message, timeoutMs, cause) {
61
+ super(message, cause);
62
+ this.timeoutMs = timeoutMs;
63
+ }
64
+ /**
65
+ * Type guard to check if an error is a FlightTimeoutError.
66
+ */
67
+ static isError(error) {
68
+ return error instanceof FlightTimeoutError;
69
+ }
70
+ }
71
+ /**
72
+ * Error thrown when the server returns an error response.
73
+ */
74
+ export class FlightServerError extends FlightError {
75
+ /** Error name for identification. */
76
+ name = "FlightServerError";
77
+ /** gRPC status code from the server. */
78
+ code;
79
+ /** Additional error details from the server, if provided. */
80
+ details;
81
+ constructor(message, code, details, cause) {
82
+ super(message, cause);
83
+ this.code = code;
84
+ this.details = details;
85
+ }
86
+ /**
87
+ * Type guard to check if an error is a FlightServerError.
88
+ */
89
+ static isError(error) {
90
+ return error instanceof FlightServerError;
91
+ }
92
+ }
93
+ /**
94
+ * Error thrown when an operation is cancelled.
95
+ */
96
+ export class FlightCancelledError extends FlightError {
97
+ /** Error name for identification. */
98
+ name = "FlightCancelledError";
99
+ /**
100
+ * Type guard to check if an error is a FlightCancelledError.
101
+ */
102
+ static isError(error) {
103
+ return error instanceof FlightCancelledError;
104
+ }
105
+ }
106
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/client/errors.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,WAAY,SAAQ,KAAK;IACpC,qCAAqC;IACnB,IAAI,GAAW,aAAa,CAAA;IAC9C,qDAAqD;IACnC,KAAK,CAAS;IAEhC,YAAY,OAAe,EAAE,KAAe;QAC1C,KAAK,CAAC,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;QACzB,IAAI,CAAC,KAAK,GAAG,KAAK,CAAA;IACpB,CAAC;IAED;;OAEG;IACH,MAAM,CAAC,OAAO,CAAC,KAAc;QAC3B,OAAO,KAAK,YAAY,WAAW,CAAA;IACrC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,qBAAsB,SAAQ,WAAW;IACpD,qCAAqC;IACnB,IAAI,GAAW,uBAAuB,CAAA;IACxD,kCAAkC;IACzB,GAAG,CAAQ;IAEpB,YAAY,OAAe,EAAE,GAAW,EAAE,KAAe;QACvD,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACrB,IAAI,CAAC,GAAG,GAAG,GAAG,CAAA;IAChB,CAAC;IAED;;OAEG;IACH,MAAM,CAAU,OAAO,CAAC,KAAc;QACpC,OAAO,KAAK,YAAY,qBAAqB,CAAA;IAC/C,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,eAAgB,SAAQ,WAAW;IAC9C,qCAAqC;IACnB,IAAI,GAAW,iBAAiB,CAAA;IAElD;;OAEG;IACH,MAAM,CAAU,OAAO,CAAC,KAAc;QACpC,OAAO,KAAK,YAAY,eAAe,CAAA;IACzC,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,WAAW;IACjD,qCAAqC;IACnB,IAAI,GAAW,oBAAoB,CAAA;IACrD,0DAA0D;IACjD,SAAS,CAAQ;IAE1B,YAAY,OAAe,EAAE,SAAiB,EAAE,KAAe;QAC7D,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACrB,IAAI,CAAC,SAAS,GAAG,SAAS,CAAA;IAC5B,CAAC;IAED;;OAEG;IACH,MAAM,CAAU,OAAO,CAAC,KAAc;QACpC,OAAO,KAAK,YAAY,kBAAkB,CAAA;IAC5C,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,iBAAkB,SAAQ,WAAW;IAChD,qCAAqC;IACnB,IAAI,GAAW,mBAAmB,CAAA;IACpD,wCAAwC;IAC/B,IAAI,CAAQ;IACrB,6DAA6D;IACpD,OAAO,CAAoB;IAEpC,YAAY,OAAe,EAAE,IAAY,EAAE,OAAgB,EAAE,KAAe;QAC1E,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAA;QACrB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;QAChB,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;IACxB,CAAC;IAED;;OAEG;IACH,MAAM,CAAU,OAAO,CAAC,KAAc;QACpC,OAAO,KAAK,YAAY,iBAAiB,CAAA;IAC3C,CAAC;CACF;AAED;;GAEG;AACH,MAAM,OAAO,oBAAqB,SAAQ,WAAW;IACnD,qCAAqC;IACnB,IAAI,GAAW,sBAAsB,CAAA;IAEvD;;OAEG;IACH,MAAM,CAAU,OAAO,CAAC,KAAc;QACpC,OAAO,KAAK,YAAY,oBAAoB,CAAA;IAC9C,CAAC;CACF"}
@@ -0,0 +1,161 @@
1
+ import { type ActionType, type FlightData, type FlightInfo, type PollInfo, type PutResult, type Result, type SchemaResult } from "../gen/arrow/flight/Flight_pb.js";
2
+ import { type FlightAction, type FlightClientOptions, type FlightCriteria, type FlightDescriptorInput, type FlightTicket } from "./types.js";
3
+ /**
4
+ * Low-level Arrow Flight client for communicating with Flight servers.
5
+ *
6
+ * This client provides access to all core Flight RPC methods.
7
+ * For SQL operations, use `FlightSqlClient` instead.
8
+ *
9
+ * @example
10
+ * ```ts
11
+ * const client = new FlightClient({ url: "https://flight.example.com:8815" })
12
+ *
13
+ * const info = await client.getFlightInfo({ type: "cmd", cmd: myCommand })
14
+ * for await (const flight of client.listFlights()) {
15
+ * console.log(flight)
16
+ * }
17
+ *
18
+ * client.close()
19
+ * ```
20
+ */
21
+ export declare class FlightClient {
22
+ #private;
23
+ constructor(options: FlightClientOptions);
24
+ /**
25
+ * The base URL of the Flight server.
26
+ */
27
+ get url(): string;
28
+ /**
29
+ * Whether the client has been closed.
30
+ */
31
+ get closed(): boolean;
32
+ /**
33
+ * Whether the client has been authenticated via handshake.
34
+ */
35
+ get authenticated(): boolean;
36
+ /**
37
+ * Close the client and release resources.
38
+ * After calling close, the client should not be used.
39
+ */
40
+ close(): void;
41
+ /**
42
+ * Perform Flight Handshake authentication.
43
+ *
44
+ * This method is automatically called for clients configured with `auth: { type: "basic" }`.
45
+ * For custom handshake payloads, call this method directly with raw bytes.
46
+ *
47
+ * @param payload - Raw handshake payload (defaults to BasicAuth if auth.type is "basic")
48
+ * @returns The authentication token from the server
49
+ */
50
+ handshake(payload?: Uint8Array): Promise<string>;
51
+ /**
52
+ * Authenticate with the server using configured credentials.
53
+ *
54
+ * For basic auth, this calls the Handshake RPC.
55
+ * For bearer auth, no action is needed (token is sent in headers).
56
+ *
57
+ * @returns The authentication token (if applicable)
58
+ */
59
+ authenticate(): Promise<string | undefined>;
60
+ /**
61
+ * Get information about a specific flight.
62
+ *
63
+ * @param descriptor - Flight descriptor identifying the dataset
64
+ * @returns Flight information including schema and endpoints
65
+ */
66
+ getFlightInfo(descriptor: FlightDescriptorInput): Promise<FlightInfo>;
67
+ /**
68
+ * Poll for updated flight information (useful for long-running queries).
69
+ *
70
+ * @param descriptor - Flight descriptor identifying the dataset
71
+ * @returns Poll information with progress and updated flight info
72
+ */
73
+ pollFlightInfo(descriptor: FlightDescriptorInput): Promise<PollInfo>;
74
+ /**
75
+ * Get the schema for a flight.
76
+ *
77
+ * @param descriptor - Flight descriptor identifying the dataset
78
+ * @returns Schema result containing the Arrow schema bytes
79
+ */
80
+ getSchema(descriptor: FlightDescriptorInput): Promise<SchemaResult>;
81
+ /**
82
+ * List available flights matching the given criteria.
83
+ *
84
+ * @param criteria - Optional filter criteria for listing flights
85
+ * @yields FlightInfo for each matching flight
86
+ */
87
+ listFlights(criteria?: FlightCriteria): AsyncIterable<FlightInfo>;
88
+ /**
89
+ * List available actions supported by the server.
90
+ *
91
+ * @yields ActionType describing each available action
92
+ */
93
+ listActions(): AsyncIterable<ActionType>;
94
+ /**
95
+ * Execute a custom action on the server.
96
+ *
97
+ * @param action - The action to execute (type and optional body)
98
+ * @yields Result messages from the server
99
+ */
100
+ doAction(action: FlightAction): AsyncIterable<Result>;
101
+ /**
102
+ * Retrieve flight data for the given ticket.
103
+ * Returns an async iterable of FlightData messages.
104
+ *
105
+ * Use the IPC decoding utilities to convert FlightData to Arrow RecordBatches:
106
+ * - `decodeFlightDataStream()` - decode to RecordBatch stream
107
+ * - `decodeFlightDataToTable()` - decode to a single Table
108
+ *
109
+ * @param ticket - The ticket identifying the data to retrieve
110
+ * @yields FlightData messages containing Arrow IPC data
111
+ *
112
+ * @example
113
+ * ```ts
114
+ * import { decodeFlightDataStream } from "@qualithm/arrow-flight-client"
115
+ *
116
+ * const stream = client.doGet(ticket)
117
+ * for await (const batch of decodeFlightDataStream(stream)) {
118
+ * console.log(`Received batch with ${batch.numRows} rows`)
119
+ * }
120
+ * ```
121
+ */
122
+ doGet(ticket: FlightTicket): AsyncIterable<FlightData>;
123
+ /**
124
+ * Upload data to the server.
125
+ * Returns an async iterable of PutResult messages containing server acknowledgements.
126
+ *
127
+ * Use the IPC encoding utilities to create FlightData from Arrow data:
128
+ * - `encodeRecordBatchesToFlightData()` - encode RecordBatch stream
129
+ * - `encodeTableToFlightData()` - encode a Table
130
+ *
131
+ * @param data - Async iterable of FlightData messages to upload (include descriptor in first message)
132
+ * @yields PutResult messages from the server
133
+ *
134
+ * @example
135
+ * ```ts
136
+ * import { encodeTableToFlightData } from "@qualithm/arrow-flight-client"
137
+ *
138
+ * const descriptor = { type: "path", path: ["my", "table"] }
139
+ * const flightData = encodeTableToFlightData(table)
140
+ *
141
+ * // Add descriptor to first message
142
+ * async function* withDescriptor() {
143
+ * let first = true
144
+ * for await (const data of flightData) {
145
+ * if (first) {
146
+ * yield { ...data, flightDescriptor: descriptor }
147
+ * first = false
148
+ * } else {
149
+ * yield data
150
+ * }
151
+ * }
152
+ * }
153
+ *
154
+ * for await (const result of client.doPut(withDescriptor())) {
155
+ * console.log("Server acknowledged:", result.appMetadata)
156
+ * }
157
+ * ```
158
+ */
159
+ doPut(data: AsyncIterable<FlightData>): AsyncIterable<PutResult>;
160
+ }
161
+ //# sourceMappingURL=flight-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flight-client.d.ts","sourceRoot":"","sources":["../../src/client/flight-client.ts"],"names":[],"mappings":"AAOA,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,UAAU,EAEf,KAAK,UAAU,EAGf,KAAK,QAAQ,EACb,KAAK,SAAS,EACd,KAAK,MAAM,EACX,KAAK,YAAY,EAClB,MAAM,kCAAkC,CAAA;AAEzC,OAAO,EACL,KAAK,YAAY,EACjB,KAAK,mBAAmB,EACxB,KAAK,cAAc,EACnB,KAAK,qBAAqB,EAC1B,KAAK,YAAY,EAGlB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;;;;;GAiBG;AACH,qBAAa,YAAY;;gBAQX,OAAO,EAAE,mBAAmB;IAcxC;;OAEG;IACH,IAAI,GAAG,IAAI,MAAM,CAEhB;IAED;;OAEG;IACH,IAAI,MAAM,IAAI,OAAO,CAEpB;IAED;;OAEG;IACH,IAAI,aAAa,IAAI,OAAO,CAE3B;IAED;;;OAGG;IACH,KAAK,IAAI,IAAI;IAIb;;;;;;;;OAQG;IACG,SAAS,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,OAAO,CAAC,MAAM,CAAC;IA6DtD;;;;;;;OAOG;IACG,YAAY,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAgBjD;;;;;OAKG;IACG,aAAa,CAAC,UAAU,EAAE,qBAAqB,GAAG,OAAO,CAAC,UAAU,CAAC;IAW3E;;;;;OAKG;IACG,cAAc,CAAC,UAAU,EAAE,qBAAqB,GAAG,OAAO,CAAC,QAAQ,CAAC;IAW1E;;;;;OAKG;IACG,SAAS,CAAC,UAAU,EAAE,qBAAqB,GAAG,OAAO,CAAC,YAAY,CAAC;IAWzE;;;;;OAKG;IACI,WAAW,CAAC,QAAQ,CAAC,EAAE,cAAc,GAAG,aAAa,CAAC,UAAU,CAAC;IAexE;;;;OAIG;IACI,WAAW,IAAI,aAAa,CAAC,UAAU,CAAC;IAY/C;;;;;OAKG;IACI,QAAQ,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,MAAM,CAAC;IAe5D;;;;;;;;;;;;;;;;;;;;OAoBG;IACI,KAAK,CAAC,MAAM,EAAE,YAAY,GAAG,aAAa,CAAC,UAAU,CAAC;IAc7D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAmCG;IACI,KAAK,CAAC,IAAI,EAAE,aAAa,CAAC,UAAU,CAAC,GAAG,aAAa,CAAC,SAAS,CAAC;CA4GxE"}
@@ -0,0 +1,403 @@
1
+ /* eslint-disable @typescript-eslint/no-unsafe-return, @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-unsafe-member-access */
2
+ // Disabled due to generated proto types using @ts-nocheck
3
+ import { create, toBinary } from "@bufbuild/protobuf";
4
+ import { createClient } from "@connectrpc/connect";
5
+ import { createGrpcTransport } from "@connectrpc/connect-node";
6
+ import { BasicAuthSchema, FlightService } from "../gen/arrow/flight/Flight_pb.js";
7
+ import { FlightAuthError, FlightConnectionError, FlightError, FlightServerError } from "./errors.js";
8
+ import { resolveOptions } from "./types.js";
9
+ /**
10
+ * Low-level Arrow Flight client for communicating with Flight servers.
11
+ *
12
+ * This client provides access to all core Flight RPC methods.
13
+ * For SQL operations, use `FlightSqlClient` instead.
14
+ *
15
+ * @example
16
+ * ```ts
17
+ * const client = new FlightClient({ url: "https://flight.example.com:8815" })
18
+ *
19
+ * const info = await client.getFlightInfo({ type: "cmd", cmd: myCommand })
20
+ * for await (const flight of client.listFlights()) {
21
+ * console.log(flight)
22
+ * }
23
+ *
24
+ * client.close()
25
+ * ```
26
+ */
27
+ export class FlightClient {
28
+ #options;
29
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any -- Generated proto types use @ts-nocheck
30
+ #client;
31
+ #closed = false;
32
+ #authenticated = false;
33
+ #authToken;
34
+ constructor(options) {
35
+ this.#options = resolveOptions(options);
36
+ // Build node options with TLS configuration if provided
37
+ const nodeOptions = this.#buildNodeOptions();
38
+ const transport = createGrpcTransport({
39
+ baseUrl: this.#options.url,
40
+ nodeOptions
41
+ });
42
+ this.#client = createClient(FlightService, transport);
43
+ }
44
+ /**
45
+ * The base URL of the Flight server.
46
+ */
47
+ get url() {
48
+ return this.#options.url;
49
+ }
50
+ /**
51
+ * Whether the client has been closed.
52
+ */
53
+ get closed() {
54
+ return this.#closed;
55
+ }
56
+ /**
57
+ * Whether the client has been authenticated via handshake.
58
+ */
59
+ get authenticated() {
60
+ return this.#authenticated;
61
+ }
62
+ /**
63
+ * Close the client and release resources.
64
+ * After calling close, the client should not be used.
65
+ */
66
+ close() {
67
+ this.#closed = true;
68
+ }
69
+ /**
70
+ * Perform Flight Handshake authentication.
71
+ *
72
+ * This method is automatically called for clients configured with `auth: { type: "basic" }`.
73
+ * For custom handshake payloads, call this method directly with raw bytes.
74
+ *
75
+ * @param payload - Raw handshake payload (defaults to BasicAuth if auth.type is "basic")
76
+ * @returns The authentication token from the server
77
+ */
78
+ async handshake(payload) {
79
+ this.#assertOpen();
80
+ // Use provided payload or build from basic auth credentials
81
+ let handshakePayload = payload;
82
+ if (!handshakePayload && this.#options.auth?.type === "basic") {
83
+ const basicAuth = create(BasicAuthSchema, {
84
+ username: this.#options.auth.credentials.username,
85
+ password: this.#options.auth.credentials.password
86
+ });
87
+ handshakePayload = toBinary(BasicAuthSchema, basicAuth);
88
+ }
89
+ if (!handshakePayload) {
90
+ throw new FlightError("no handshake payload provided and no basic auth credentials configured");
91
+ }
92
+ try {
93
+ // Create async iterable with single handshake request
94
+ // eslint-disable-next-line @typescript-eslint/require-await
95
+ const requests = async function* () {
96
+ yield {
97
+ protocolVersion: 0n,
98
+ payload: handshakePayload
99
+ };
100
+ };
101
+ const stream = this.#client.handshake(requests(), {
102
+ headers: this.#getRequestHeaders()
103
+ });
104
+ let response;
105
+ for await (const msg of stream) {
106
+ response = msg;
107
+ break; // Only need first response
108
+ }
109
+ if (!response) {
110
+ throw new FlightAuthError("handshake failed: no response from server");
111
+ }
112
+ // Extract token from response payload (typically Bearer token)
113
+ const token = new TextDecoder().decode(response.payload);
114
+ this.#authToken = token;
115
+ this.#authenticated = true;
116
+ return token;
117
+ }
118
+ catch (error) {
119
+ if (FlightError.isError(error)) {
120
+ throw error;
121
+ }
122
+ throw this.#wrapError(error, "handshake");
123
+ }
124
+ }
125
+ /**
126
+ * Authenticate with the server using configured credentials.
127
+ *
128
+ * For basic auth, this calls the Handshake RPC.
129
+ * For bearer auth, no action is needed (token is sent in headers).
130
+ *
131
+ * @returns The authentication token (if applicable)
132
+ */
133
+ async authenticate() {
134
+ this.#assertOpen();
135
+ if (this.#options.auth?.type === "basic") {
136
+ return this.handshake();
137
+ }
138
+ if (this.#options.auth?.type === "bearer") {
139
+ this.#authenticated = true;
140
+ return this.#options.auth.token;
141
+ }
142
+ // No auth configured
143
+ return undefined;
144
+ }
145
+ /**
146
+ * Get information about a specific flight.
147
+ *
148
+ * @param descriptor - Flight descriptor identifying the dataset
149
+ * @returns Flight information including schema and endpoints
150
+ */
151
+ async getFlightInfo(descriptor) {
152
+ this.#assertOpen();
153
+ try {
154
+ return await this.#client.getFlightInfo(this.#toFlightDescriptor(descriptor), {
155
+ headers: this.#getRequestHeaders()
156
+ });
157
+ }
158
+ catch (error) {
159
+ throw this.#wrapError(error, "getFlightInfo");
160
+ }
161
+ }
162
+ /**
163
+ * Poll for updated flight information (useful for long-running queries).
164
+ *
165
+ * @param descriptor - Flight descriptor identifying the dataset
166
+ * @returns Poll information with progress and updated flight info
167
+ */
168
+ async pollFlightInfo(descriptor) {
169
+ this.#assertOpen();
170
+ try {
171
+ return await this.#client.pollFlightInfo(this.#toFlightDescriptor(descriptor), {
172
+ headers: this.#getRequestHeaders()
173
+ });
174
+ }
175
+ catch (error) {
176
+ throw this.#wrapError(error, "pollFlightInfo");
177
+ }
178
+ }
179
+ /**
180
+ * Get the schema for a flight.
181
+ *
182
+ * @param descriptor - Flight descriptor identifying the dataset
183
+ * @returns Schema result containing the Arrow schema bytes
184
+ */
185
+ async getSchema(descriptor) {
186
+ this.#assertOpen();
187
+ try {
188
+ return await this.#client.getSchema(this.#toFlightDescriptor(descriptor), {
189
+ headers: this.#getRequestHeaders()
190
+ });
191
+ }
192
+ catch (error) {
193
+ throw this.#wrapError(error, "getSchema");
194
+ }
195
+ }
196
+ /**
197
+ * List available flights matching the given criteria.
198
+ *
199
+ * @param criteria - Optional filter criteria for listing flights
200
+ * @yields FlightInfo for each matching flight
201
+ */
202
+ async *listFlights(criteria) {
203
+ this.#assertOpen();
204
+ try {
205
+ const stream = this.#client.listFlights({ expression: criteria?.expression ?? new Uint8Array() }, { headers: this.#getRequestHeaders() });
206
+ for await (const info of stream) {
207
+ yield info;
208
+ }
209
+ }
210
+ catch (error) {
211
+ throw this.#wrapError(error, "listFlights");
212
+ }
213
+ }
214
+ /**
215
+ * List available actions supported by the server.
216
+ *
217
+ * @yields ActionType describing each available action
218
+ */
219
+ async *listActions() {
220
+ this.#assertOpen();
221
+ try {
222
+ const stream = this.#client.listActions({}, { headers: this.#getRequestHeaders() });
223
+ for await (const action of stream) {
224
+ yield action;
225
+ }
226
+ }
227
+ catch (error) {
228
+ throw this.#wrapError(error, "listActions");
229
+ }
230
+ }
231
+ /**
232
+ * Execute a custom action on the server.
233
+ *
234
+ * @param action - The action to execute (type and optional body)
235
+ * @yields Result messages from the server
236
+ */
237
+ async *doAction(action) {
238
+ this.#assertOpen();
239
+ try {
240
+ const stream = this.#client.doAction({ type: action.type, body: action.body ?? new Uint8Array() }, { headers: this.#getRequestHeaders() });
241
+ for await (const result of stream) {
242
+ yield result;
243
+ }
244
+ }
245
+ catch (error) {
246
+ throw this.#wrapError(error, "doAction");
247
+ }
248
+ }
249
+ /**
250
+ * Retrieve flight data for the given ticket.
251
+ * Returns an async iterable of FlightData messages.
252
+ *
253
+ * Use the IPC decoding utilities to convert FlightData to Arrow RecordBatches:
254
+ * - `decodeFlightDataStream()` - decode to RecordBatch stream
255
+ * - `decodeFlightDataToTable()` - decode to a single Table
256
+ *
257
+ * @param ticket - The ticket identifying the data to retrieve
258
+ * @yields FlightData messages containing Arrow IPC data
259
+ *
260
+ * @example
261
+ * ```ts
262
+ * import { decodeFlightDataStream } from "@qualithm/arrow-flight-client"
263
+ *
264
+ * const stream = client.doGet(ticket)
265
+ * for await (const batch of decodeFlightDataStream(stream)) {
266
+ * console.log(`Received batch with ${batch.numRows} rows`)
267
+ * }
268
+ * ```
269
+ */
270
+ async *doGet(ticket) {
271
+ this.#assertOpen();
272
+ try {
273
+ const stream = this.#client.doGet(ticket, {
274
+ headers: this.#getRequestHeaders()
275
+ });
276
+ for await (const data of stream) {
277
+ yield data;
278
+ }
279
+ }
280
+ catch (error) {
281
+ throw this.#wrapError(error, "doGet");
282
+ }
283
+ }
284
+ /**
285
+ * Upload data to the server.
286
+ * Returns an async iterable of PutResult messages containing server acknowledgements.
287
+ *
288
+ * Use the IPC encoding utilities to create FlightData from Arrow data:
289
+ * - `encodeRecordBatchesToFlightData()` - encode RecordBatch stream
290
+ * - `encodeTableToFlightData()` - encode a Table
291
+ *
292
+ * @param data - Async iterable of FlightData messages to upload (include descriptor in first message)
293
+ * @yields PutResult messages from the server
294
+ *
295
+ * @example
296
+ * ```ts
297
+ * import { encodeTableToFlightData } from "@qualithm/arrow-flight-client"
298
+ *
299
+ * const descriptor = { type: "path", path: ["my", "table"] }
300
+ * const flightData = encodeTableToFlightData(table)
301
+ *
302
+ * // Add descriptor to first message
303
+ * async function* withDescriptor() {
304
+ * let first = true
305
+ * for await (const data of flightData) {
306
+ * if (first) {
307
+ * yield { ...data, flightDescriptor: descriptor }
308
+ * first = false
309
+ * } else {
310
+ * yield data
311
+ * }
312
+ * }
313
+ * }
314
+ *
315
+ * for await (const result of client.doPut(withDescriptor())) {
316
+ * console.log("Server acknowledged:", result.appMetadata)
317
+ * }
318
+ * ```
319
+ */
320
+ async *doPut(data) {
321
+ this.#assertOpen();
322
+ try {
323
+ const stream = this.#client.doPut(data, {
324
+ headers: this.#getRequestHeaders()
325
+ });
326
+ for await (const result of stream) {
327
+ yield result;
328
+ }
329
+ }
330
+ catch (error) {
331
+ throw this.#wrapError(error, "doPut");
332
+ }
333
+ }
334
+ // ── Private helpers ──────────────────────────────────────────────────
335
+ /** Throws if the client has been closed. */
336
+ #assertOpen() {
337
+ if (this.#closed) {
338
+ throw new FlightError("client is closed");
339
+ }
340
+ }
341
+ /** Converts a FlightDescriptorInput to the proto FlightDescriptor format. */
342
+ #toFlightDescriptor(input) {
343
+ if (input.type === "path") {
344
+ return { type: 1, path: input.path }; // PATH = 1
345
+ }
346
+ return { type: 2, cmd: input.cmd }; // CMD = 2
347
+ }
348
+ /** Builds Node.js HTTP/2 options including TLS configuration. */
349
+ #buildNodeOptions() {
350
+ const nodeOptions = { ...this.#options.nodeOptions };
351
+ // Apply TLS options if configured
352
+ if (this.#options.tls) {
353
+ const { tls } = this.#options;
354
+ if (tls.cert !== undefined) {
355
+ nodeOptions.cert = tls.cert;
356
+ }
357
+ if (tls.key !== undefined) {
358
+ nodeOptions.key = tls.key;
359
+ }
360
+ if (tls.ca !== undefined) {
361
+ nodeOptions.ca = tls.ca;
362
+ }
363
+ if (tls.passphrase !== undefined && tls.passphrase !== "") {
364
+ nodeOptions.passphrase = tls.passphrase;
365
+ }
366
+ if (tls.rejectUnauthorized !== undefined) {
367
+ nodeOptions.rejectUnauthorized = tls.rejectUnauthorized;
368
+ }
369
+ }
370
+ return nodeOptions;
371
+ }
372
+ /** Returns headers for requests, including auth token if authenticated. */
373
+ #getRequestHeaders() {
374
+ const headers = { ...this.#options.headers };
375
+ // Add auth token if authenticated via handshake
376
+ if (this.#authToken !== undefined && this.#authToken !== "") {
377
+ headers.Authorization = `Bearer ${this.#authToken}`;
378
+ }
379
+ return headers;
380
+ }
381
+ /** Wraps errors in appropriate FlightError subclasses based on error type. */
382
+ #wrapError(error, operation) {
383
+ if (FlightError.isError(error)) {
384
+ return error;
385
+ }
386
+ // Handle ConnectRPC errors
387
+ if (error instanceof Error && "code" in error) {
388
+ const connectError = error;
389
+ // Check for authentication-related errors
390
+ if (connectError.code === "UNAUTHENTICATED" || connectError.code === "PERMISSION_DENIED") {
391
+ return new FlightAuthError(`${operation} failed: ${connectError.message}`, error);
392
+ }
393
+ return new FlightServerError(`${operation} failed: ${connectError.message}`, connectError.code, connectError.rawMessage, error);
394
+ }
395
+ // Handle connection errors
396
+ if (error instanceof Error && error.message.includes("ECONNREFUSED")) {
397
+ return new FlightConnectionError(`failed to connect to ${this.#options.url}`, this.#options.url, error);
398
+ }
399
+ // Generic error wrapping
400
+ return new FlightError(`${operation} failed: ${error instanceof Error ? error.message : String(error)}`, error);
401
+ }
402
+ }
403
+ //# sourceMappingURL=flight-client.js.map