@squadbase/vite-server 0.0.1-build-10 → 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") {
@@ -469,6 +618,70 @@ import path3 from "path";
469
618
  import { readdir, readFile as readFile2, mkdir } from "fs/promises";
470
619
  import { watch as fsWatch2 } from "fs";
471
620
  import path2 from "path";
621
+
622
+ // src/types/data-source.ts
623
+ import { z } from "zod";
624
+ var parameterMetaSchema = z.object({
625
+ name: z.string(),
626
+ type: z.enum(["string", "number", "boolean"]),
627
+ description: z.string(),
628
+ required: z.boolean().optional(),
629
+ default: z.union([z.string(), z.number(), z.boolean()]).optional()
630
+ });
631
+ var dataSourceCacheConfigSchema = z.object({
632
+ ttl: z.number(),
633
+ staleWhileRevalidate: z.boolean().optional()
634
+ });
635
+ var dataSourceSchemaObjectSchema = z.lazy(
636
+ () => z.object({
637
+ type: z.enum(["string", "number", "integer", "boolean", "object", "array", "null"]).optional(),
638
+ format: z.string().optional(),
639
+ description: z.string().optional(),
640
+ nullable: z.boolean().optional(),
641
+ enum: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(),
642
+ items: dataSourceSchemaObjectSchema.optional(),
643
+ properties: z.record(z.string(), dataSourceSchemaObjectSchema).optional(),
644
+ required: z.array(z.string()).optional(),
645
+ additionalProperties: z.union([z.boolean(), dataSourceSchemaObjectSchema]).optional(),
646
+ minimum: z.number().optional(),
647
+ maximum: z.number().optional(),
648
+ minLength: z.number().optional(),
649
+ maxLength: z.number().optional(),
650
+ pattern: z.string().optional()
651
+ })
652
+ );
653
+ var dataSourceMediaTypeSchema = z.object({
654
+ schema: dataSourceSchemaObjectSchema.optional(),
655
+ example: z.unknown().optional()
656
+ });
657
+ var dataSourceResponseSchema = z.object({
658
+ description: z.string().optional(),
659
+ defaultContentType: z.string().optional(),
660
+ content: z.record(z.string(), dataSourceMediaTypeSchema).optional()
661
+ });
662
+ var jsonBaseFields = {
663
+ description: z.string(),
664
+ parameters: z.array(parameterMetaSchema).optional(),
665
+ response: dataSourceResponseSchema.optional(),
666
+ cache: dataSourceCacheConfigSchema.optional()
667
+ };
668
+ var jsonSqlDataSourceSchema = z.object({
669
+ ...jsonBaseFields,
670
+ type: z.literal("sql").optional(),
671
+ query: z.string(),
672
+ connectionId: z.string()
673
+ });
674
+ var jsonTypeScriptDataSourceSchema = z.object({
675
+ ...jsonBaseFields,
676
+ type: z.literal("typescript"),
677
+ handlerPath: z.string()
678
+ });
679
+ var anyJsonDataSourceSchema = z.union([
680
+ jsonTypeScriptDataSourceSchema,
681
+ jsonSqlDataSourceSchema
682
+ ]);
683
+
684
+ // src/registry.ts
472
685
  function buildQuery(queryTemplate, parameterMeta, runtimeParams) {
473
686
  const defaults = new Map(
474
687
  parameterMeta.map((p) => [p.name, p.default ?? null])
@@ -605,7 +818,17 @@ async function runDataSource(slug, dirPath, params, limit) {
605
818
  let def;
606
819
  try {
607
820
  const raw = await readFile3(jsonPath, "utf-8");
608
- def = JSON.parse(raw);
821
+ const parsed = anyJsonDataSourceSchema.safeParse(JSON.parse(raw));
822
+ if (!parsed.success) {
823
+ return {
824
+ slug,
825
+ rows: [],
826
+ rowCount: 0,
827
+ durationMs: 0,
828
+ error: new Error(`Invalid data source definition: ${parsed.error.message}`)
829
+ };
830
+ }
831
+ def = parsed.data;
609
832
  } catch {
610
833
  return {
611
834
  slug,
@@ -811,8 +1034,8 @@ Total: ${results.length}, Failed: ${failed}`);
811
1034
  let paramMeta = [];
812
1035
  try {
813
1036
  const raw = await readFile4(jsonPath, "utf-8");
814
- const def = JSON.parse(raw);
815
- paramMeta = def.parameters ?? [];
1037
+ const parsed = anyJsonDataSourceSchema.safeParse(JSON.parse(raw));
1038
+ if (parsed.success) paramMeta = parsed.data.parameters ?? [];
816
1039
  } catch {
817
1040
  }
818
1041
  const interactiveParams = await inputParameters2(paramMeta);
package/dist/index.d.ts CHANGED
@@ -126,6 +126,16 @@ declare const getClient: (connectionId: string) => Promise<{
126
126
  }>;
127
127
  declare const loadConnections: () => Promise<ConnectionsMap>;
128
128
 
129
+ type ConnectionFetchOptions = {
130
+ method?: "GET" | "POST" | "PUT" | "PATCH" | "DELETE";
131
+ headers?: Record<string, string>;
132
+ body?: unknown;
133
+ timeoutMs?: number;
134
+ };
135
+ declare function connection(connectionId: string): {
136
+ fetch(url: string, options?: ConnectionFetchOptions): Promise<Response>;
137
+ };
138
+
129
139
  declare const app: Hono<hono_types.BlankEnv, hono_types.BlankSchema, "/">;
130
140
 
131
- export { type AirtableClient, type AirtableRecord, type ConnectionEntry, type ConnectionsMap, type DatabaseClient, type DbtClient, type GoogleAnalyticsClient, type KintoneClient, type WixStoreClient, createAirtableClient, createDbtClient, createGoogleAnalyticsClient, createKintoneClient, createWixStoreClient, app as default, getClient, loadConnections };
141
+ export { type AirtableClient, type AirtableRecord, type ConnectionEntry, type ConnectionFetchOptions, type ConnectionsMap, type DatabaseClient, type DbtClient, type GoogleAnalyticsClient, type KintoneClient, type WixStoreClient, connection, createAirtableClient, createDbtClient, createGoogleAnalyticsClient, createKintoneClient, createWixStoreClient, app as default, getClient, loadConnections };
package/dist/index.js CHANGED
@@ -1,5 +1,6 @@
1
1
  // src/index.ts
2
2
  import { Hono as Hono5 } from "hono";
3
+ import { contextStorage } from "hono/context-storage";
3
4
  import { cors } from "hono/cors";
4
5
  import path4 from "path";
5
6
 
@@ -68,6 +69,152 @@ function createBigQueryClient(entry, connectionId) {
68
69
  };
69
70
  }
70
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
+
71
218
  // src/connector-client/snowflake.ts
72
219
  function createSnowflakeClient(entry, connectionId) {
73
220
  const accountIdentifier = resolveEnvVar(entry, "account", connectionId);
@@ -80,7 +227,7 @@ function createSnowflakeClient(entry, connectionId) {
80
227
  async query(sql) {
81
228
  const snowflake = (await import("snowflake-sdk")).default;
82
229
  snowflake.configure({ logLevel: "ERROR" });
83
- const connection = snowflake.createConnection({
230
+ const connection2 = snowflake.createConnection({
84
231
  account: accountIdentifier,
85
232
  username: user,
86
233
  role,
@@ -89,13 +236,13 @@ function createSnowflakeClient(entry, connectionId) {
89
236
  privateKey
90
237
  });
91
238
  await new Promise((resolve, reject) => {
92
- connection.connect((err) => {
239
+ connection2.connect((err) => {
93
240
  if (err) reject(new Error(`Snowflake connect failed: ${err.message}`));
94
241
  else resolve();
95
242
  });
96
243
  });
97
244
  const rows = await new Promise((resolve, reject) => {
98
- connection.execute({
245
+ connection2.execute({
99
246
  sqlText: sql,
100
247
  complete: (err, _stmt, rows2) => {
101
248
  if (err) reject(new Error(`Snowflake query failed: ${err.message}`));
@@ -103,7 +250,7 @@ function createSnowflakeClient(entry, connectionId) {
103
250
  }
104
251
  });
105
252
  });
106
- connection.destroy((err) => {
253
+ connection2.destroy((err) => {
107
254
  if (err) console.warn(`[connector-client] Snowflake destroy error: ${err.message}`);
108
255
  });
109
256
  return { rows };
@@ -309,6 +456,9 @@ function createConnectorRegistry() {
309
456
  return { client: createSnowflakeClient(entry, connectionId), connectorSlug };
310
457
  }
311
458
  if (connectorSlug === "bigquery") {
459
+ if (entry.connector.authType === "oauth") {
460
+ return { client: createBigQueryOAuthClient(entry, connectionId), connectorSlug };
461
+ }
312
462
  return { client: createBigQueryClient(entry, connectionId), connectorSlug };
313
463
  }
314
464
  if (connectorSlug === "athena") {
@@ -640,6 +790,68 @@ function createDbtClient(entry, slug) {
640
790
  // src/connector-client/index.ts
641
791
  var { getClient, loadConnections, reloadEnvFile, watchConnectionsFile } = createConnectorRegistry();
642
792
 
793
+ // src/types/data-source.ts
794
+ import { z } from "zod";
795
+ var parameterMetaSchema = z.object({
796
+ name: z.string(),
797
+ type: z.enum(["string", "number", "boolean"]),
798
+ description: z.string(),
799
+ required: z.boolean().optional(),
800
+ default: z.union([z.string(), z.number(), z.boolean()]).optional()
801
+ });
802
+ var dataSourceCacheConfigSchema = z.object({
803
+ ttl: z.number(),
804
+ staleWhileRevalidate: z.boolean().optional()
805
+ });
806
+ var dataSourceSchemaObjectSchema = z.lazy(
807
+ () => z.object({
808
+ type: z.enum(["string", "number", "integer", "boolean", "object", "array", "null"]).optional(),
809
+ format: z.string().optional(),
810
+ description: z.string().optional(),
811
+ nullable: z.boolean().optional(),
812
+ enum: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(),
813
+ items: dataSourceSchemaObjectSchema.optional(),
814
+ properties: z.record(z.string(), dataSourceSchemaObjectSchema).optional(),
815
+ required: z.array(z.string()).optional(),
816
+ additionalProperties: z.union([z.boolean(), dataSourceSchemaObjectSchema]).optional(),
817
+ minimum: z.number().optional(),
818
+ maximum: z.number().optional(),
819
+ minLength: z.number().optional(),
820
+ maxLength: z.number().optional(),
821
+ pattern: z.string().optional()
822
+ })
823
+ );
824
+ var dataSourceMediaTypeSchema = z.object({
825
+ schema: dataSourceSchemaObjectSchema.optional(),
826
+ example: z.unknown().optional()
827
+ });
828
+ var dataSourceResponseSchema = z.object({
829
+ description: z.string().optional(),
830
+ defaultContentType: z.string().optional(),
831
+ content: z.record(z.string(), dataSourceMediaTypeSchema).optional()
832
+ });
833
+ var jsonBaseFields = {
834
+ description: z.string(),
835
+ parameters: z.array(parameterMetaSchema).optional(),
836
+ response: dataSourceResponseSchema.optional(),
837
+ cache: dataSourceCacheConfigSchema.optional()
838
+ };
839
+ var jsonSqlDataSourceSchema = z.object({
840
+ ...jsonBaseFields,
841
+ type: z.literal("sql").optional(),
842
+ query: z.string(),
843
+ connectionId: z.string()
844
+ });
845
+ var jsonTypeScriptDataSourceSchema = z.object({
846
+ ...jsonBaseFields,
847
+ type: z.literal("typescript"),
848
+ handlerPath: z.string()
849
+ });
850
+ var anyJsonDataSourceSchema = z.union([
851
+ jsonTypeScriptDataSourceSchema,
852
+ jsonSqlDataSourceSchema
853
+ ]);
854
+
643
855
  // src/registry.ts
644
856
  var dataSources = /* @__PURE__ */ new Map();
645
857
  var currentDirPath = "";
@@ -705,26 +917,18 @@ async function initialize() {
705
917
  jsonFiles.map(async (file) => {
706
918
  const slug = file.replace(/\.json$/, "");
707
919
  const raw = await readFile2(`${dirPath}/${file}`, "utf-8");
708
- const def = JSON.parse(raw);
709
- if (!def.description) {
710
- console.warn(`[registry] Skipping ${file}: missing description`);
711
- return;
712
- }
713
- if (!def.connectionId) {
714
- console.warn(`[registry] Skipping ${file}: missing connectionId`);
920
+ const parsed = anyJsonDataSourceSchema.safeParse(JSON.parse(raw));
921
+ if (!parsed.success) {
922
+ console.warn(`[registry] Skipping ${file}: ${parsed.error.message}`);
715
923
  return;
716
924
  }
925
+ const def = parsed.data;
717
926
  if (def.type === "typescript") {
718
- if (!def.handlerPath) {
719
- console.warn(`[registry] Skipping ${file}: missing handlerPath`);
720
- return;
721
- }
722
927
  const absoluteHandlerPath = validateHandlerPath(dirPath, def.handlerPath);
723
928
  const dataSourceDef = {
724
929
  description: def.description,
725
930
  parameters: def.parameters ?? [],
726
931
  response: def.response,
727
- connectionId: def.connectionId,
728
932
  cacheConfig: def.cache,
729
933
  handler: async () => {
730
934
  throw new Error("TypeScript handler must be called via _tsHandlerPath");
@@ -736,10 +940,6 @@ async function initialize() {
736
940
  console.log(`[registry] registered (typescript): ${slug}`);
737
941
  } else {
738
942
  const sqlDef = def;
739
- if (!sqlDef.query) {
740
- console.warn(`[registry] Skipping ${file}: missing query`);
741
- return;
742
- }
743
943
  const dataSourceDef = {
744
944
  description: sqlDef.description,
745
945
  parameters: sqlDef.parameters ?? [],
@@ -829,17 +1029,26 @@ function getDataSource(slug) {
829
1029
  return dataSources.get(slug);
830
1030
  }
831
1031
  function buildMeta(slug, def) {
832
- return {
1032
+ const base = {
833
1033
  slug,
834
1034
  description: def.description,
835
- type: def._isTypescript ? "typescript" : "sql",
836
1035
  parameters: def.parameters,
837
1036
  response: def.response,
838
- query: def._query,
839
- connectionId: def.connectionId,
840
- handlerPath: def._tsHandlerPath ? path2.relative(currentDirPath, def._tsHandlerPath) : void 0,
841
1037
  cache: def.cacheConfig
842
1038
  };
1039
+ if (def._isTypescript) {
1040
+ return {
1041
+ ...base,
1042
+ type: "typescript",
1043
+ handlerPath: path2.relative(currentDirPath, def._tsHandlerPath)
1044
+ };
1045
+ }
1046
+ return {
1047
+ ...base,
1048
+ type: "sql",
1049
+ connectionId: def.connectionId,
1050
+ query: def._query
1051
+ };
843
1052
  }
844
1053
  function getAllMeta() {
845
1054
  return Array.from(dataSources.entries()).map(
@@ -1177,6 +1386,7 @@ var pages_default = app4;
1177
1386
 
1178
1387
  // src/index.ts
1179
1388
  var apiApp = new Hono5();
1389
+ apiApp.use("/*", contextStorage());
1180
1390
  apiApp.use("/*", cors());
1181
1391
  apiApp.route("/data-source", data_source_default);
1182
1392
  apiApp.route("/data-source-meta", data_source_meta_default);
@@ -1191,6 +1401,7 @@ app5.get("/healthz", (c) => c.json({ status: "ok" }));
1191
1401
  app5.route("/api", apiApp);
1192
1402
  var src_default = app5;
1193
1403
  export {
1404
+ connection,
1194
1405
  createAirtableClient,
1195
1406
  createDbtClient,
1196
1407
  createGoogleAnalyticsClient,