@squadbase/vite-server 0.0.1-build-11 → 0.0.1-build-12

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/dist/cli/index.js CHANGED
@@ -144,6 +144,152 @@ function createBigQueryClient(entry, connectionId) {
144
144
  };
145
145
  }
146
146
 
147
+ // src/connection.ts
148
+ import { getContext } from "hono/context-storage";
149
+ import { getCookie } from "hono/cookie";
150
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
151
+ var PREVIEW_SESSION_COOKIE_NAME = "squadbase-preview-session";
152
+ var APP_BASE_DOMAIN = "squadbase.app";
153
+ var PREVIEW_BASE_DOMAIN = "preview.app.squadbase.dev";
154
+ var SANDBOX_ID_ENV_NAME = "INTERNAL_SQUADBASE_SANDBOX_ID";
155
+ var MACHINE_CREDENTIAL_ENV_NAME = "INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL";
156
+ function resolveProxyUrl(connectionId) {
157
+ const connectionPath = `/_sqcore/connections/${connectionId}/request`;
158
+ const sandboxId = process.env[SANDBOX_ID_ENV_NAME];
159
+ if (sandboxId) {
160
+ const baseDomain2 = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? PREVIEW_BASE_DOMAIN;
161
+ return `https://${sandboxId}.${baseDomain2}${connectionPath}`;
162
+ }
163
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
164
+ if (!projectId) {
165
+ throw new Error(
166
+ "Project ID is required. Please set SQUADBASE_PROJECT_ID environment variable."
167
+ );
168
+ }
169
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? APP_BASE_DOMAIN;
170
+ return `https://${projectId}.${baseDomain}${connectionPath}`;
171
+ }
172
+ function resolveAuthHeaders() {
173
+ const machineCredential = process.env[MACHINE_CREDENTIAL_ENV_NAME];
174
+ if (machineCredential) {
175
+ return { Authorization: `Bearer ${machineCredential}` };
176
+ }
177
+ const c = getContext();
178
+ const cookies = getCookie(c);
179
+ const previewSession = cookies[PREVIEW_SESSION_COOKIE_NAME];
180
+ if (previewSession) {
181
+ return {
182
+ Cookie: `${PREVIEW_SESSION_COOKIE_NAME}=${previewSession}`
183
+ };
184
+ }
185
+ const appSession = cookies[APP_SESSION_COOKIE_NAME];
186
+ if (appSession) {
187
+ return { Authorization: `Bearer ${appSession}` };
188
+ }
189
+ throw new Error(
190
+ "No authentication method available for connection proxy. Expected one of: INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL env var, preview session cookie, or app session cookie."
191
+ );
192
+ }
193
+ function connection(connectionId) {
194
+ return {
195
+ async fetch(url, options) {
196
+ const proxyUrl = resolveProxyUrl(connectionId);
197
+ const authHeaders = resolveAuthHeaders();
198
+ return await fetch(proxyUrl, {
199
+ method: "POST",
200
+ headers: {
201
+ "Content-Type": "application/json",
202
+ ...authHeaders
203
+ },
204
+ body: JSON.stringify({
205
+ url,
206
+ method: options?.method,
207
+ headers: options?.headers,
208
+ body: options?.body,
209
+ timeoutMs: options?.timeoutMs
210
+ })
211
+ });
212
+ }
213
+ };
214
+ }
215
+
216
+ // src/connector-client/bigquery-oauth.ts
217
+ var MAX_RESULTS = 1e4;
218
+ var POLL_INTERVAL_MS = 1e3;
219
+ var POLL_TIMEOUT_MS = 12e4;
220
+ function flattenRows(fields, rows) {
221
+ return rows.map((row) => {
222
+ const obj = {};
223
+ for (let i = 0; i < fields.length; i++) {
224
+ obj[fields[i].name] = row.f[i].v;
225
+ }
226
+ return obj;
227
+ });
228
+ }
229
+ function createBigQueryOAuthClient(entry, connectionId) {
230
+ const projectId = resolveEnvVar(entry, "project-id", connectionId);
231
+ const baseUrl = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}`;
232
+ return {
233
+ async query(sql) {
234
+ const conn = connection(connectionId);
235
+ const res = await conn.fetch(`${baseUrl}/queries`, {
236
+ method: "POST",
237
+ body: {
238
+ query: sql,
239
+ useLegacySql: false,
240
+ maxResults: MAX_RESULTS
241
+ }
242
+ });
243
+ if (!res.ok) {
244
+ const text = await res.text().catch(() => res.statusText);
245
+ throw new Error(`BigQuery query failed: HTTP ${res.status} ${text}`);
246
+ }
247
+ let data = await res.json();
248
+ if (data.errors?.length) {
249
+ throw new Error(
250
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
251
+ );
252
+ }
253
+ if (!data.jobComplete) {
254
+ const jobId = data.jobReference.jobId;
255
+ const location = data.jobReference.location;
256
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
257
+ while (!data.jobComplete) {
258
+ if (Date.now() > deadline) {
259
+ throw new Error(
260
+ `BigQuery query timed out after ${POLL_TIMEOUT_MS / 1e3}s (jobId: ${jobId})`
261
+ );
262
+ }
263
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
264
+ const params = new URLSearchParams({
265
+ maxResults: String(MAX_RESULTS)
266
+ });
267
+ if (location) params.set("location", location);
268
+ const pollRes = await conn.fetch(
269
+ `${baseUrl}/queries/${jobId}?${params}`,
270
+ { method: "GET" }
271
+ );
272
+ if (!pollRes.ok) {
273
+ const text = await pollRes.text().catch(() => pollRes.statusText);
274
+ throw new Error(
275
+ `BigQuery poll failed: HTTP ${pollRes.status} ${text}`
276
+ );
277
+ }
278
+ data = await pollRes.json();
279
+ if (data.errors?.length) {
280
+ throw new Error(
281
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
282
+ );
283
+ }
284
+ }
285
+ }
286
+ const fields = data.schema?.fields ?? [];
287
+ const rawRows = data.rows ?? [];
288
+ return { rows: flattenRows(fields, rawRows) };
289
+ }
290
+ };
291
+ }
292
+
147
293
  // src/connector-client/snowflake.ts
148
294
  function createSnowflakeClient(entry, connectionId) {
149
295
  const accountIdentifier = resolveEnvVar(entry, "account", connectionId);
@@ -156,7 +302,7 @@ function createSnowflakeClient(entry, connectionId) {
156
302
  async query(sql) {
157
303
  const snowflake = (await import("snowflake-sdk")).default;
158
304
  snowflake.configure({ logLevel: "ERROR" });
159
- const connection = snowflake.createConnection({
305
+ const connection2 = snowflake.createConnection({
160
306
  account: accountIdentifier,
161
307
  username: user,
162
308
  role,
@@ -165,13 +311,13 @@ function createSnowflakeClient(entry, connectionId) {
165
311
  privateKey
166
312
  });
167
313
  await new Promise((resolve, reject) => {
168
- connection.connect((err) => {
314
+ connection2.connect((err) => {
169
315
  if (err) reject(new Error(`Snowflake connect failed: ${err.message}`));
170
316
  else resolve();
171
317
  });
172
318
  });
173
319
  const rows = await new Promise((resolve, reject) => {
174
- connection.execute({
320
+ connection2.execute({
175
321
  sqlText: sql,
176
322
  complete: (err, _stmt, rows2) => {
177
323
  if (err) reject(new Error(`Snowflake query failed: ${err.message}`));
@@ -179,7 +325,7 @@ function createSnowflakeClient(entry, connectionId) {
179
325
  }
180
326
  });
181
327
  });
182
- connection.destroy((err) => {
328
+ connection2.destroy((err) => {
183
329
  if (err) console.warn(`[connector-client] Snowflake destroy error: ${err.message}`);
184
330
  });
185
331
  return { rows };
@@ -385,6 +531,9 @@ function createConnectorRegistry() {
385
531
  return { client: createSnowflakeClient(entry, connectionId), connectorSlug };
386
532
  }
387
533
  if (connectorSlug === "bigquery") {
534
+ if (entry.connector.authType === "oauth") {
535
+ return { client: createBigQueryOAuthClient(entry, connectionId), connectorSlug };
536
+ }
388
537
  return { client: createBigQueryClient(entry, connectionId), connectorSlug };
389
538
  }
390
539
  if (connectorSlug === "athena") {
package/dist/index.js CHANGED
@@ -69,6 +69,152 @@ function createBigQueryClient(entry, connectionId) {
69
69
  };
70
70
  }
71
71
 
72
+ // src/connection.ts
73
+ import { getContext } from "hono/context-storage";
74
+ import { getCookie } from "hono/cookie";
75
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
76
+ var PREVIEW_SESSION_COOKIE_NAME = "squadbase-preview-session";
77
+ var APP_BASE_DOMAIN = "squadbase.app";
78
+ var PREVIEW_BASE_DOMAIN = "preview.app.squadbase.dev";
79
+ var SANDBOX_ID_ENV_NAME = "INTERNAL_SQUADBASE_SANDBOX_ID";
80
+ var MACHINE_CREDENTIAL_ENV_NAME = "INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL";
81
+ function resolveProxyUrl(connectionId) {
82
+ const connectionPath = `/_sqcore/connections/${connectionId}/request`;
83
+ const sandboxId = process.env[SANDBOX_ID_ENV_NAME];
84
+ if (sandboxId) {
85
+ const baseDomain2 = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? PREVIEW_BASE_DOMAIN;
86
+ return `https://${sandboxId}.${baseDomain2}${connectionPath}`;
87
+ }
88
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
89
+ if (!projectId) {
90
+ throw new Error(
91
+ "Project ID is required. Please set SQUADBASE_PROJECT_ID environment variable."
92
+ );
93
+ }
94
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? APP_BASE_DOMAIN;
95
+ return `https://${projectId}.${baseDomain}${connectionPath}`;
96
+ }
97
+ function resolveAuthHeaders() {
98
+ const machineCredential = process.env[MACHINE_CREDENTIAL_ENV_NAME];
99
+ if (machineCredential) {
100
+ return { Authorization: `Bearer ${machineCredential}` };
101
+ }
102
+ const c = getContext();
103
+ const cookies = getCookie(c);
104
+ const previewSession = cookies[PREVIEW_SESSION_COOKIE_NAME];
105
+ if (previewSession) {
106
+ return {
107
+ Cookie: `${PREVIEW_SESSION_COOKIE_NAME}=${previewSession}`
108
+ };
109
+ }
110
+ const appSession = cookies[APP_SESSION_COOKIE_NAME];
111
+ if (appSession) {
112
+ return { Authorization: `Bearer ${appSession}` };
113
+ }
114
+ throw new Error(
115
+ "No authentication method available for connection proxy. Expected one of: INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL env var, preview session cookie, or app session cookie."
116
+ );
117
+ }
118
+ function connection(connectionId) {
119
+ return {
120
+ async fetch(url, options) {
121
+ const proxyUrl = resolveProxyUrl(connectionId);
122
+ const authHeaders = resolveAuthHeaders();
123
+ return await fetch(proxyUrl, {
124
+ method: "POST",
125
+ headers: {
126
+ "Content-Type": "application/json",
127
+ ...authHeaders
128
+ },
129
+ body: JSON.stringify({
130
+ url,
131
+ method: options?.method,
132
+ headers: options?.headers,
133
+ body: options?.body,
134
+ timeoutMs: options?.timeoutMs
135
+ })
136
+ });
137
+ }
138
+ };
139
+ }
140
+
141
+ // src/connector-client/bigquery-oauth.ts
142
+ var MAX_RESULTS = 1e4;
143
+ var POLL_INTERVAL_MS = 1e3;
144
+ var POLL_TIMEOUT_MS = 12e4;
145
+ function flattenRows(fields, rows) {
146
+ return rows.map((row) => {
147
+ const obj = {};
148
+ for (let i = 0; i < fields.length; i++) {
149
+ obj[fields[i].name] = row.f[i].v;
150
+ }
151
+ return obj;
152
+ });
153
+ }
154
+ function createBigQueryOAuthClient(entry, connectionId) {
155
+ const projectId = resolveEnvVar(entry, "project-id", connectionId);
156
+ const baseUrl = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}`;
157
+ return {
158
+ async query(sql) {
159
+ const conn = connection(connectionId);
160
+ const res = await conn.fetch(`${baseUrl}/queries`, {
161
+ method: "POST",
162
+ body: {
163
+ query: sql,
164
+ useLegacySql: false,
165
+ maxResults: MAX_RESULTS
166
+ }
167
+ });
168
+ if (!res.ok) {
169
+ const text = await res.text().catch(() => res.statusText);
170
+ throw new Error(`BigQuery query failed: HTTP ${res.status} ${text}`);
171
+ }
172
+ let data = await res.json();
173
+ if (data.errors?.length) {
174
+ throw new Error(
175
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
176
+ );
177
+ }
178
+ if (!data.jobComplete) {
179
+ const jobId = data.jobReference.jobId;
180
+ const location = data.jobReference.location;
181
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
182
+ while (!data.jobComplete) {
183
+ if (Date.now() > deadline) {
184
+ throw new Error(
185
+ `BigQuery query timed out after ${POLL_TIMEOUT_MS / 1e3}s (jobId: ${jobId})`
186
+ );
187
+ }
188
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
189
+ const params = new URLSearchParams({
190
+ maxResults: String(MAX_RESULTS)
191
+ });
192
+ if (location) params.set("location", location);
193
+ const pollRes = await conn.fetch(
194
+ `${baseUrl}/queries/${jobId}?${params}`,
195
+ { method: "GET" }
196
+ );
197
+ if (!pollRes.ok) {
198
+ const text = await pollRes.text().catch(() => pollRes.statusText);
199
+ throw new Error(
200
+ `BigQuery poll failed: HTTP ${pollRes.status} ${text}`
201
+ );
202
+ }
203
+ data = await pollRes.json();
204
+ if (data.errors?.length) {
205
+ throw new Error(
206
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
207
+ );
208
+ }
209
+ }
210
+ }
211
+ const fields = data.schema?.fields ?? [];
212
+ const rawRows = data.rows ?? [];
213
+ return { rows: flattenRows(fields, rawRows) };
214
+ }
215
+ };
216
+ }
217
+
72
218
  // src/connector-client/snowflake.ts
73
219
  function createSnowflakeClient(entry, connectionId) {
74
220
  const accountIdentifier = resolveEnvVar(entry, "account", connectionId);
@@ -310,6 +456,9 @@ function createConnectorRegistry() {
310
456
  return { client: createSnowflakeClient(entry, connectionId), connectorSlug };
311
457
  }
312
458
  if (connectorSlug === "bigquery") {
459
+ if (entry.connector.authType === "oauth") {
460
+ return { client: createBigQueryOAuthClient(entry, connectionId), connectorSlug };
461
+ }
313
462
  return { client: createBigQueryClient(entry, connectionId), connectorSlug };
314
463
  }
315
464
  if (connectorSlug === "athena") {
@@ -1235,75 +1384,6 @@ app4.get("/runtime-data", (c) => {
1235
1384
  });
1236
1385
  var pages_default = app4;
1237
1386
 
1238
- // src/connection.ts
1239
- import { getContext } from "hono/context-storage";
1240
- import { getCookie } from "hono/cookie";
1241
- var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
1242
- var PREVIEW_SESSION_COOKIE_NAME = "squadbase-preview-session";
1243
- var APP_BASE_DOMAIN = "squadbase.app";
1244
- var PREVIEW_BASE_DOMAIN = "preview.app.squadbase.dev";
1245
- var SANDBOX_ID_ENV_NAME = "INTERNAL_SQUADBASE_SANDBOX_ID";
1246
- var MACHINE_CREDENTIAL_ENV_NAME = "INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL";
1247
- function resolveProxyUrl(connectionId) {
1248
- const connectionPath = `/_sqcore/connections/${connectionId}/request`;
1249
- const sandboxId = process.env[SANDBOX_ID_ENV_NAME];
1250
- if (sandboxId) {
1251
- const baseDomain2 = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? PREVIEW_BASE_DOMAIN;
1252
- return `https://${sandboxId}.${baseDomain2}${connectionPath}`;
1253
- }
1254
- const projectId = process.env["SQUADBASE_PROJECT_ID"];
1255
- if (!projectId) {
1256
- throw new Error(
1257
- "Project ID is required. Please set SQUADBASE_PROJECT_ID environment variable."
1258
- );
1259
- }
1260
- const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? APP_BASE_DOMAIN;
1261
- return `https://${projectId}.${baseDomain}${connectionPath}`;
1262
- }
1263
- function resolveAuthHeaders() {
1264
- const machineCredential = process.env[MACHINE_CREDENTIAL_ENV_NAME];
1265
- if (machineCredential) {
1266
- return { Authorization: `Bearer ${machineCredential}` };
1267
- }
1268
- const c = getContext();
1269
- const cookies = getCookie(c);
1270
- const previewSession = cookies[PREVIEW_SESSION_COOKIE_NAME];
1271
- if (previewSession) {
1272
- return {
1273
- Cookie: `${PREVIEW_SESSION_COOKIE_NAME}=${previewSession}`
1274
- };
1275
- }
1276
- const appSession = cookies[APP_SESSION_COOKIE_NAME];
1277
- if (appSession) {
1278
- return { Authorization: `Bearer ${appSession}` };
1279
- }
1280
- throw new Error(
1281
- "No authentication method available for connection proxy. Expected one of: INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL env var, preview session cookie, or app session cookie."
1282
- );
1283
- }
1284
- function connection(connectionId) {
1285
- return {
1286
- async fetch(url, options) {
1287
- const proxyUrl = resolveProxyUrl(connectionId);
1288
- const authHeaders = resolveAuthHeaders();
1289
- return await fetch(proxyUrl, {
1290
- method: "POST",
1291
- headers: {
1292
- "Content-Type": "application/json",
1293
- ...authHeaders
1294
- },
1295
- body: JSON.stringify({
1296
- url,
1297
- method: options?.method,
1298
- headers: options?.headers,
1299
- body: options?.body,
1300
- timeoutMs: options?.timeoutMs
1301
- })
1302
- });
1303
- }
1304
- };
1305
- }
1306
-
1307
1387
  // src/index.ts
1308
1388
  var apiApp = new Hono5();
1309
1389
  apiApp.use("/*", contextStorage());
package/dist/main.js CHANGED
@@ -69,6 +69,152 @@ function createBigQueryClient(entry, connectionId) {
69
69
  };
70
70
  }
71
71
 
72
+ // src/connection.ts
73
+ import { getContext } from "hono/context-storage";
74
+ import { getCookie } from "hono/cookie";
75
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
76
+ var PREVIEW_SESSION_COOKIE_NAME = "squadbase-preview-session";
77
+ var APP_BASE_DOMAIN = "squadbase.app";
78
+ var PREVIEW_BASE_DOMAIN = "preview.app.squadbase.dev";
79
+ var SANDBOX_ID_ENV_NAME = "INTERNAL_SQUADBASE_SANDBOX_ID";
80
+ var MACHINE_CREDENTIAL_ENV_NAME = "INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL";
81
+ function resolveProxyUrl(connectionId) {
82
+ const connectionPath = `/_sqcore/connections/${connectionId}/request`;
83
+ const sandboxId = process.env[SANDBOX_ID_ENV_NAME];
84
+ if (sandboxId) {
85
+ const baseDomain2 = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? PREVIEW_BASE_DOMAIN;
86
+ return `https://${sandboxId}.${baseDomain2}${connectionPath}`;
87
+ }
88
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
89
+ if (!projectId) {
90
+ throw new Error(
91
+ "Project ID is required. Please set SQUADBASE_PROJECT_ID environment variable."
92
+ );
93
+ }
94
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? APP_BASE_DOMAIN;
95
+ return `https://${projectId}.${baseDomain}${connectionPath}`;
96
+ }
97
+ function resolveAuthHeaders() {
98
+ const machineCredential = process.env[MACHINE_CREDENTIAL_ENV_NAME];
99
+ if (machineCredential) {
100
+ return { Authorization: `Bearer ${machineCredential}` };
101
+ }
102
+ const c = getContext();
103
+ const cookies = getCookie(c);
104
+ const previewSession = cookies[PREVIEW_SESSION_COOKIE_NAME];
105
+ if (previewSession) {
106
+ return {
107
+ Cookie: `${PREVIEW_SESSION_COOKIE_NAME}=${previewSession}`
108
+ };
109
+ }
110
+ const appSession = cookies[APP_SESSION_COOKIE_NAME];
111
+ if (appSession) {
112
+ return { Authorization: `Bearer ${appSession}` };
113
+ }
114
+ throw new Error(
115
+ "No authentication method available for connection proxy. Expected one of: INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL env var, preview session cookie, or app session cookie."
116
+ );
117
+ }
118
+ function connection(connectionId) {
119
+ return {
120
+ async fetch(url, options) {
121
+ const proxyUrl = resolveProxyUrl(connectionId);
122
+ const authHeaders = resolveAuthHeaders();
123
+ return await fetch(proxyUrl, {
124
+ method: "POST",
125
+ headers: {
126
+ "Content-Type": "application/json",
127
+ ...authHeaders
128
+ },
129
+ body: JSON.stringify({
130
+ url,
131
+ method: options?.method,
132
+ headers: options?.headers,
133
+ body: options?.body,
134
+ timeoutMs: options?.timeoutMs
135
+ })
136
+ });
137
+ }
138
+ };
139
+ }
140
+
141
+ // src/connector-client/bigquery-oauth.ts
142
+ var MAX_RESULTS = 1e4;
143
+ var POLL_INTERVAL_MS = 1e3;
144
+ var POLL_TIMEOUT_MS = 12e4;
145
+ function flattenRows(fields, rows) {
146
+ return rows.map((row) => {
147
+ const obj = {};
148
+ for (let i = 0; i < fields.length; i++) {
149
+ obj[fields[i].name] = row.f[i].v;
150
+ }
151
+ return obj;
152
+ });
153
+ }
154
+ function createBigQueryOAuthClient(entry, connectionId) {
155
+ const projectId = resolveEnvVar(entry, "project-id", connectionId);
156
+ const baseUrl = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}`;
157
+ return {
158
+ async query(sql) {
159
+ const conn = connection(connectionId);
160
+ const res = await conn.fetch(`${baseUrl}/queries`, {
161
+ method: "POST",
162
+ body: {
163
+ query: sql,
164
+ useLegacySql: false,
165
+ maxResults: MAX_RESULTS
166
+ }
167
+ });
168
+ if (!res.ok) {
169
+ const text = await res.text().catch(() => res.statusText);
170
+ throw new Error(`BigQuery query failed: HTTP ${res.status} ${text}`);
171
+ }
172
+ let data = await res.json();
173
+ if (data.errors?.length) {
174
+ throw new Error(
175
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
176
+ );
177
+ }
178
+ if (!data.jobComplete) {
179
+ const jobId = data.jobReference.jobId;
180
+ const location = data.jobReference.location;
181
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
182
+ while (!data.jobComplete) {
183
+ if (Date.now() > deadline) {
184
+ throw new Error(
185
+ `BigQuery query timed out after ${POLL_TIMEOUT_MS / 1e3}s (jobId: ${jobId})`
186
+ );
187
+ }
188
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
189
+ const params = new URLSearchParams({
190
+ maxResults: String(MAX_RESULTS)
191
+ });
192
+ if (location) params.set("location", location);
193
+ const pollRes = await conn.fetch(
194
+ `${baseUrl}/queries/${jobId}?${params}`,
195
+ { method: "GET" }
196
+ );
197
+ if (!pollRes.ok) {
198
+ const text = await pollRes.text().catch(() => pollRes.statusText);
199
+ throw new Error(
200
+ `BigQuery poll failed: HTTP ${pollRes.status} ${text}`
201
+ );
202
+ }
203
+ data = await pollRes.json();
204
+ if (data.errors?.length) {
205
+ throw new Error(
206
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
207
+ );
208
+ }
209
+ }
210
+ }
211
+ const fields = data.schema?.fields ?? [];
212
+ const rawRows = data.rows ?? [];
213
+ return { rows: flattenRows(fields, rawRows) };
214
+ }
215
+ };
216
+ }
217
+
72
218
  // src/connector-client/snowflake.ts
73
219
  function createSnowflakeClient(entry, connectionId) {
74
220
  const accountIdentifier = resolveEnvVar(entry, "account", connectionId);
@@ -310,6 +456,9 @@ function createConnectorRegistry() {
310
456
  return { client: createSnowflakeClient(entry, connectionId), connectorSlug };
311
457
  }
312
458
  if (connectorSlug === "bigquery") {
459
+ if (entry.connector.authType === "oauth") {
460
+ return { client: createBigQueryOAuthClient(entry, connectionId), connectorSlug };
461
+ }
313
462
  return { client: createBigQueryClient(entry, connectionId), connectorSlug };
314
463
  }
315
464
  if (connectorSlug === "athena") {
@@ -973,10 +1122,6 @@ app4.get("/runtime-data", (c) => {
973
1122
  });
974
1123
  var pages_default = app4;
975
1124
 
976
- // src/connection.ts
977
- import { getContext } from "hono/context-storage";
978
- import { getCookie } from "hono/cookie";
979
-
980
1125
  // src/index.ts
981
1126
  var apiApp = new Hono5();
982
1127
  apiApp.use("/*", contextStorage());
@@ -70,6 +70,152 @@ function createBigQueryClient(entry, connectionId) {
70
70
  };
71
71
  }
72
72
 
73
+ // src/connection.ts
74
+ import { getContext } from "hono/context-storage";
75
+ import { getCookie } from "hono/cookie";
76
+ var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
77
+ var PREVIEW_SESSION_COOKIE_NAME = "squadbase-preview-session";
78
+ var APP_BASE_DOMAIN = "squadbase.app";
79
+ var PREVIEW_BASE_DOMAIN = "preview.app.squadbase.dev";
80
+ var SANDBOX_ID_ENV_NAME = "INTERNAL_SQUADBASE_SANDBOX_ID";
81
+ var MACHINE_CREDENTIAL_ENV_NAME = "INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL";
82
+ function resolveProxyUrl(connectionId) {
83
+ const connectionPath = `/_sqcore/connections/${connectionId}/request`;
84
+ const sandboxId = process.env[SANDBOX_ID_ENV_NAME];
85
+ if (sandboxId) {
86
+ const baseDomain2 = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? PREVIEW_BASE_DOMAIN;
87
+ return `https://${sandboxId}.${baseDomain2}${connectionPath}`;
88
+ }
89
+ const projectId = process.env["SQUADBASE_PROJECT_ID"];
90
+ if (!projectId) {
91
+ throw new Error(
92
+ "Project ID is required. Please set SQUADBASE_PROJECT_ID environment variable."
93
+ );
94
+ }
95
+ const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? APP_BASE_DOMAIN;
96
+ return `https://${projectId}.${baseDomain}${connectionPath}`;
97
+ }
98
+ function resolveAuthHeaders() {
99
+ const machineCredential = process.env[MACHINE_CREDENTIAL_ENV_NAME];
100
+ if (machineCredential) {
101
+ return { Authorization: `Bearer ${machineCredential}` };
102
+ }
103
+ const c = getContext();
104
+ const cookies = getCookie(c);
105
+ const previewSession = cookies[PREVIEW_SESSION_COOKIE_NAME];
106
+ if (previewSession) {
107
+ return {
108
+ Cookie: `${PREVIEW_SESSION_COOKIE_NAME}=${previewSession}`
109
+ };
110
+ }
111
+ const appSession = cookies[APP_SESSION_COOKIE_NAME];
112
+ if (appSession) {
113
+ return { Authorization: `Bearer ${appSession}` };
114
+ }
115
+ throw new Error(
116
+ "No authentication method available for connection proxy. Expected one of: INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL env var, preview session cookie, or app session cookie."
117
+ );
118
+ }
119
+ function connection(connectionId) {
120
+ return {
121
+ async fetch(url, options) {
122
+ const proxyUrl = resolveProxyUrl(connectionId);
123
+ const authHeaders = resolveAuthHeaders();
124
+ return await fetch(proxyUrl, {
125
+ method: "POST",
126
+ headers: {
127
+ "Content-Type": "application/json",
128
+ ...authHeaders
129
+ },
130
+ body: JSON.stringify({
131
+ url,
132
+ method: options?.method,
133
+ headers: options?.headers,
134
+ body: options?.body,
135
+ timeoutMs: options?.timeoutMs
136
+ })
137
+ });
138
+ }
139
+ };
140
+ }
141
+
142
+ // src/connector-client/bigquery-oauth.ts
143
+ var MAX_RESULTS = 1e4;
144
+ var POLL_INTERVAL_MS = 1e3;
145
+ var POLL_TIMEOUT_MS = 12e4;
146
+ function flattenRows(fields, rows) {
147
+ return rows.map((row) => {
148
+ const obj = {};
149
+ for (let i = 0; i < fields.length; i++) {
150
+ obj[fields[i].name] = row.f[i].v;
151
+ }
152
+ return obj;
153
+ });
154
+ }
155
+ function createBigQueryOAuthClient(entry, connectionId) {
156
+ const projectId = resolveEnvVar(entry, "project-id", connectionId);
157
+ const baseUrl = `https://bigquery.googleapis.com/bigquery/v2/projects/${projectId}`;
158
+ return {
159
+ async query(sql) {
160
+ const conn = connection(connectionId);
161
+ const res = await conn.fetch(`${baseUrl}/queries`, {
162
+ method: "POST",
163
+ body: {
164
+ query: sql,
165
+ useLegacySql: false,
166
+ maxResults: MAX_RESULTS
167
+ }
168
+ });
169
+ if (!res.ok) {
170
+ const text = await res.text().catch(() => res.statusText);
171
+ throw new Error(`BigQuery query failed: HTTP ${res.status} ${text}`);
172
+ }
173
+ let data = await res.json();
174
+ if (data.errors?.length) {
175
+ throw new Error(
176
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
177
+ );
178
+ }
179
+ if (!data.jobComplete) {
180
+ const jobId = data.jobReference.jobId;
181
+ const location = data.jobReference.location;
182
+ const deadline = Date.now() + POLL_TIMEOUT_MS;
183
+ while (!data.jobComplete) {
184
+ if (Date.now() > deadline) {
185
+ throw new Error(
186
+ `BigQuery query timed out after ${POLL_TIMEOUT_MS / 1e3}s (jobId: ${jobId})`
187
+ );
188
+ }
189
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
190
+ const params = new URLSearchParams({
191
+ maxResults: String(MAX_RESULTS)
192
+ });
193
+ if (location) params.set("location", location);
194
+ const pollRes = await conn.fetch(
195
+ `${baseUrl}/queries/${jobId}?${params}`,
196
+ { method: "GET" }
197
+ );
198
+ if (!pollRes.ok) {
199
+ const text = await pollRes.text().catch(() => pollRes.statusText);
200
+ throw new Error(
201
+ `BigQuery poll failed: HTTP ${pollRes.status} ${text}`
202
+ );
203
+ }
204
+ data = await pollRes.json();
205
+ if (data.errors?.length) {
206
+ throw new Error(
207
+ `BigQuery query error: ${data.errors.map((e) => e.message).join("; ")}`
208
+ );
209
+ }
210
+ }
211
+ }
212
+ const fields = data.schema?.fields ?? [];
213
+ const rawRows = data.rows ?? [];
214
+ return { rows: flattenRows(fields, rawRows) };
215
+ }
216
+ };
217
+ }
218
+
73
219
  // src/connector-client/snowflake.ts
74
220
  function createSnowflakeClient(entry, connectionId) {
75
221
  const accountIdentifier = resolveEnvVar(entry, "account", connectionId);
@@ -82,7 +228,7 @@ function createSnowflakeClient(entry, connectionId) {
82
228
  async query(sql) {
83
229
  const snowflake = (await import("snowflake-sdk")).default;
84
230
  snowflake.configure({ logLevel: "ERROR" });
85
- const connection = snowflake.createConnection({
231
+ const connection2 = snowflake.createConnection({
86
232
  account: accountIdentifier,
87
233
  username: user,
88
234
  role,
@@ -91,13 +237,13 @@ function createSnowflakeClient(entry, connectionId) {
91
237
  privateKey
92
238
  });
93
239
  await new Promise((resolve, reject) => {
94
- connection.connect((err) => {
240
+ connection2.connect((err) => {
95
241
  if (err) reject(new Error(`Snowflake connect failed: ${err.message}`));
96
242
  else resolve();
97
243
  });
98
244
  });
99
245
  const rows = await new Promise((resolve, reject) => {
100
- connection.execute({
246
+ connection2.execute({
101
247
  sqlText: sql,
102
248
  complete: (err, _stmt, rows2) => {
103
249
  if (err) reject(new Error(`Snowflake query failed: ${err.message}`));
@@ -105,7 +251,7 @@ function createSnowflakeClient(entry, connectionId) {
105
251
  }
106
252
  });
107
253
  });
108
- connection.destroy((err) => {
254
+ connection2.destroy((err) => {
109
255
  if (err) console.warn(`[connector-client] Snowflake destroy error: ${err.message}`);
110
256
  });
111
257
  return { rows };
@@ -311,6 +457,9 @@ function createConnectorRegistry() {
311
457
  return { client: createSnowflakeClient(entry, connectionId), connectorSlug };
312
458
  }
313
459
  if (connectorSlug === "bigquery") {
460
+ if (entry.connector.authType === "oauth") {
461
+ return { client: createBigQueryOAuthClient(entry, connectionId), connectorSlug };
462
+ }
314
463
  return { client: createBigQueryClient(entry, connectionId), connectorSlug };
315
464
  }
316
465
  if (connectorSlug === "athena") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squadbase/vite-server",
3
- "version": "0.0.1-build-11",
3
+ "version": "0.0.1-build-12",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {