snowpipe-streaming 1.4.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.
package/src/client.js ADDED
@@ -0,0 +1,216 @@
1
+ // Copyright 2026 Snowflake Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ "use strict";
17
+
18
+ const { callFFI, StreamingIngestError } = require("./errors.js");
19
+ const { ChannelStatus } = require("./channel_status.js");
20
+ const { StreamingIngestChannel } = require("./channel.js");
21
+
22
+ /**
23
+ * Streaming ingest client for creating channels to ingest data into Snowflake.
24
+ *
25
+ * Use {@link createClient} to create an instance — the constructor is not public.
26
+ *
27
+ * @example
28
+ * const client = await createClient({
29
+ * clientName: "my-client",
30
+ * dbName: "MY_DB",
31
+ * schemaName: "MY_SCHEMA",
32
+ * pipeName: "MY_PIPE",
33
+ * properties: {
34
+ * account: "myaccount",
35
+ * user: "myuser",
36
+ * private_key: "-----BEGIN PRIVATE KEY-----\n...",
37
+ * },
38
+ * });
39
+ *
40
+ * const { channel, status } = await client.openChannel({ name: "ch1" });
41
+ * // ... insert rows via channel ...
42
+ * await client.close();
43
+ */
44
+ class StreamingIngestClient {
45
+ /** @param {object} nativeClient - Internal JsClient from FFI */
46
+ constructor(nativeClient) {
47
+ this._native = nativeClient;
48
+ }
49
+
50
+ /** @returns {string} The client name */
51
+ get clientName() {
52
+ return this._native.clientName;
53
+ }
54
+
55
+ /** @returns {string} The database name */
56
+ get dbName() {
57
+ return this._native.dbName;
58
+ }
59
+
60
+ /** @returns {string} The schema name */
61
+ get schemaName() {
62
+ return this._native.schemaName;
63
+ }
64
+
65
+ /** @returns {string} The pipe name */
66
+ get pipeName() {
67
+ return this._native.pipeName;
68
+ }
69
+
70
+ /** @returns {boolean} True if the client is closed */
71
+ get isClosed() {
72
+ return this._native.isClosed;
73
+ }
74
+
75
+ /**
76
+ * Get the binary input format for this client.
77
+ * @returns {string} One of: "BASE64", "HEX", "UTF-8"
78
+ */
79
+ get binaryInputFormat() {
80
+ return this._native.binaryInputFormat;
81
+ }
82
+
83
+ /**
84
+ * Close the client.
85
+ * @param {{waitForFlush?: boolean, timeoutMs?: number}} [options]
86
+ * @returns {Promise<void>}
87
+ */
88
+ async close(options) {
89
+ const nativeOpts = options
90
+ ? { waitForFlush: options.waitForFlush, timeoutMs: options.timeoutMs }
91
+ : undefined;
92
+ return callFFI(() => this._native.close(nativeOpts));
93
+ }
94
+
95
+ /**
96
+ * Open a channel for streaming data.
97
+ * @param {{name: string, offsetToken?: string}} options
98
+ * @returns {Promise<{channel: StreamingIngestChannel, status: ChannelStatus}>}
99
+ */
100
+ async openChannel(options) {
101
+ const nativeChannel = await callFFI(() =>
102
+ this._native.openChannel(options.name, options.offsetToken),
103
+ );
104
+ const rawStatus = nativeChannel.channelStatus;
105
+ return {
106
+ channel: new StreamingIngestChannel(nativeChannel),
107
+ status: rawStatus ? new ChannelStatus(rawStatus) : null,
108
+ };
109
+ }
110
+
111
+ /**
112
+ * Drop a channel by name (server-side removal).
113
+ * @param {string} channelName
114
+ * @returns {Promise<void>}
115
+ */
116
+ async dropChannel(channelName) {
117
+ return callFFI(() => this._native.dropChannel(channelName));
118
+ }
119
+
120
+ /**
121
+ * Get the status of multiple channels.
122
+ * @param {string[]} channelNames
123
+ * @returns {Promise<Object.<string, ChannelStatus>>}
124
+ */
125
+ async getChannelStatuses(channelNames) {
126
+ const raw = await callFFI(() => this._native.getChannelStatuses(channelNames));
127
+ const result = {};
128
+ for (const [name, data] of Object.entries(raw)) {
129
+ result[name] = new ChannelStatus(data);
130
+ }
131
+ return result;
132
+ }
133
+
134
+ /**
135
+ * Get the latest committed offset tokens for multiple channels.
136
+ * @param {string[]} channelNames
137
+ * @returns {Promise<Object.<string, string|null>>}
138
+ */
139
+ async getLatestCommittedOffsetTokens(channelNames) {
140
+ const statuses = await this.getChannelStatuses(channelNames);
141
+ const result = {};
142
+ for (const [name, status] of Object.entries(statuses)) {
143
+ result[name] = status.latestCommittedOffsetToken;
144
+ }
145
+ return result;
146
+ }
147
+
148
+ /**
149
+ * Trigger a flush of all channels (non-blocking).
150
+ * @throws {StreamingIngestError} If the flush cannot be initiated
151
+ */
152
+ initiateFlush() {
153
+ try {
154
+ this._native.initiateFlush();
155
+ } catch (e) {
156
+ throw StreamingIngestError.from(e);
157
+ }
158
+ }
159
+
160
+ /**
161
+ * Wait for all channels to flush.
162
+ * @param {{timeoutMs?: number}} [options]
163
+ * @returns {Promise<void>}
164
+ */
165
+ async waitForFlush(options) {
166
+ return callFFI(() => this._native.waitForFlush(options?.timeoutMs));
167
+ }
168
+
169
+ /** Support `await using client = ...` (Node 20+) */
170
+ async [Symbol.asyncDispose]() {
171
+ if (!this.isClosed) {
172
+ await this.close();
173
+ }
174
+ }
175
+ }
176
+
177
+ /**
178
+ * Create a new streaming ingest client.
179
+ *
180
+ * @param {object} native - The native addon instance (provided by index.js)
181
+ * @param {{clientName: string, dbName: string, schemaName: string, pipeName: string, profilePath?: string, properties?: Object.<string, string>}} options
182
+ * @returns {Promise<StreamingIngestClient>}
183
+ */
184
+ async function createClient(native, options) {
185
+ // Coerce property values to strings (matching Python)
186
+ let properties = options.properties;
187
+ if (properties) {
188
+ properties = Object.fromEntries(Object.entries(properties).map(([k, v]) => [k, String(v)]));
189
+ }
190
+
191
+ // Windows workaround: SNOW-2345722
192
+ if (process.platform === "win32") {
193
+ properties = properties || {};
194
+ if (!("mem_throttle_by_process" in properties)) {
195
+ properties.mem_throttle_by_process = "false";
196
+ }
197
+ }
198
+
199
+ const nativeClient = await callFFI(() =>
200
+ native.createClient({
201
+ clientName: options.clientName,
202
+ dbName: options.dbName,
203
+ schemaName: options.schemaName,
204
+ pipeName: options.pipeName,
205
+ profilePath: options.profilePath,
206
+ properties,
207
+ }),
208
+ );
209
+
210
+ return new StreamingIngestClient(nativeClient);
211
+ }
212
+
213
+ module.exports = {
214
+ StreamingIngestClient,
215
+ createClient,
216
+ };
package/src/errors.js ADDED
@@ -0,0 +1,110 @@
1
+ // Copyright 2026 Snowflake Inc.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+ //
4
+ // Licensed under the Apache License, Version 2.0 (the "License");
5
+ // you may not use this file except in compliance with the License.
6
+ // You may obtain a copy of the License at
7
+ //
8
+ // http://www.apache.org/licenses/LICENSE-2.0
9
+ //
10
+ // Unless required by applicable law or agreed to in writing, software
11
+ // distributed under the License is distributed on an "AS IS" BASIS,
12
+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ // See the License for the specific language governing permissions and
14
+ // limitations under the License.
15
+
16
+ "use strict";
17
+
18
+ /**
19
+ * Error class for Snowpipe Streaming SDK errors.
20
+ *
21
+ * This error is thrown when the Rust core encounters an error condition.
22
+ * It includes structured error information including an error code,
23
+ * HTTP status code, and descriptive message.
24
+ *
25
+ * @example
26
+ * try {
27
+ * await client.openChannel({ name: "ch1" });
28
+ * } catch (e) {
29
+ * if (e instanceof StreamingIngestError) {
30
+ * console.log(e.errorCode, e.httpStatusCode, e.message);
31
+ * }
32
+ * }
33
+ *
34
+ * @extends Error
35
+ */
36
+ class StreamingIngestError extends Error {
37
+ /**
38
+ * Create a StreamingIngestError.
39
+ * @param {string} errorCode - The error code (e.g., "ConfigError", "ChannelNotFound")
40
+ * @param {string} message - The error message
41
+ * @param {number} httpStatusCode - HTTP status code indicating error category
42
+ * @param {string} httpStatusName - HTTP status name (e.g., "Bad Request")
43
+ */
44
+ constructor(errorCode, message, httpStatusCode, httpStatusName) {
45
+ super(message);
46
+ this.name = "StreamingIngestError";
47
+ this.errorCode = errorCode;
48
+ this.httpStatusCode = httpStatusCode;
49
+ this.httpStatusName = httpStatusName;
50
+ }
51
+
52
+ /**
53
+ * Create a StreamingIngestError from an FFI error.
54
+ *
55
+ * FFI errors from the Rust core have their message encoded as JSON with
56
+ * error_code, message, http_status_code, and http_status_name fields.
57
+ * This method parses that JSON and constructs a StreamingIngestError.
58
+ *
59
+ * @param {Error} e - The error from FFI
60
+ * @returns {StreamingIngestError|Error} Parsed error or original if not parseable
61
+ */
62
+ static from(e) {
63
+ try {
64
+ const data = JSON.parse(e.message);
65
+ if (data.error_code && data.message && data.http_status_code !== undefined) {
66
+ return new StreamingIngestError(
67
+ data.error_code,
68
+ data.message,
69
+ data.http_status_code,
70
+ data.http_status_name || "Unknown",
71
+ );
72
+ }
73
+ } catch {
74
+ // Not JSON, return original
75
+ }
76
+ return e;
77
+ }
78
+
79
+ /**
80
+ * Returns a string representation of the error.
81
+ * @returns {string} Formatted error string
82
+ */
83
+ toString() {
84
+ return `${this.errorCode}: ${this.message} (HTTP ${this.httpStatusCode} ${this.httpStatusName})`;
85
+ }
86
+ }
87
+
88
+ /**
89
+ * Wrap an FFI call with error conversion.
90
+ *
91
+ * Equivalent to Python's @_rethrow_ffi_errors decorator.
92
+ * Catches napi errors, parses the JSON message, and throws
93
+ * a StreamingIngestError.
94
+ *
95
+ * @param {Function} fn - The FFI function to call
96
+ * @returns {Promise<*>} The result of the FFI call
97
+ * @throws {StreamingIngestError} If the FFI call fails
98
+ */
99
+ async function callFFI(fn) {
100
+ try {
101
+ return await fn();
102
+ } catch (e) {
103
+ throw StreamingIngestError.from(e);
104
+ }
105
+ }
106
+
107
+ module.exports = {
108
+ StreamingIngestError,
109
+ callFFI,
110
+ };
package/src/index.d.ts ADDED
@@ -0,0 +1,351 @@
1
+ /*
2
+ * Copyright 2026 Snowflake Inc.
3
+ * SPDX-License-Identifier: Apache-2.0
4
+ *
5
+ * Licensed under the Apache License, Version 2.0 (the "License");
6
+ * you may not use this file except in compliance with the License.
7
+ * You may obtain a copy of the License at
8
+ *
9
+ * http://www.apache.org/licenses/LICENSE-2.0
10
+ *
11
+ * Unless required by applicable law or agreed to in writing, software
12
+ * distributed under the License is distributed on an "AS IS" BASIS,
13
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
+ * See the License for the specific language governing permissions and
15
+ * limitations under the License.
16
+ */
17
+
18
+ //
19
+ // TypeScript declaration file for the Snowpipe Streaming Node.js SDK.
20
+ //
21
+ // This file serves two purposes:
22
+ // 1. Provides TypeScript/JavaScript users with autocomplete and type checking
23
+ // 2. Used by TypeDoc (see docs/typedoc.json) to generate API reference HTML
24
+ //
25
+ // This file mirrors the public API surface defined in src/*.js.
26
+ // The CI lint step (`npm run lint`) runs check-types-sync.js which verifies
27
+ // that this file stays consistent with the JavaScript source.
28
+ //
29
+
30
+ /**
31
+ * Create a new streaming ingest client. Each client is tied to a single
32
+ * database, schema, and pipe. To ingest into multiple pipes, create
33
+ * multiple clients.
34
+ *
35
+ * Either `profilePath` or `properties` must be provided for authentication.
36
+ * If both are provided, `properties` takes precedence for conflicting keys.
37
+ *
38
+ * @param options - Client configuration
39
+ * @throws StreamingIngestError If client creation fails
40
+ */
41
+ export function createClient(options: {
42
+ /** A unique name to identify this client instance, used for tracking and debugging purposes. */
43
+ clientName: string;
44
+ /** The name of the Snowflake database where data will be ingested. */
45
+ dbName: string;
46
+ /** The name of the schema within the database. */
47
+ schemaName: string;
48
+ /** The name of the pipe that will be used for streaming ingestion. */
49
+ pipeName: string;
50
+ /** Path to a JSON file containing connection properties and authentication information. */
51
+ profilePath?: string;
52
+ /** Connection properties and authentication information. Common properties include account, user, private_key, url. */
53
+ properties?: Record<string, string>;
54
+ }): Promise<StreamingIngestClient>;
55
+
56
+ /**
57
+ * A client that is the starting point for using the Streaming Ingest client APIs.
58
+ *
59
+ * A single client maps to exactly one account/database/schema/pipe in Snowflake;
60
+ * however, multiple clients can point to the same account/database/schema/pipe.
61
+ * Each client contains information for Snowflake authentication and authorization,
62
+ * and it is used to create one or more StreamingIngestChannel instances for data
63
+ * ingestion.
64
+ *
65
+ * Create instances using the `createClient()` function.
66
+ */
67
+ export class StreamingIngestClient {
68
+ private constructor();
69
+
70
+ /** The client name. */
71
+ readonly clientName: string;
72
+ /** The database name. */
73
+ readonly dbName: string;
74
+ /** The schema name. */
75
+ readonly schemaName: string;
76
+ /** The pipe name. */
77
+ readonly pipeName: string;
78
+ /** Whether the client is closed. */
79
+ readonly isClosed: boolean;
80
+ /** The binary input format. One of: "BASE64", "HEX", "UTF-8". */
81
+ readonly binaryInputFormat: string;
82
+
83
+ /**
84
+ * Close the client. If waitForFlush is true, the client waits for all data in every
85
+ * channel to be flushed to Snowflake before shutting down.
86
+ *
87
+ * @param options - Close options
88
+ * @throws StreamingIngestError If closing the client fails or the timeout is reached
89
+ */
90
+ close(options?: {
91
+ /** Whether to wait for the flush to complete, defaults to true. */
92
+ waitForFlush?: boolean;
93
+ /** Optional timeout in milliseconds for the flush operation. */
94
+ timeoutMs?: number;
95
+ }): Promise<void>;
96
+
97
+ /**
98
+ * Open a channel with the given name. The channel is opened on the
99
+ * database/schema/pipe defined by this client. If the channel already exists,
100
+ * Snowflake reuses the latest persisted offset token unless a new one is provided.
101
+ *
102
+ * @param options - Channel options
103
+ * @throws StreamingIngestError If opening the channel fails
104
+ */
105
+ openChannel(options: {
106
+ /** The name of the channel to open. */
107
+ name: string;
108
+ /** Optional offset token to set on the channel. If not provided and this reopens an existing channel, the latest persisted offset token is reused. */
109
+ offsetToken?: string;
110
+ }): Promise<{
111
+ /** The opened channel instance. */
112
+ channel: StreamingIngestChannel;
113
+ /** The channel status at the time of opening, or null if not available. */
114
+ status: ChannelStatus | null;
115
+ }>;
116
+
117
+ /**
118
+ * Drop a channel by name (server-side removal).
119
+ *
120
+ * @param channelName - Name of the channel to drop
121
+ * @throws StreamingIngestError If dropping the channel fails
122
+ */
123
+ dropChannel(channelName: string): Promise<void>;
124
+
125
+ /**
126
+ * Get the status of multiple channels. Can fetch status for channels not
127
+ * opened by this client.
128
+ *
129
+ * @param channelNames - Names of channels to query
130
+ * @throws StreamingIngestError If retrieving statuses fails
131
+ */
132
+ getChannelStatuses(channelNames: string[]): Promise<Record<string, ChannelStatus>>;
133
+
134
+ /**
135
+ * Get the latest committed offset tokens for multiple channels.
136
+ *
137
+ * @param channelNames - Names of channels to query
138
+ * @throws StreamingIngestError If retrieving offset tokens fails
139
+ */
140
+ getLatestCommittedOffsetTokens(channelNames: string[]): Promise<Record<string, string | null>>;
141
+
142
+ /**
143
+ * Initiate a flush of the client. Causes all outstanding buffered data to be
144
+ * flushed to Snowflake. Data can still be accepted by the client after calling
145
+ * this method — this is an asynchronous call that returns after the flush is
146
+ * initiated for all channels.
147
+ *
148
+ * @throws StreamingIngestError If initiating the flush fails
149
+ */
150
+ initiateFlush(): void;
151
+
152
+ /**
153
+ * Wait for all buffered data in all channels managed by this client to be flushed
154
+ * to Snowflake. This method triggers a flush of all pending data across all channels
155
+ * and waits for the flush operations to complete.
156
+ *
157
+ * @param options - Wait options
158
+ * @throws StreamingIngestError If waiting for the flush fails
159
+ * @throws Error If the timeout is reached
160
+ */
161
+ waitForFlush(options?: {
162
+ /** Optional timeout in milliseconds. */
163
+ timeoutMs?: number;
164
+ }): Promise<void>;
165
+ }
166
+
167
+ /**
168
+ * A channel for streaming data into a Snowflake table.
169
+ *
170
+ * A channel represents a logical stream of data into a single table through a pipe.
171
+ * Multiple channels can be opened on the same pipe for parallel ingestion. Each channel
172
+ * maintains its own offset token for exactly-once delivery guarantees.
173
+ *
174
+ * Create instances using `client.openChannel()`.
175
+ */
176
+ export class StreamingIngestChannel {
177
+ private constructor();
178
+
179
+ /** The channel name. */
180
+ readonly channelName: string;
181
+ /** The database name. */
182
+ readonly dbName: string;
183
+ /** The schema name. */
184
+ readonly schemaName: string;
185
+ /** The pipe name. */
186
+ readonly pipeName: string;
187
+ /** Whether the channel is closed. */
188
+ readonly isClosed: boolean;
189
+ /** The binary input format. One of: "BASE64", "HEX", "UTF-8". */
190
+ readonly binaryInputFormat: string;
191
+
192
+ /**
193
+ * Append a single row into the channel.
194
+ *
195
+ * The row is an object with keys as column names and values as column values.
196
+ * Supported value types: null, undefined, boolean, number, string, BigInt,
197
+ * Buffer, Uint8Array, Date, Array, and Object.
198
+ *
199
+ * @param row - Row data as column-name to value pairs
200
+ * @param offsetToken - Optional offset token, used to track the ingestion
201
+ * progress and replay ingestion in case of failures
202
+ * @throws StreamingIngestError If the row appending fails
203
+ */
204
+ appendRow(row: Record<string, any>, offsetToken?: string): Promise<void>;
205
+
206
+ /**
207
+ * Append multiple rows to the channel.
208
+ *
209
+ * Each row is an object with keys as column names and values as column values.
210
+ * Supported value types: null, undefined, boolean, number, string, BigInt,
211
+ * Buffer, Uint8Array, Date, Array, and Object.
212
+ *
213
+ * @param rows - Array of row objects (column-name to value pairs)
214
+ * @param startOffsetToken - Optional start offset token of the batch
215
+ * @param endOffsetToken - Optional end offset token of the batch
216
+ * @throws TypeError If rows is not an array
217
+ * @throws StreamingIngestError If the rows appending fails
218
+ */
219
+ appendRows(rows: Record<string, any>[], startOffsetToken?: string, endOffsetToken?: string): Promise<void>;
220
+
221
+ /**
222
+ * Close the channel.
223
+ *
224
+ * @param options - Close options
225
+ * @throws StreamingIngestError If closing the channel fails
226
+ */
227
+ close(options?: {
228
+ /** Whether to drop the channel on the server, defaults to false. */
229
+ drop?: boolean;
230
+ /** Whether to wait for the flush to complete, defaults to true. */
231
+ waitForFlush?: boolean;
232
+ /** Optional timeout in milliseconds for the flush operation. */
233
+ timeoutMs?: number;
234
+ }): Promise<void>;
235
+
236
+ /**
237
+ * Initiate a flush of all buffered data for this channel but do not wait for
238
+ * the flush to complete. Calls to appendRow/appendRows are still allowed on
239
+ * the channel after calling this method.
240
+ *
241
+ * @throws StreamingIngestError If the flush cannot be initiated
242
+ */
243
+ initiateFlush(): void;
244
+
245
+ /**
246
+ * Wait for all buffered data in this channel to be flushed to Snowflake.
247
+ *
248
+ * @param options - Wait options
249
+ * @throws StreamingIngestError If waiting for the flush fails
250
+ * @throws Error If the timeout is reached
251
+ */
252
+ waitForFlush(options?: {
253
+ /** Optional timeout in milliseconds. */
254
+ timeoutMs?: number;
255
+ }): Promise<void>;
256
+
257
+ /**
258
+ * Get the current status of this channel.
259
+ *
260
+ * @throws StreamingIngestError If getting the channel status fails
261
+ */
262
+ getChannelStatus(): Promise<ChannelStatus>;
263
+
264
+ /**
265
+ * Get the latest committed offset token for this channel.
266
+ *
267
+ * @returns The latest committed offset token, or null if the channel is brand new
268
+ * @throws StreamingIngestError If getting the latest committed offset token fails
269
+ */
270
+ getLatestCommittedOffsetToken(): Promise<string | null>;
271
+
272
+ /**
273
+ * Wait for a specific offset token to be committed.
274
+ *
275
+ * Snowflake commits offset tokens in batches, so the latest committed offset token
276
+ * may jump past the one you are waiting for. The tokenChecker should handle this
277
+ * case by doing a range check (greater than or equal) rather than an exact match.
278
+ *
279
+ * @param tokenChecker - A callable that receives the latest committed offset token
280
+ * (which may be null) and should return true when the wait condition is satisfied
281
+ * @param options - Wait options
282
+ * @throws StreamingIngestError If waiting for the commit fails
283
+ * @throws Error If the timeout is reached
284
+ * @throws TypeError If tokenChecker is not a function
285
+ * @throws RangeError If timeoutMs is negative
286
+ */
287
+ waitForCommit(tokenChecker: (token: string | null) => boolean, options?: {
288
+ /** Optional timeout in milliseconds. */
289
+ timeoutMs?: number;
290
+ }): Promise<void>;
291
+ }
292
+
293
+ /**
294
+ * Status information for a streaming ingest channel.
295
+ *
296
+ * Contains metadata about the channel's current state including the latest
297
+ * committed offset token, row counts, error information, and processing latency.
298
+ * Returned by `client.getChannelStatuses()` and `channel.getChannelStatus()`.
299
+ */
300
+ export class ChannelStatus {
301
+ private constructor();
302
+
303
+ /** The database name. */
304
+ readonly databaseName: string;
305
+ /** The schema name. */
306
+ readonly schemaName: string;
307
+ /** The pipe name. */
308
+ readonly pipeName: string;
309
+ /** The channel name. */
310
+ readonly channelName: string;
311
+ /** The channel status code. */
312
+ readonly statusCode: string;
313
+ /** The latest committed offset token, or null if the channel is brand new. */
314
+ readonly latestCommittedOffsetToken: string | null;
315
+ /** When the channel was created. */
316
+ readonly createdOn: Date | null;
317
+ /** Total number of rows successfully inserted. */
318
+ readonly rowsInsertedCount: number;
319
+ /** Total number of rows parsed. */
320
+ readonly rowsParsedCount: number;
321
+ /** Total number of rows that encountered errors. */
322
+ readonly rowsErrorCount: number;
323
+ /** Upper bound offset token for the last error, or null if no errors. */
324
+ readonly lastErrorOffsetTokenUpperBound: string | null;
325
+ /** The message of the last error, or null if no errors. */
326
+ readonly lastErrorMessage: string | null;
327
+ /** Timestamp of the last error, or null if no errors. */
328
+ readonly lastErrorTimestamp: Date | null;
329
+ /** Average processing latency in milliseconds, or null if not available. */
330
+ readonly serverAvgProcessingLatencyMs: number | null;
331
+ /** When the status was last refreshed from the server. */
332
+ readonly lastRefreshedOn: Date | null;
333
+ }
334
+
335
+ /**
336
+ * Error thrown by the Snowpipe Streaming SDK.
337
+ *
338
+ * Includes structured error information: an error code, HTTP status code,
339
+ * and descriptive message. Use `errorCode` and `httpStatusCode` to determine
340
+ * the appropriate recovery action.
341
+ */
342
+ export class StreamingIngestError extends Error {
343
+ /** The error code (for example, "ConfigError", "ChannelNotFound"). */
344
+ readonly errorCode: string;
345
+ /** HTTP status code indicating error category. */
346
+ readonly httpStatusCode: number;
347
+ /** HTTP status name (for example, "Bad Request"). */
348
+ readonly httpStatusName: string;
349
+ }
350
+
351
+