@squadbase/vite-server 0.1.1 → 0.1.2-dev.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.
@@ -0,0 +1,5 @@
1
+ import * as _squadbase_connectors_sdk from '@squadbase/connectors/sdk';
2
+
3
+ declare const connection: (connectionId: string) => _squadbase_connectors_sdk.GoogleAdsConnectorSdk;
4
+
5
+ export { connection };
@@ -0,0 +1,68 @@
1
+ // src/connectors/entries/google-ads-oauth.ts
2
+ import { googleAds } from "@squadbase/connectors/sdk";
3
+ import { googleAdsOauthConnector } from "@squadbase/connectors";
4
+
5
+ // src/connectors/create-connector-sdk.ts
6
+ import { readFileSync } from "fs";
7
+ import path from "path";
8
+
9
+ // src/connector-client/env.ts
10
+ function resolveEnvVar(entry, key, connectionId) {
11
+ const envVarName = entry.envVars[key];
12
+ if (!envVarName) {
13
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
14
+ }
15
+ const value = process.env[envVarName];
16
+ if (!value) {
17
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
18
+ }
19
+ return value;
20
+ }
21
+ function resolveEnvVarOptional(entry, key) {
22
+ const envVarName = entry.envVars[key];
23
+ if (!envVarName) return void 0;
24
+ return process.env[envVarName] || void 0;
25
+ }
26
+
27
+ // src/connectors/create-connector-sdk.ts
28
+ function loadConnectionsSync() {
29
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
30
+ try {
31
+ const raw = readFileSync(filePath, "utf-8");
32
+ return JSON.parse(raw);
33
+ } catch {
34
+ return {};
35
+ }
36
+ }
37
+ function createConnectorSdk(plugin, createClient) {
38
+ return (connectionId) => {
39
+ const connections = loadConnectionsSync();
40
+ const entry = connections[connectionId];
41
+ if (!entry) {
42
+ throw new Error(
43
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
44
+ );
45
+ }
46
+ if (entry.connector.slug !== plugin.slug) {
47
+ throw new Error(
48
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
49
+ );
50
+ }
51
+ const params = {};
52
+ for (const param of Object.values(plugin.parameters)) {
53
+ if (param.required) {
54
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
55
+ } else {
56
+ const val = resolveEnvVarOptional(entry, param.slug);
57
+ if (val !== void 0) params[param.slug] = val;
58
+ }
59
+ }
60
+ return createClient(params);
61
+ };
62
+ }
63
+
64
+ // src/connectors/entries/google-ads-oauth.ts
65
+ var connection = createConnectorSdk(googleAdsOauthConnector, googleAds);
66
+ export {
67
+ connection
68
+ };
@@ -0,0 +1,5 @@
1
+ import * as _squadbase_connectors_sdk from '@squadbase/connectors/sdk';
2
+
3
+ declare const connection: (connectionId: string) => _squadbase_connectors_sdk.GoogleAnalyticsOauthConnectorSdk;
4
+
5
+ export { connection };
@@ -0,0 +1,68 @@
1
+ // src/connectors/entries/google-analytics-oauth.ts
2
+ import { googleAnalyticsOauth } from "@squadbase/connectors/sdk";
3
+ import { googleAnalyticsOauthConnector } from "@squadbase/connectors";
4
+
5
+ // src/connectors/create-connector-sdk.ts
6
+ import { readFileSync } from "fs";
7
+ import path from "path";
8
+
9
+ // src/connector-client/env.ts
10
+ function resolveEnvVar(entry, key, connectionId) {
11
+ const envVarName = entry.envVars[key];
12
+ if (!envVarName) {
13
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
14
+ }
15
+ const value = process.env[envVarName];
16
+ if (!value) {
17
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
18
+ }
19
+ return value;
20
+ }
21
+ function resolveEnvVarOptional(entry, key) {
22
+ const envVarName = entry.envVars[key];
23
+ if (!envVarName) return void 0;
24
+ return process.env[envVarName] || void 0;
25
+ }
26
+
27
+ // src/connectors/create-connector-sdk.ts
28
+ function loadConnectionsSync() {
29
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
30
+ try {
31
+ const raw = readFileSync(filePath, "utf-8");
32
+ return JSON.parse(raw);
33
+ } catch {
34
+ return {};
35
+ }
36
+ }
37
+ function createConnectorSdk(plugin, createClient) {
38
+ return (connectionId) => {
39
+ const connections = loadConnectionsSync();
40
+ const entry = connections[connectionId];
41
+ if (!entry) {
42
+ throw new Error(
43
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
44
+ );
45
+ }
46
+ if (entry.connector.slug !== plugin.slug) {
47
+ throw new Error(
48
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
49
+ );
50
+ }
51
+ const params = {};
52
+ for (const param of Object.values(plugin.parameters)) {
53
+ if (param.required) {
54
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
55
+ } else {
56
+ const val = resolveEnvVarOptional(entry, param.slug);
57
+ if (val !== void 0) params[param.slug] = val;
58
+ }
59
+ }
60
+ return createClient(params);
61
+ };
62
+ }
63
+
64
+ // src/connectors/entries/google-analytics-oauth.ts
65
+ var connection = createConnectorSdk(googleAnalyticsOauthConnector, googleAnalyticsOauth);
66
+ export {
67
+ connection
68
+ };
@@ -0,0 +1,5 @@
1
+ import * as _squadbase_connectors_sdk from '@squadbase/connectors/sdk';
2
+
3
+ declare const connection: (connectionId: string) => _squadbase_connectors_sdk.GoogleSheetsConnectorSdk;
4
+
5
+ export { connection };
@@ -0,0 +1,68 @@
1
+ // src/connectors/entries/google-sheets-oauth.ts
2
+ import { googleSheets } from "@squadbase/connectors/sdk";
3
+ import { googleSheetsOauthConnector } from "@squadbase/connectors";
4
+
5
+ // src/connectors/create-connector-sdk.ts
6
+ import { readFileSync } from "fs";
7
+ import path from "path";
8
+
9
+ // src/connector-client/env.ts
10
+ function resolveEnvVar(entry, key, connectionId) {
11
+ const envVarName = entry.envVars[key];
12
+ if (!envVarName) {
13
+ throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
14
+ }
15
+ const value = process.env[envVarName];
16
+ if (!value) {
17
+ throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
18
+ }
19
+ return value;
20
+ }
21
+ function resolveEnvVarOptional(entry, key) {
22
+ const envVarName = entry.envVars[key];
23
+ if (!envVarName) return void 0;
24
+ return process.env[envVarName] || void 0;
25
+ }
26
+
27
+ // src/connectors/create-connector-sdk.ts
28
+ function loadConnectionsSync() {
29
+ const filePath = process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
30
+ try {
31
+ const raw = readFileSync(filePath, "utf-8");
32
+ return JSON.parse(raw);
33
+ } catch {
34
+ return {};
35
+ }
36
+ }
37
+ function createConnectorSdk(plugin, createClient) {
38
+ return (connectionId) => {
39
+ const connections = loadConnectionsSync();
40
+ const entry = connections[connectionId];
41
+ if (!entry) {
42
+ throw new Error(
43
+ `Connection "${connectionId}" not found in .squadbase/connections.json`
44
+ );
45
+ }
46
+ if (entry.connector.slug !== plugin.slug) {
47
+ throw new Error(
48
+ `Connection "${connectionId}" is not a ${plugin.slug} connection (got "${entry.connector.slug}")`
49
+ );
50
+ }
51
+ const params = {};
52
+ for (const param of Object.values(plugin.parameters)) {
53
+ if (param.required) {
54
+ params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
55
+ } else {
56
+ const val = resolveEnvVarOptional(entry, param.slug);
57
+ if (val !== void 0) params[param.slug] = val;
58
+ }
59
+ }
60
+ return createClient(params);
61
+ };
62
+ }
63
+
64
+ // src/connectors/entries/google-sheets-oauth.ts
65
+ var connection = createConnectorSdk(googleSheetsOauthConnector, googleSheets);
66
+ export {
67
+ connection
68
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@squadbase/vite-server",
3
- "version": "0.1.1",
3
+ "version": "0.1.2-dev.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": {
@@ -26,6 +26,18 @@
26
26
  "./connectors/openai": {
27
27
  "import": "./dist/connectors/openai.js",
28
28
  "types": "./dist/connectors/openai.d.ts"
29
+ },
30
+ "./connectors/google-sheets-oauth": {
31
+ "import": "./dist/connectors/google-sheets-oauth.js",
32
+ "types": "./dist/connectors/google-sheets-oauth.d.ts"
33
+ },
34
+ "./connectors/google-analytics-oauth": {
35
+ "import": "./dist/connectors/google-analytics-oauth.js",
36
+ "types": "./dist/connectors/google-analytics-oauth.d.ts"
37
+ },
38
+ "./connectors/google-ads-oauth": {
39
+ "import": "./dist/connectors/google-ads-oauth.js",
40
+ "types": "./dist/connectors/google-ads-oauth.d.ts"
29
41
  }
30
42
  },
31
43
  "files": [
@@ -45,7 +57,6 @@
45
57
  "build:cli": "tsup src/cli/index.ts --out-dir dist/cli --format esm --platform node --no-splitting --external pg --external snowflake-sdk --external @google-cloud/bigquery --external mysql2 --external @aws-sdk/client-athena --external @aws-sdk/client-redshift-data --external @databricks/sql --external @google-analytics/data --external @kintone/rest-api-client --external hono --external @clack/prompts"
46
58
  },
47
59
  "dependencies": {
48
- "@squadbase/connectors": "^0.1.0",
49
60
  "@aws-sdk/client-athena": "^3.750.0",
50
61
  "@aws-sdk/client-redshift-data": "^3.750.0",
51
62
  "@databricks/sql": "^1.8.0",
@@ -55,6 +66,7 @@
55
66
  "@hono/vite-build": "^1.10.0",
56
67
  "@hono/vite-dev-server": "^0.25.0",
57
68
  "@kintone/rest-api-client": "^5.5.0",
69
+ "@squadbase/connectors": "^0.1.2-dev.0",
58
70
  "hono": "^4.11.9",
59
71
  "mysql2": "^3.11.0",
60
72
  "pg": "^8.18.0",
@@ -1 +0,0 @@
1
- #!/usr/bin/env node
package/dist/cli/index.js DELETED
@@ -1,690 +0,0 @@
1
- #!/usr/bin/env node
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropNames = Object.getOwnPropertyNames;
4
- var __esm = (fn, res) => function __init() {
5
- return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
6
- };
7
- var __export = (target, all) => {
8
- for (var name in all)
9
- __defProp(target, name, { get: all[name], enumerable: true });
10
- };
11
-
12
- // src/cli/interactive.ts
13
- var interactive_exports = {};
14
- __export(interactive_exports, {
15
- confirmRunAll: () => confirmRunAll,
16
- inputParameters: () => inputParameters,
17
- selectServerLogic: () => selectServerLogic
18
- });
19
- async function getPrompts() {
20
- try {
21
- const mod = await import("@clack/prompts");
22
- return mod;
23
- } catch {
24
- throw new Error(
25
- "@clack/prompts is not installed. Run: npm install @clack/prompts --save-dev"
26
- );
27
- }
28
- }
29
- async function selectServerLogic(slugs) {
30
- const { select, isCancel } = await getPrompts();
31
- const result = await select({
32
- message: "Select a server logic to test:",
33
- options: slugs.map((s) => ({ value: s, label: s }))
34
- });
35
- if (isCancel(result)) return null;
36
- return result;
37
- }
38
- async function inputParameters(params) {
39
- if (params.length === 0) return {};
40
- const { text, isCancel } = await getPrompts();
41
- const result = {};
42
- for (const p of params) {
43
- const defaultHint = p.default !== void 0 ? ` (default: ${p.default})` : "";
44
- const requiredHint = p.required ? " *required*" : "";
45
- const value = await text({
46
- message: `${p.name}${requiredHint} [${p.type}]${defaultHint}: ${p.description}`,
47
- placeholder: p.default !== void 0 ? String(p.default) : "",
48
- validate: (v) => {
49
- if (p.required && !v && p.default === void 0) {
50
- return `${p.name} is required`;
51
- }
52
- }
53
- });
54
- if (isCancel(value)) {
55
- process.exit(0);
56
- }
57
- const strValue = value;
58
- if (!strValue && p.default !== void 0) {
59
- result[p.name] = p.default;
60
- } else if (strValue) {
61
- if (p.type === "number") result[p.name] = Number(strValue);
62
- else if (p.type === "boolean") result[p.name] = strValue === "true";
63
- else result[p.name] = strValue;
64
- }
65
- }
66
- return result;
67
- }
68
- async function confirmRunAll() {
69
- const { confirm, isCancel } = await getPrompts();
70
- const result = await confirm({
71
- message: "Run all server logics?"
72
- });
73
- if (isCancel(result)) return false;
74
- return result;
75
- }
76
- var init_interactive = __esm({
77
- "src/cli/interactive.ts"() {
78
- "use strict";
79
- }
80
- });
81
-
82
- // src/cli/index.ts
83
- import { parseArgs } from "util";
84
- import path4 from "path";
85
- import { readFile as readFile4 } from "fs/promises";
86
-
87
- // src/connector-client/registry.ts
88
- import { readFileSync, watch as fsWatch } from "fs";
89
- import { readFile } from "fs/promises";
90
- import path from "path";
91
- import { connectors } from "@squadbase/connectors";
92
- import { getContext } from "hono/context-storage";
93
- import { getCookie } from "hono/cookie";
94
-
95
- // src/connector-client/env.ts
96
- function resolveEnvVar(entry, key, connectionId) {
97
- const envVarName = entry.envVars[key];
98
- if (!envVarName) {
99
- throw new Error(`Connection "${connectionId}" is missing envVars mapping for key "${key}"`);
100
- }
101
- const value = process.env[envVarName];
102
- if (!value) {
103
- throw new Error(`Environment variable "${envVarName}" (for connection "${connectionId}", key "${key}") is not set`);
104
- }
105
- return value;
106
- }
107
- function resolveEnvVarOptional(entry, key) {
108
- const envVarName = entry.envVars[key];
109
- if (!envVarName) return void 0;
110
- return process.env[envVarName] || void 0;
111
- }
112
-
113
- // src/connector-client/registry.ts
114
- function createConnectorRegistry() {
115
- function getConnectionsFilePath() {
116
- return process.env.CONNECTIONS_PATH ?? path.join(process.cwd(), ".squadbase/connections.json");
117
- }
118
- async function loadConnections2() {
119
- const filePath = getConnectionsFilePath();
120
- try {
121
- const raw = await readFile(filePath, "utf-8");
122
- return JSON.parse(raw);
123
- } catch {
124
- return {};
125
- }
126
- }
127
- async function getQuery2(connectionId) {
128
- const connections = await loadConnections2();
129
- const entry = connections[connectionId];
130
- if (!entry) {
131
- throw new Error(
132
- `connection '${connectionId}' not found in .squadbase/connections.json`
133
- );
134
- }
135
- const { slug, authType } = entry.connector;
136
- const plugin = connectors.findByKey(slug, authType);
137
- if (!plugin) {
138
- throw new Error(
139
- `connector "${slug}" (authType: ${authType ?? "none"}) is not registered in @squadbase/connectors`
140
- );
141
- }
142
- if (!plugin.query) {
143
- throw new Error(
144
- `connector "${plugin.connectorKey}" does not support SQL queries. Non-SQL connectors (airtable, google-analytics, kintone, wix-store, dbt) should be used via TypeScript handlers.`
145
- );
146
- }
147
- const params = resolveParams(entry, connectionId, plugin);
148
- const context = { proxyFetch: createProxyFetch(connectionId) };
149
- return (sql, namedParams) => plugin.query(params, sql, namedParams, context);
150
- }
151
- function reloadEnvFile2(envPath) {
152
- try {
153
- const raw = readFileSync(envPath, "utf-8");
154
- for (const line of raw.split("\n")) {
155
- const trimmed = line.trim();
156
- if (!trimmed || trimmed.startsWith("#")) continue;
157
- const eqIdx = trimmed.indexOf("=");
158
- if (eqIdx === -1) continue;
159
- const key = trimmed.slice(0, eqIdx).trim();
160
- const value = trimmed.slice(eqIdx + 1).trim();
161
- if (key) process.env[key] = value;
162
- }
163
- console.log("[connector-client] .env reloaded");
164
- } catch {
165
- }
166
- }
167
- function watchConnectionsFile2() {
168
- const filePath = getConnectionsFilePath();
169
- const envPath = path.join(process.cwd(), ".env");
170
- try {
171
- fsWatch(filePath, { persistent: false }, () => {
172
- console.log(
173
- "[connector-client] connections.json changed"
174
- );
175
- setImmediate(() => reloadEnvFile2(envPath));
176
- });
177
- } catch {
178
- }
179
- }
180
- return { getQuery: getQuery2, loadConnections: loadConnections2, reloadEnvFile: reloadEnvFile2, watchConnectionsFile: watchConnectionsFile2 };
181
- }
182
- var APP_SESSION_COOKIE_NAME = "__Host-squadbase-session";
183
- function createSandboxProxyFetch(connectionId) {
184
- return async (input, init) => {
185
- const token = process.env.INTERNAL_SQUADBASE_OAUTH_MACHINE_CREDENTIAL;
186
- const sandboxId = process.env.INTERNAL_SQUADBASE_SANDBOX_ID;
187
- if (!token || !sandboxId) {
188
- throw new Error(
189
- "Connection proxy is not configured. Please check your deployment settings."
190
- );
191
- }
192
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
193
- const originalMethod = init?.method ?? "GET";
194
- const originalBody = init?.body ? JSON.parse(init.body) : void 0;
195
- const baseDomain = process.env["SQUADBASE_PREVIEW_BASE_DOMAIN"] ?? "preview.app.squadbase.dev";
196
- const proxyUrl = `https://${sandboxId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
197
- return fetch(proxyUrl, {
198
- method: "POST",
199
- headers: {
200
- "Content-Type": "application/json",
201
- Authorization: `Bearer ${token}`
202
- },
203
- body: JSON.stringify({
204
- url: originalUrl,
205
- method: originalMethod,
206
- body: originalBody
207
- })
208
- });
209
- };
210
- }
211
- function createDeployedAppProxyFetch(connectionId) {
212
- const projectId = process.env["SQUADBASE_PROJECT_ID"];
213
- if (!projectId) {
214
- throw new Error(
215
- "Connection proxy is not configured. Please check your deployment settings."
216
- );
217
- }
218
- const baseDomain = process.env["SQUADBASE_APP_BASE_DOMAIN"] ?? "squadbase.app";
219
- const proxyUrl = `https://${projectId}.${baseDomain}/_sqcore/connections/${connectionId}/request`;
220
- return async (input, init) => {
221
- const originalUrl = typeof input === "string" ? input : input instanceof URL ? input.href : input.url;
222
- const originalMethod = init?.method ?? "GET";
223
- const originalBody = init?.body ? JSON.parse(init.body) : void 0;
224
- const c = getContext();
225
- const appSession = getCookie(c, APP_SESSION_COOKIE_NAME);
226
- if (!appSession) {
227
- throw new Error(
228
- "No authentication method available for connection proxy."
229
- );
230
- }
231
- return fetch(proxyUrl, {
232
- method: "POST",
233
- headers: {
234
- "Content-Type": "application/json",
235
- Authorization: `Bearer ${appSession}`
236
- },
237
- body: JSON.stringify({
238
- url: originalUrl,
239
- method: originalMethod,
240
- body: originalBody
241
- })
242
- });
243
- };
244
- }
245
- function createProxyFetch(connectionId) {
246
- if (process.env.INTERNAL_SQUADBASE_SANDBOX_ID) {
247
- return createSandboxProxyFetch(connectionId);
248
- }
249
- return createDeployedAppProxyFetch(connectionId);
250
- }
251
- function resolveParams(entry, connectionId, plugin) {
252
- const params = {};
253
- for (const param of Object.values(plugin.parameters)) {
254
- if (param.required) {
255
- params[param.slug] = resolveEnvVar(entry, param.slug, connectionId);
256
- } else {
257
- const val = resolveEnvVarOptional(entry, param.slug);
258
- if (val !== void 0) params[param.slug] = val;
259
- }
260
- }
261
- return params;
262
- }
263
-
264
- // src/connector-client/index.ts
265
- var { getQuery, loadConnections, reloadEnvFile, watchConnectionsFile } = createConnectorRegistry();
266
-
267
- // src/cli/env-loader.ts
268
- import { readFileSync as readFileSync2 } from "fs";
269
- function loadEnvFile(envPath) {
270
- reloadEnvFile(envPath);
271
- }
272
-
273
- // src/cli/runner.ts
274
- import { pathToFileURL } from "url";
275
- import { readFile as readFile3, readdir as readdir2 } from "fs/promises";
276
- import path3 from "path";
277
-
278
- // src/registry.ts
279
- import { readdir, readFile as readFile2, mkdir } from "fs/promises";
280
- import { watch as fsWatch2 } from "fs";
281
- import path2 from "path";
282
-
283
- // src/types/server-logic.ts
284
- import { z } from "zod";
285
- var parameterMetaSchema = z.object({
286
- name: z.string(),
287
- type: z.enum(["string", "number", "boolean"]),
288
- description: z.string(),
289
- required: z.boolean().optional(),
290
- default: z.union([z.string(), z.number(), z.boolean()]).optional()
291
- });
292
- var serverLogicCacheConfigSchema = z.object({
293
- ttl: z.number(),
294
- staleWhileRevalidate: z.boolean().optional()
295
- });
296
- var serverLogicSchemaObjectSchema = z.lazy(
297
- () => z.object({
298
- type: z.enum(["string", "number", "integer", "boolean", "object", "array", "null"]).optional(),
299
- format: z.string().optional(),
300
- description: z.string().optional(),
301
- nullable: z.boolean().optional(),
302
- enum: z.array(z.union([z.string(), z.number(), z.boolean(), z.null()])).optional(),
303
- items: serverLogicSchemaObjectSchema.optional(),
304
- properties: z.record(z.string(), serverLogicSchemaObjectSchema).optional(),
305
- required: z.array(z.string()).optional(),
306
- additionalProperties: z.union([z.boolean(), serverLogicSchemaObjectSchema]).optional(),
307
- minimum: z.number().optional(),
308
- maximum: z.number().optional(),
309
- minLength: z.number().optional(),
310
- maxLength: z.number().optional(),
311
- pattern: z.string().optional()
312
- })
313
- );
314
- var serverLogicMediaTypeSchema = z.object({
315
- schema: serverLogicSchemaObjectSchema.optional(),
316
- example: z.unknown().optional()
317
- });
318
- var serverLogicResponseSchema = z.object({
319
- description: z.string().optional(),
320
- content: z.record(z.string(), serverLogicMediaTypeSchema).optional()
321
- });
322
- var jsonBaseFields = {
323
- description: z.string(),
324
- parameters: z.array(parameterMetaSchema).optional(),
325
- response: serverLogicResponseSchema.optional(),
326
- cache: serverLogicCacheConfigSchema.optional()
327
- };
328
- var jsonSqlServerLogicSchema = z.object({
329
- ...jsonBaseFields,
330
- type: z.literal("sql").optional(),
331
- query: z.string(),
332
- connectionId: z.string()
333
- });
334
- var jsonTypeScriptServerLogicSchema = z.object({
335
- ...jsonBaseFields,
336
- type: z.literal("typescript"),
337
- handlerPath: z.string()
338
- });
339
- var anyJsonServerLogicSchema = z.union([
340
- jsonTypeScriptServerLogicSchema,
341
- jsonSqlServerLogicSchema
342
- ]);
343
-
344
- // src/registry.ts
345
- function applyDefaults(parameterMeta, runtimeParams) {
346
- const defaults = new Map(
347
- parameterMeta.map((p) => [p.name, p.default ?? null])
348
- );
349
- const result = {};
350
- for (const [key, value] of Object.entries(runtimeParams)) {
351
- result[key] = value;
352
- }
353
- for (const [key, defaultVal] of defaults) {
354
- if (!(key in result)) {
355
- result[key] = defaultVal;
356
- }
357
- }
358
- return result;
359
- }
360
- var defaultServerLogicDir = path2.join(process.cwd(), "server-logic");
361
-
362
- // src/cli/runner.ts
363
- function createStubContext(params) {
364
- const stub = {
365
- req: {
366
- json: () => Promise.resolve(params),
367
- query: (name) => {
368
- if (name === void 0) {
369
- return Object.fromEntries(
370
- Object.entries(params).map(([k, v2]) => [k, String(v2)])
371
- );
372
- }
373
- const v = params[name];
374
- return v !== void 0 ? String(v) : "";
375
- },
376
- param: (_name) => void 0,
377
- header: (_name) => void 0,
378
- raw: new Request("http://localhost/cli")
379
- },
380
- json: (data) => data,
381
- text: (data) => data,
382
- body: (data) => data,
383
- env: {},
384
- var: {},
385
- get: (_key) => void 0,
386
- set: () => {
387
- }
388
- };
389
- return stub;
390
- }
391
- async function runSqlServerLogic(slug, def, params, limit) {
392
- const start = Date.now();
393
- try {
394
- const query = await getQuery(def.connectionId);
395
- const namedParams = applyDefaults(def.parameters ?? [], params);
396
- const result = await query(def.query, namedParams);
397
- const rows = result.rows.slice(0, limit);
398
- return {
399
- slug,
400
- rows,
401
- rowCount: result.rows.length,
402
- durationMs: Date.now() - start,
403
- query: def.query
404
- };
405
- } catch (error) {
406
- return {
407
- slug,
408
- rows: [],
409
- rowCount: 0,
410
- durationMs: Date.now() - start,
411
- error: error instanceof Error ? error : new Error(String(error))
412
- };
413
- }
414
- }
415
- async function runTypescriptServerLogic(slug, handlerPath, params) {
416
- const start = Date.now();
417
- try {
418
- const mod = await import(pathToFileURL(handlerPath).href);
419
- const handler = mod.default;
420
- if (typeof handler !== "function") {
421
- throw new Error(`Handler must export a default function: ${handlerPath}`);
422
- }
423
- const ctx = createStubContext(params);
424
- const raw = await handler(ctx);
425
- let rows;
426
- if (Array.isArray(raw)) {
427
- rows = raw;
428
- } else if (raw !== null && typeof raw === "object") {
429
- rows = [raw];
430
- } else {
431
- rows = [{ result: raw }];
432
- }
433
- return {
434
- slug,
435
- rows,
436
- rowCount: rows.length,
437
- durationMs: Date.now() - start
438
- };
439
- } catch (error) {
440
- return {
441
- slug,
442
- rows: [],
443
- rowCount: 0,
444
- durationMs: Date.now() - start,
445
- error: error instanceof Error ? error : new Error(String(error))
446
- };
447
- }
448
- }
449
- async function runServerLogic(slug, dirPath, params, limit) {
450
- const jsonPath = path3.join(dirPath, `${slug}.json`);
451
- let def;
452
- try {
453
- const raw = await readFile3(jsonPath, "utf-8");
454
- const parsed = anyJsonServerLogicSchema.safeParse(JSON.parse(raw));
455
- if (!parsed.success) {
456
- return {
457
- slug,
458
- rows: [],
459
- rowCount: 0,
460
- durationMs: 0,
461
- error: new Error(`Invalid server logic definition: ${parsed.error.message}`)
462
- };
463
- }
464
- def = parsed.data;
465
- } catch {
466
- return {
467
- slug,
468
- rows: [],
469
- rowCount: 0,
470
- durationMs: 0,
471
- error: new Error(`Server logic not found: ${jsonPath}`)
472
- };
473
- }
474
- if (def.type === "typescript") {
475
- const absolutePath = path3.resolve(dirPath, def.handlerPath);
476
- return runTypescriptServerLogic(slug, absolutePath, params);
477
- }
478
- return runSqlServerLogic(slug, def, params, limit);
479
- }
480
- async function listSlugs(dirPath) {
481
- try {
482
- const files = await readdir2(dirPath);
483
- return files.filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
484
- } catch {
485
- return [];
486
- }
487
- }
488
- async function runAll(dirPath, params, limit) {
489
- const slugs = await listSlugs(dirPath);
490
- return Promise.all(slugs.map((slug) => runServerLogic(slug, dirPath, params, limit)));
491
- }
492
-
493
- // src/cli/display.ts
494
- var RESET = "\x1B[0m";
495
- var BOLD = "\x1B[1m";
496
- var RED = "\x1B[31m";
497
- var GREEN = "\x1B[32m";
498
- var YELLOW = "\x1B[33m";
499
- var CYAN = "\x1B[36m";
500
- var DIM = "\x1B[2m";
501
- function truncate(value, maxLen) {
502
- return value.length > maxLen ? value.slice(0, maxLen - 1) + "\u2026" : value;
503
- }
504
- function formatValue(value) {
505
- if (value === null || value === void 0) return DIM + "null" + RESET;
506
- if (typeof value === "object") return JSON.stringify(value);
507
- return String(value);
508
- }
509
- function displayTable(rows, limit) {
510
- if (rows.length === 0) {
511
- console.log(DIM + " (no rows)" + RESET);
512
- return;
513
- }
514
- const display = rows.slice(0, limit);
515
- const columns = Object.keys(display[0] ?? {});
516
- const colWidths = columns.map((col) => {
517
- const maxData = Math.max(...display.map((r) => formatValue(r[col]).replace(/\x1b\[[0-9;]*m/g, "").length));
518
- return Math.min(40, Math.max(col.length, maxData));
519
- });
520
- const header = columns.map((c, i) => BOLD + CYAN + c.padEnd(colWidths[i] ?? 0) + RESET).join(" ");
521
- const divider = colWidths.map((w) => "-".repeat(w)).join("--");
522
- console.log(" " + header);
523
- console.log(" " + DIM + divider + RESET);
524
- for (const row of display) {
525
- const line = columns.map((c, i) => {
526
- const raw = formatValue(row[c]);
527
- const plain = raw.replace(/\x1b\[[0-9;]*m/g, "");
528
- const padded = truncate(plain, colWidths[i] ?? 40).padEnd(colWidths[i] ?? 0);
529
- return raw.startsWith(DIM) ? DIM + padded + RESET : padded;
530
- }).join(" ");
531
- console.log(" " + line);
532
- }
533
- if (rows.length > limit) {
534
- console.log(DIM + ` \u2026 and ${rows.length - limit} more rows (use --limit to show more)` + RESET);
535
- }
536
- }
537
- function displaySummary(result) {
538
- const status = result.error ? RED + "\u2717 FAIL" + RESET : GREEN + "\u2713 OK" + RESET;
539
- const duration = DIM + `(${result.durationMs}ms)` + RESET;
540
- console.log(`
541
- ${BOLD}[${result.slug}]${RESET} ${status} ${duration}`);
542
- if (result.error) {
543
- console.log(RED + ` Error: ${result.error.message}` + RESET);
544
- return;
545
- }
546
- console.log(DIM + ` ${result.rowCount} row(s)` + RESET);
547
- }
548
- function displayDebug(result) {
549
- if (result.query) {
550
- console.log(YELLOW + " Query:" + RESET);
551
- console.log(DIM + ` ${result.query.replace(/\n/g, "\n ")}` + RESET);
552
- }
553
- if (result.queryValues && result.queryValues.length > 0) {
554
- console.log(YELLOW + " Params:" + RESET, result.queryValues);
555
- }
556
- }
557
- function displayJson(results) {
558
- const output = results.map((r) => ({
559
- slug: r.slug,
560
- rowCount: r.rowCount,
561
- durationMs: r.durationMs,
562
- rows: r.rows,
563
- error: r.error?.message
564
- }));
565
- console.log(JSON.stringify(output, null, 2));
566
- }
567
- function displayError(error) {
568
- console.error(RED + "Error: " + error.message + RESET);
569
- }
570
-
571
- // src/cli/index.ts
572
- var HELP = `
573
- Usage: squadbase-sl-test [options]
574
-
575
- Options:
576
- --slug <slug> Run a specific server logic
577
- --all Run all server logics
578
- --params k=v,... Comma-separated key=value parameters
579
- --env <path> Path to .env file (default: ../../.env)
580
- --dir <path> Server logic directory (default: ./server-logic)
581
- --format table|json Output format (default: table)
582
- --limit <n> Max rows to display (default: 50)
583
- --debug Show SQL query and parameter values
584
- --help Show this help
585
-
586
- Examples:
587
- npx tsx src/cli/index.ts --slug sales-summary
588
- npx tsx src/cli/index.ts --slug sales-summary --params year=2024,limit=10
589
- npx tsx src/cli/index.ts --all --format json
590
- npx tsx src/cli/index.ts # interactive mode
591
- `;
592
- async function main() {
593
- const { values } = parseArgs({
594
- options: {
595
- slug: { type: "string" },
596
- all: { type: "boolean", default: false },
597
- params: { type: "string" },
598
- env: { type: "string" },
599
- dir: { type: "string" },
600
- format: { type: "string", default: "table" },
601
- limit: { type: "string", default: "50" },
602
- debug: { type: "boolean", default: false },
603
- help: { type: "boolean", default: false }
604
- },
605
- allowPositionals: false
606
- });
607
- if (values.help) {
608
- console.log(HELP);
609
- process.exit(0);
610
- }
611
- const cwd = process.cwd();
612
- const dirPath = values.dir ? path4.resolve(cwd, values.dir) : path4.join(cwd, "server-logic");
613
- const envPath = values.env ? path4.resolve(cwd, values.env) : path4.join(cwd, "../../.env");
614
- const limit = parseInt(values.limit ?? "50", 10);
615
- const format = values.format ?? "table";
616
- loadEnvFile(envPath);
617
- const params = {};
618
- if (values.params) {
619
- for (const pair of values.params.split(",")) {
620
- const eqIdx = pair.indexOf("=");
621
- if (eqIdx === -1) continue;
622
- const key = pair.slice(0, eqIdx).trim();
623
- const val = pair.slice(eqIdx + 1).trim();
624
- params[key] = val;
625
- }
626
- }
627
- if (values.slug) {
628
- const result = await runServerLogic(values.slug, dirPath, params, limit);
629
- if (format === "json") {
630
- displayJson([result]);
631
- } else {
632
- displaySummary(result);
633
- if (values.debug) displayDebug(result);
634
- if (!result.error) displayTable(result.rows, limit);
635
- }
636
- if (result.error) process.exit(1);
637
- } else if (values.all) {
638
- const results = await runAll(dirPath, params, limit);
639
- if (format === "json") {
640
- displayJson(results);
641
- } else {
642
- for (const r of results) {
643
- displaySummary(r);
644
- if (values.debug) displayDebug(r);
645
- if (!r.error) displayTable(r.rows, limit);
646
- }
647
- const failed = results.filter((r) => r.error).length;
648
- console.log(`
649
- Total: ${results.length}, Failed: ${failed}`);
650
- }
651
- const anyFailed = results.some((r) => r.error);
652
- if (anyFailed) process.exit(1);
653
- } else {
654
- const slugs = await listSlugs(dirPath);
655
- if (slugs.length === 0) {
656
- displayError(new Error(`No server logics found in ${dirPath}`));
657
- process.exit(1);
658
- }
659
- try {
660
- const { selectServerLogic: selectServerLogic2, inputParameters: inputParameters2 } = await Promise.resolve().then(() => (init_interactive(), interactive_exports));
661
- const slug = await selectServerLogic2(slugs);
662
- if (!slug) {
663
- console.log("Cancelled.");
664
- process.exit(0);
665
- }
666
- const jsonPath = path4.join(dirPath, `${slug}.json`);
667
- let paramMeta = [];
668
- try {
669
- const raw = await readFile4(jsonPath, "utf-8");
670
- const parsed = anyJsonServerLogicSchema.safeParse(JSON.parse(raw));
671
- if (parsed.success) paramMeta = parsed.data.parameters ?? [];
672
- } catch {
673
- }
674
- const interactiveParams = await inputParameters2(paramMeta);
675
- const merged = { ...interactiveParams, ...params };
676
- const result = await runServerLogic(slug, dirPath, merged, limit);
677
- displaySummary(result);
678
- if (values.debug) displayDebug(result);
679
- if (!result.error) displayTable(result.rows, limit);
680
- if (result.error) process.exit(1);
681
- } catch (err) {
682
- displayError(err instanceof Error ? err : new Error(String(err)));
683
- process.exit(1);
684
- }
685
- }
686
- }
687
- main().catch((err) => {
688
- displayError(err instanceof Error ? err : new Error(String(err)));
689
- process.exit(1);
690
- });