snowpipe-streaming 1.4.0 → 1.6.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "snowpipe-streaming",
3
- "version": "1.4.0",
3
+ "version": "1.6.0",
4
4
  "description": "Snowflake Streaming Ingest SDK for Node.js",
5
5
  "main": "src/index.js",
6
6
  "types": "src/index.d.ts",
@@ -27,11 +27,11 @@
27
27
  "typedoc": "^0.28.19"
28
28
  },
29
29
  "optionalDependencies": {
30
- "snowpipe-streaming-linux-x64-gnu": "1.4.0",
31
- "snowpipe-streaming-linux-x64-musl": "1.4.0",
32
- "snowpipe-streaming-linux-arm64-gnu": "1.4.0",
33
- "snowpipe-streaming-linux-arm64-musl": "1.4.0",
34
- "snowpipe-streaming-darwin-arm64": "1.4.0",
35
- "snowpipe-streaming-win32-x64-msvc": "1.4.0"
30
+ "snowpipe-streaming-linux-x64-gnu": "1.6.0",
31
+ "snowpipe-streaming-linux-x64-musl": "1.6.0",
32
+ "snowpipe-streaming-linux-arm64-gnu": "1.6.0",
33
+ "snowpipe-streaming-linux-arm64-musl": "1.6.0",
34
+ "snowpipe-streaming-darwin-arm64": "1.6.0",
35
+ "snowpipe-streaming-win32-x64-msvc": "1.6.0"
36
36
  }
37
37
  }
package/src/channel.js CHANGED
@@ -104,10 +104,11 @@ class StreamingIngestChannel {
104
104
  * Append a single row. Convenience wrapper for appendRows.
105
105
  * @param {Object} row - Row data as key-value pairs
106
106
  * @param {string} [offsetToken] - Offset token for this row
107
- * @returns {Promise<void>}
107
+ * @returns {void}
108
+ * @throws {StreamingIngestError} If the row appending fails
108
109
  */
109
- async appendRow(row, offsetToken) {
110
- await this.appendRows([row], offsetToken, offsetToken);
110
+ appendRow(row, offsetToken) {
111
+ this.appendRows([row], offsetToken, offsetToken);
111
112
  }
112
113
 
113
114
  /**
@@ -116,25 +117,27 @@ class StreamingIngestChannel {
116
117
  * @param {Object[]} rows - Array of row objects
117
118
  * @param {string} [startOffsetToken] - Start offset token for this batch
118
119
  * @param {string} [endOffsetToken] - End offset token for this batch
119
- * @returns {Promise<void>}
120
+ * @returns {void}
121
+ * @throws {TypeError} If rows is not an array
122
+ * @throws {StreamingIngestError} If the rows appending fails
120
123
  */
121
- async appendRows(rows, startOffsetToken, endOffsetToken) {
124
+ appendRows(rows, startOffsetToken, endOffsetToken) {
122
125
  if (!Array.isArray(rows)) {
123
126
  throw new TypeError("rows must be an array");
124
127
  }
125
- // Empty arrays are handled by the Rust core, which returns a proper InvalidRequest error
126
- if (rows.length === 0) {
127
- // Pass empty buffer to Rust core to get proper error message
128
- return callFFI(() =>
129
- this._native.appendRows(Buffer.alloc(0), 0, startOffsetToken, endOffsetToken),
130
- );
128
+ // Empty arrays are handled by the Rust core, which returns a proper InvalidRequest error.
129
+ const buffer =
130
+ rows.length === 0
131
+ ? Buffer.alloc(0)
132
+ : Buffer.from(
133
+ rows.map((r) => JSON.stringify(this._convertBytesInRow(r))).join("\n"),
134
+ "utf8",
135
+ );
136
+ try {
137
+ this._native.appendRows(buffer, rows.length, startOffsetToken, endOffsetToken);
138
+ } catch (e) {
139
+ throw StreamingIngestError.from(e);
131
140
  }
132
- // Convert binary values according to binaryInputFormat before JSON serialization
133
- const ndjson = rows.map((r) => JSON.stringify(this._convertBytesInRow(r))).join("\n");
134
- const buffer = Buffer.from(ndjson, "utf8");
135
- return callFFI(() =>
136
- this._native.appendRows(buffer, rows.length, startOffsetToken, endOffsetToken),
137
- );
138
141
  }
139
142
 
140
143
  /**
package/src/index.d.ts CHANGED
@@ -49,7 +49,9 @@ export function createClient(options: {
49
49
  pipeName: string;
50
50
  /** Path to a JSON file containing connection properties and authentication information. */
51
51
  profilePath?: string;
52
- /** Connection properties and authentication information. Common properties include account, user, private_key, url. */
52
+ /** Connection properties and authentication information. Common properties include account, user, private_key, url
53
+ * and application (partner app name for User-Agent; 1–64 visible ASCII chars (0x21–0x7E),
54
+ * excluding `(`, `)`, `\`; regex: `^[!-'*-\[-\]-~]{1,64}$`). */
53
55
  properties?: Record<string, string>;
54
56
  }): Promise<StreamingIngestClient>;
55
57
 
@@ -201,7 +203,7 @@ export class StreamingIngestChannel {
201
203
  * progress and replay ingestion in case of failures
202
204
  * @throws StreamingIngestError If the row appending fails
203
205
  */
204
- appendRow(row: Record<string, any>, offsetToken?: string): Promise<void>;
206
+ appendRow(row: Record<string, any>, offsetToken?: string): void;
205
207
 
206
208
  /**
207
209
  * Append multiple rows to the channel.
@@ -216,7 +218,7 @@ export class StreamingIngestChannel {
216
218
  * @throws TypeError If rows is not an array
217
219
  * @throws StreamingIngestError If the rows appending fails
218
220
  */
219
- appendRows(rows: Record<string, any>[], startOffsetToken?: string, endOffsetToken?: string): Promise<void>;
221
+ appendRows(rows: Record<string, any>[], startOffsetToken?: string, endOffsetToken?: string): void;
220
222
 
221
223
  /**
222
224
  * Close the channel.
@@ -347,5 +349,3 @@ export class StreamingIngestError extends Error {
347
349
  /** HTTP status name (for example, "Bad Request"). */
348
350
  readonly httpStatusName: string;
349
351
  }
350
-
351
-
package/src/loader.js CHANGED
@@ -22,6 +22,25 @@ const fs = require("node:fs");
22
22
 
23
23
  const LOG_PREFIX = "[snowpipe-streaming]";
24
24
 
25
+ // Verbose loader breadcrumbs are gated on SS_LOG_LEVEL=debug|trace, matching
26
+ // the env var the Rust core and Java/Python SDKs already use (see
27
+ // rust/src/util/bootstrap_config.rs and FFIBootstrap.java). Default: silent on
28
+ // the success path. Fall-through and error paths still write to stderr.
29
+ // Match Rust's case-sensitive lowercase comparison so the same env var value
30
+ // is interpreted identically on both sides of the FFI boundary.
31
+ const VERBOSE = ["debug", "trace"].includes(process.env.SS_LOG_LEVEL || "");
32
+
33
+ /**
34
+ * Emit a loader breadcrumb to stderr when SS_LOG_LEVEL=debug|trace.
35
+ * No-op otherwise — keeps the success path silent on both stdout and stderr.
36
+ * @param {string} msg
37
+ */
38
+ function debug(msg) {
39
+ if (VERBOSE) {
40
+ console.error(`${LOG_PREFIX} ${msg}`);
41
+ }
42
+ }
43
+
25
44
  /**
26
45
  * Platform package name mappings.
27
46
  * Maps (os, arch, libc) to the npm package suffix.
@@ -45,7 +64,7 @@ function detectLibc() {
45
64
  try {
46
65
  const libDir = fs.readdirSync("/lib");
47
66
  if (libDir.some((f) => f.startsWith("ld-musl-"))) {
48
- console.log(`${LOG_PREFIX} Detected musl libc (found /lib/ld-musl-*)`);
67
+ debug("Detected musl libc (found /lib/ld-musl-*)");
49
68
  return "musl";
50
69
  }
51
70
  } catch {
@@ -57,7 +76,7 @@ function detectLibc() {
57
76
  const { execSync } = require("node:child_process");
58
77
  const lddOutput = execSync("ldd --version 2>&1", { encoding: "utf8" });
59
78
  if (lddOutput.includes("musl")) {
60
- console.log(`${LOG_PREFIX} Detected musl libc (ldd --version)`);
79
+ debug("Detected musl libc (ldd --version)");
61
80
  return "musl";
62
81
  }
63
82
  } catch {
@@ -68,15 +87,13 @@ function detectLibc() {
68
87
  if (process.report && typeof process.report.getReport === "function") {
69
88
  const report = process.report.getReport();
70
89
  if (report?.header?.glibcVersionRuntime) {
71
- console.log(
72
- `${LOG_PREFIX} Detected glibc ${report.header.glibcVersionRuntime} (process.report)`,
73
- );
90
+ debug(`Detected glibc ${report.header.glibcVersionRuntime} (process.report)`);
74
91
  return "glibc";
75
92
  }
76
93
  }
77
94
 
78
95
  // Default to glibc (most common)
79
- console.log(`${LOG_PREFIX} Defaulting to glibc (no musl indicators found)`);
96
+ debug("Defaulting to glibc (no musl indicators found)");
80
97
  return "glibc";
81
98
  }
82
99
 
@@ -91,12 +108,12 @@ function getPlatformKey() {
91
108
  if (platform === "linux") {
92
109
  const libc = detectLibc();
93
110
  const key = `${platform}-${arch}-${libc}`;
94
- console.log(`${LOG_PREFIX} Platform: ${key} (os=${platform}, arch=${arch}, libc=${libc})`);
111
+ debug(`Platform: ${key} (os=${platform}, arch=${arch}, libc=${libc})`);
95
112
  return key;
96
113
  }
97
114
 
98
115
  const key = `${platform}-${arch}`;
99
- console.log(`${LOG_PREFIX} Platform: ${key} (os=${platform}, arch=${arch})`);
116
+ debug(`Platform: ${key} (os=${platform}, arch=${arch})`);
100
117
  return key;
101
118
  }
102
119
 
@@ -113,10 +130,15 @@ function loadNativeAddon() {
113
130
  // Mode 1: Bazel development - ADDON_PATH points directly to the .node file
114
131
  if (process.env.ADDON_PATH) {
115
132
  const addonPath = path.resolve(process.env.ADDON_PATH);
116
- console.log(`${LOG_PREFIX} Loading native addon from ADDON_PATH: ${addonPath}`);
117
- const addon = require(addonPath);
118
- console.log(`${LOG_PREFIX} Native addon loaded successfully from ADDON_PATH`);
119
- return addon;
133
+ debug(`Loading native addon from ADDON_PATH: ${addonPath}`);
134
+ try {
135
+ return require(addonPath);
136
+ } catch (err) {
137
+ console.error(
138
+ `${LOG_PREFIX} Failed to load native addon from ADDON_PATH ${addonPath}: ${err.message}`,
139
+ );
140
+ throw err;
141
+ }
120
142
  }
121
143
 
122
144
  // Mode 2: Production - load from platform-specific npm package
@@ -125,33 +147,37 @@ function loadNativeAddon() {
125
147
 
126
148
  if (packageSuffix) {
127
149
  const packageName = `snowpipe-streaming-${packageSuffix}`;
128
- console.log(`${LOG_PREFIX} Attempting to load platform package: ${packageName}`);
150
+ debug(`Attempting to load platform package: ${packageName}`);
129
151
  try {
130
- const addon = require(packageName);
131
- console.log(`${LOG_PREFIX} Native addon loaded successfully from ${packageName}`);
132
- return addon;
152
+ return require(packageName);
133
153
  } catch (err) {
134
154
  // Package not installed - fall through to fallback
135
155
  if (err.code !== "MODULE_NOT_FOUND") {
136
156
  console.error(`${LOG_PREFIX} Failed to load ${packageName}: ${err.message}`);
137
157
  throw err;
138
158
  }
139
- console.log(`${LOG_PREFIX} Platform package ${packageName} not found, trying local fallback`);
159
+ console.error(
160
+ `${LOG_PREFIX} Platform package ${packageName} not found, trying local fallback`,
161
+ );
140
162
  }
141
163
  } else {
142
- console.log(
164
+ console.error(
143
165
  `${LOG_PREFIX} No platform package mapping for ${platformKey}, trying local fallback`,
144
166
  );
145
167
  }
146
168
 
147
169
  // Mode 3: Fallback - local .node file (for testing or manual installation)
148
170
  const localAddon = path.resolve(__dirname, "..", "snowpipe_streaming.node");
149
- console.log(`${LOG_PREFIX} Checking for local addon: ${localAddon}`);
150
171
  if (fs.existsSync(localAddon)) {
151
- console.log(`${LOG_PREFIX} Loading native addon from local file: ${localAddon}`);
152
- const addon = require(localAddon);
153
- console.log(`${LOG_PREFIX} Native addon loaded successfully from local file`);
154
- return addon;
172
+ debug(`Loading native addon from local file: ${localAddon}`);
173
+ try {
174
+ return require(localAddon);
175
+ } catch (err) {
176
+ console.error(
177
+ `${LOG_PREFIX} Failed to load native addon from local file ${localAddon}: ${err.message}`,
178
+ );
179
+ throw err;
180
+ }
155
181
  }
156
182
 
157
183
  // No addon found - provide helpful error message