@squadbase/vite-server 0.1.9-dev.f236b23 → 0.1.10-dev.5b0c0a8

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.
@@ -42,6 +42,131 @@ var ParameterDefinition = class {
42
42
  }
43
43
  };
44
44
 
45
+ // ../connectors/src/lib/ssh-tunnel.ts
46
+ var sshTunnelParameters = {
47
+ sshHost: new ParameterDefinition({
48
+ slug: "ssh-host",
49
+ name: "SSH Tunnel Host",
50
+ description: "Optional. Hostname of the SSH bastion to tunnel through. Leave empty to connect directly.",
51
+ envVarBaseKey: "SSH_TUNNEL_HOST",
52
+ type: "text",
53
+ secret: false,
54
+ required: false
55
+ }),
56
+ sshPort: new ParameterDefinition({
57
+ slug: "ssh-port",
58
+ name: "SSH Tunnel Port",
59
+ description: "Optional. SSH port of the bastion host (default: 22).",
60
+ envVarBaseKey: "SSH_TUNNEL_PORT",
61
+ type: "text",
62
+ secret: false,
63
+ required: false
64
+ }),
65
+ sshUsername: new ParameterDefinition({
66
+ slug: "ssh-username",
67
+ name: "SSH Tunnel Username",
68
+ description: "Optional. Username for SSH authentication. Required when SSH Tunnel Host is set.",
69
+ envVarBaseKey: "SSH_TUNNEL_USERNAME",
70
+ type: "text",
71
+ secret: false,
72
+ required: false
73
+ }),
74
+ sshPrivateKeyBase64: new ParameterDefinition({
75
+ slug: "ssh-private-key-base64",
76
+ name: "SSH Private Key",
77
+ description: "Optional. Private key (PEM, base64-encoded) used for SSH authentication. Required when SSH Tunnel Host is set.",
78
+ envVarBaseKey: "SSH_TUNNEL_PRIVATE_KEY_BASE64",
79
+ type: "base64EncodedText",
80
+ secret: true,
81
+ required: false
82
+ }),
83
+ sshPassphrase: new ParameterDefinition({
84
+ slug: "ssh-passphrase",
85
+ name: "SSH Private Key Passphrase",
86
+ description: "Optional. Passphrase for the SSH private key, if it is encrypted.",
87
+ envVarBaseKey: "SSH_TUNNEL_PASSPHRASE",
88
+ type: "text",
89
+ secret: true,
90
+ required: false
91
+ })
92
+ };
93
+ var NOOP_TUNNEL_HOSTPORT = (host, port) => ({
94
+ host,
95
+ port,
96
+ close: async () => {
97
+ }
98
+ });
99
+ function connectionParamsToRecord(connection2) {
100
+ const out = {};
101
+ for (const p of connection2.parameters) {
102
+ if (p.value != null) out[p.parameterSlug] = p.value;
103
+ }
104
+ return out;
105
+ }
106
+ async function maybeOpenSshTunnelHostPort(params, dbHost, dbPort) {
107
+ const sshHost = params[sshTunnelParameters.sshHost.slug];
108
+ if (!sshHost) return NOOP_TUNNEL_HOSTPORT(dbHost, dbPort);
109
+ const sshUsername = params[sshTunnelParameters.sshUsername.slug];
110
+ const sshPrivateKeyBase64 = params[sshTunnelParameters.sshPrivateKeyBase64.slug];
111
+ if (!sshUsername || !sshPrivateKeyBase64) {
112
+ throw new Error(
113
+ "SSH tunnel requires `ssh-username` and `ssh-private-key-base64` when `ssh-host` is set."
114
+ );
115
+ }
116
+ const sshPort = Number(params[sshTunnelParameters.sshPort.slug] || "22") || 22;
117
+ const sshPassphrase = params[sshTunnelParameters.sshPassphrase.slug];
118
+ const [{ Client }, net] = await Promise.all([
119
+ import("ssh2"),
120
+ import("net")
121
+ ]);
122
+ const sshClient = new Client();
123
+ await new Promise((resolve, reject) => {
124
+ sshClient.once("ready", () => resolve());
125
+ sshClient.once("error", reject);
126
+ sshClient.connect({
127
+ host: sshHost,
128
+ port: sshPort,
129
+ username: sshUsername,
130
+ privateKey: Buffer.from(sshPrivateKeyBase64, "base64"),
131
+ passphrase: sshPassphrase || void 0,
132
+ readyTimeout: 1e4
133
+ });
134
+ });
135
+ const server = net.createServer((socket) => {
136
+ sshClient.forwardOut(
137
+ socket.remoteAddress ?? "127.0.0.1",
138
+ socket.remotePort ?? 0,
139
+ dbHost,
140
+ dbPort,
141
+ (err, stream) => {
142
+ if (err) {
143
+ socket.destroy(err);
144
+ return;
145
+ }
146
+ socket.pipe(stream).pipe(socket);
147
+ }
148
+ );
149
+ });
150
+ await new Promise((resolve, reject) => {
151
+ server.once("error", reject);
152
+ server.listen(0, "127.0.0.1", () => resolve());
153
+ });
154
+ const address = server.address();
155
+ if (!address || typeof address === "string") {
156
+ server.close();
157
+ sshClient.end();
158
+ throw new Error("Failed to allocate local port for SSH tunnel.");
159
+ }
160
+ return {
161
+ host: "127.0.0.1",
162
+ port: address.port,
163
+ close: async () => {
164
+ await new Promise((resolve) => server.close(() => resolve()));
165
+ sshClient.end();
166
+ }
167
+ };
168
+ }
169
+
45
170
  // ../connectors/src/connectors/sqlserver/utils.ts
46
171
  var SQLSERVER_PREFIX_RE = /^(?:jdbc:)?sqlserver:\/\//i;
47
172
  var TRUE_VALUES = /* @__PURE__ */ new Set(["true", "1", "yes"]);
@@ -120,17 +245,27 @@ async function importMssql() {
120
245
  }
121
246
  async function runMssqlQuery(parsed, sql, options = {}) {
122
247
  const sqlMod = await importMssql();
123
- const config = toMssqlConfig(parsed, {
124
- encrypt: options.forceEncrypt
125
- });
126
- const pool = new sqlMod.ConnectionPool(config);
127
- await pool.connect();
248
+ const tunnel = options.tunnelParams ? await maybeOpenSshTunnelHostPort(
249
+ options.tunnelParams,
250
+ parsed.server,
251
+ parsed.port
252
+ ) : null;
128
253
  try {
129
- const result = await pool.request().query(sql);
130
- const recordset = result.recordset ?? [];
131
- return { rows: recordset };
254
+ const tunneled = tunnel ? { ...parsed, server: tunnel.host, port: tunnel.port } : parsed;
255
+ const config = toMssqlConfig(tunneled, {
256
+ encrypt: options.forceEncrypt
257
+ });
258
+ const pool = new sqlMod.ConnectionPool(config);
259
+ await pool.connect();
260
+ try {
261
+ const result = await pool.request().query(sql);
262
+ const recordset = result.recordset ?? [];
263
+ return { rows: recordset };
264
+ } finally {
265
+ await pool.close();
266
+ }
132
267
  } finally {
133
- await pool.close();
268
+ await tunnel?.close();
134
269
  }
135
270
  }
136
271
  async function checkMssqlConnection(url, credentials, options = {}) {
@@ -181,7 +316,8 @@ var parameters = {
181
316
  type: "text",
182
317
  secret: true,
183
318
  required: false
184
- })
319
+ }),
320
+ ...sshTunnelParameters
185
321
  };
186
322
 
187
323
  // ../connectors/src/connectors/sqlserver/sdk/index.ts
@@ -197,7 +333,9 @@ function createClient(params) {
197
333
  const parsed = parseSqlServerJdbcUrl(jdbcUrl, { username, password });
198
334
  async function runQuery(sql) {
199
335
  try {
200
- const { rows } = await runMssqlQuery(parsed, sql);
336
+ const { rows } = await runMssqlQuery(parsed, sql, {
337
+ tunnelParams: params
338
+ });
201
339
  return rows;
202
340
  } catch (err) {
203
341
  const msg = err instanceof Error ? err.message : String(err);
@@ -442,7 +580,9 @@ Avoid loading large amounts of data; always include \`TOP\` in queries.`,
442
580
  };
443
581
  }
444
582
  try {
445
- const { rows } = await runMssqlQuery(parsed, sql);
583
+ const { rows } = await runMssqlQuery(parsed, sql, {
584
+ tunnelParams: connectionParamsToRecord(connection2)
585
+ });
446
586
  const truncated = rows.length > MAX_ROWS;
447
587
  return {
448
588
  success: true,
@@ -513,7 +653,8 @@ The business logic type for this connector is "sql".
513
653
  {
514
654
  username: params[parameters.username.slug],
515
655
  password: params[parameters.password.slug]
516
- }
656
+ },
657
+ { tunnelParams: params }
517
658
  );
518
659
  },
519
660
  async query(params, sql, _namedParams) {
@@ -523,10 +664,12 @@ The business logic type for this connector is "sql".
523
664
  });
524
665
  const sample = unwrapSampleLimit(sql);
525
666
  if (sample) {
526
- const result = await runMssqlQuery(parsed, sample.inner);
667
+ const result = await runMssqlQuery(parsed, sample.inner, {
668
+ tunnelParams: params
669
+ });
527
670
  return { rows: result.rows.slice(0, sample.limit) };
528
671
  }
529
- return runMssqlQuery(parsed, sql);
672
+ return runMssqlQuery(parsed, sql, { tunnelParams: params });
530
673
  }
531
674
  });
532
675