@surf-ai/sdk 0.1.5-beta → 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @surf-ai/sdk
2
2
 
3
- Surf platform SDK data API client, Express server runtime, React hooks, and database helpers.
3
+ Surf SDK 1.0 for backend apps, typed Surf data access, and database helpers.
4
4
 
5
5
  ## Install
6
6
 
@@ -8,202 +8,136 @@ Surf platform SDK — data API client, Express server runtime, React hooks, and
8
8
  bun add @surf-ai/sdk
9
9
  ```
10
10
 
11
- ## Usage
11
+ ## Configuration
12
12
 
13
- ### Frontend (React hooks)
13
+ SDK 1.0 uses a single direct-auth model.
14
14
 
15
- ```tsx
16
- import { useMarketPrice, cn, useToast } from '@surf-ai/sdk/react'
15
+ | Env Var | Default | Purpose |
16
+ | --- | --- | --- |
17
+ | `SURF_API_BASE_URL` | `https://api.ask.surf/gateway/v1` | Full Surf API base URL |
18
+ | `SURF_API_KEY` | none | Bearer token used for upstream requests and protected runtime endpoints |
19
+ | `PORT` | none | Express server port when `createServer({ port })` is not provided |
20
+
21
+ All upstream SDK requests use:
17
22
 
18
- function App() {
19
- const { data, isLoading } = useMarketPrice({ symbol: 'BTC', time_range: '1d' })
20
- return (
21
- <div className={cn('p-4', isLoading && 'opacity-50')}>
22
- BTC: ${data?.data?.[0]?.value}
23
- </div>
24
- )
25
- }
23
+ ```http
24
+ Authorization: Bearer <SURF_API_KEY>
26
25
  ```
27
26
 
28
- ### Backend (data API)
27
+ ## Subpath exports
28
+
29
+ | Import | What it provides |
30
+ | --- | --- |
31
+ | `@surf-ai/sdk/server` | `createServer()`, `dataApi` |
32
+ | `@surf-ai/sdk/db` | `dbProvision()`, `dbQuery()`, `dbTables()`, `dbTableSchema()`, `dbStatus()` |
33
+
34
+ ## Data API usage
29
35
 
30
36
  ```js
31
37
  const { dataApi } = require('@surf-ai/sdk/server')
32
38
 
33
- // Typed methods grouped by category
34
39
  const btc = await dataApi.market.price({ symbol: 'BTC', time_range: '1d' })
35
- const holders = await dataApi.token.holders({ address: '0x...', chain: 'ethereum' })
36
- const trades = await dataApi.onchain.sql({ sql: 'SELECT ...', max_rows: 100 })
40
+ const holders = await dataApi.token.holders({
41
+ address: '0xdAC17F958D2ee523a2206206994597C13D831ec7',
42
+ chain: 'ethereum',
43
+ })
37
44
 
38
- // Escape hatch for new endpoints
39
- const data = await dataApi.get('newcategory/endpoint', { foo: 'bar' })
45
+ // Escape hatch for endpoints that do not have a typed helper yet.
46
+ const custom = await dataApi.get('market/price', { symbol: 'ETH', time_range: '1d' })
40
47
  ```
41
48
 
42
- ### Backend (Express server)
49
+ Available categories include:
50
+
51
+ - `market`
52
+ - `token`
53
+ - `wallet`
54
+ - `onchain`
55
+ - `social`
56
+ - `project`
57
+ - `news`
58
+ - `exchange`
59
+ - `fund`
60
+ - `search`
61
+ - `web`
62
+ - `polymarket`
63
+ - `kalshi`
64
+ - `prediction_market`
65
+
66
+ ## Server runtime
43
67
 
44
68
  ```js
45
69
  const { createServer } = require('@surf-ai/sdk/server')
46
70
 
47
- // Starts Express with /proxy/*, route auto-loading, DB sync, cron, health check
48
71
  createServer({ port: 3001 }).start()
49
72
  ```
50
73
 
51
- Routes in `routes/*.js` are auto-mounted at `/api/{name}`:
52
-
53
- ```js
54
- // routes/btc.js → /api/btc
55
- const { dataApi } = require('@surf-ai/sdk/server')
56
- const router = require('express').Router()
57
-
58
- router.get('/', async (req, res) => {
59
- const data = await dataApi.market.price({ symbol: 'BTC' })
60
- res.json(data)
61
- })
62
-
63
- module.exports = router
64
- ```
65
-
66
- ## Subpath Exports
67
-
68
- | Import | What |
69
- |--------|------|
70
- | `@surf-ai/sdk/server` | `createServer()`, `dataApi` — Express runtime + typed data API |
71
- | `@surf-ai/sdk/react` | `useMarketPrice()`, `cn()`, `useToast()` — React hooks + utilities |
72
- | `@surf-ai/sdk/db` | `dbQuery()`, `dbProvision()`, `dbTables()` — Drizzle/Neon database |
74
+ `createServer()` provides:
73
75
 
74
- ## Built-in Endpoints
76
+ - Auto-loading of `routes/*.js` and `routes/*.ts` as `/api/{name}`
77
+ - `GET /api/health`
78
+ - `POST /api/__sync-schema`
79
+ - `GET/POST/PATCH/DELETE /api/cron`
80
+ - `POST /api/cron/:id/run`
81
+ - Schema sync on startup and when `db/schema.js` changes
75
82
 
76
- `createServer()` provides these automatically:
83
+ `GET /api/health` is public.
77
84
 
78
- | Endpoint | Method | Purpose |
79
- |----------|--------|---------|
80
- | `/api/health` | GET | Health check — `{ status: 'ok' }` |
81
- | `/api/__sync-schema` | POST | Sync `db/schema.js` tables to database |
82
- | `/api/cron` | GET | List cron jobs and status |
83
- | `/api/cron` | POST | Update cron.json and reload |
84
- | `/proxy/*` | ANY | Data API passthrough to hermod |
85
+ These runtime endpoints require `Authorization: Bearer <SURF_API_KEY>`:
85
86
 
86
- Routes in `routes/*.js` are auto-loaded as `/api/{filename}`.
87
+ - `POST /api/__sync-schema`
88
+ - `GET /api/cron`
89
+ - `POST /api/cron`
90
+ - `PATCH /api/cron/:id`
91
+ - `DELETE /api/cron/:id`
92
+ - `POST /api/cron/:id/run`
87
93
 
88
- DB schema is auto-synced on startup and when `db/schema.js` changes (file watcher).
94
+ Routes you define in `routes/*` stay public unless your app adds its own auth.
89
95
 
90
- ## Environment Variables
96
+ Example route:
91
97
 
92
- The SDK auto-detects the runtime mode from environment variables. New prefixed names take priority; legacy names are supported for backward compatibility.
93
-
94
- ### Data API routing
98
+ ```js
99
+ const router = require('express').Router()
100
+ const { dataApi } = require('@surf-ai/sdk/server')
95
101
 
96
- | Env Var | Legacy | Mode | Set By |
97
- |---------|--------|------|--------|
98
- | `SURF_SANDBOX_PROXY_BASE` | `DATA_PROXY_BASE` | Sandbox | urania executor |
99
- | `SURF_DEPLOYED_GATEWAY_URL` | `GATEWAY_URL` | Deployed | Bifrost |
100
- | `SURF_DEPLOYED_APP_TOKEN` | `APP_TOKEN` | Deployed | Bifrost |
102
+ router.get('/', async (_req, res) => {
103
+ const data = await dataApi.market.price({ symbol: 'BTC', time_range: '1d' })
104
+ res.json(data)
105
+ })
101
106
 
102
- **Routing logic:**
103
- ```
104
- if SURF_SANDBOX_PROXY_BASE → sandbox (OutboundProxy handles auth)
105
- elif SURF_DEPLOYED_GATEWAY_URL + SURF_DEPLOYED_APP_TOKEN → deployed (hermod with Bearer)
106
- else → public (api.ask.surf, no auth)
107
+ module.exports = router
107
108
  ```
108
109
 
109
- ### Server
110
-
111
- | Env Var | Default | Purpose |
112
- |---------|---------|---------|
113
- | `PORT` | `3001` | Express listen port |
114
-
115
- ### Vite dev server (scaffold, not SDK)
110
+ ## Database helpers
116
111
 
117
- | Env Var | Default | Purpose |
118
- |---------|---------|---------|
119
- | `VITE_PORT` | `5173` | Frontend dev server port |
120
- | `VITE_BACKEND_PORT` | `3001` | Backend port for Vite proxy target |
121
-
122
- ### How routing works
112
+ ```js
113
+ const { dbProvision, dbQuery, dbTables, dbTableSchema, dbStatus } = require('@surf-ai/sdk/db')
123
114
 
115
+ await dbProvision()
116
+ const result = await dbQuery('SELECT * FROM users WHERE id = $1', [123], { arrayMode: true })
117
+ const tables = await dbTables()
118
+ const schema = await dbTableSchema('users')
119
+ const status = await dbStatus()
124
120
  ```
125
- Sandbox (urania preview):
126
- Frontend hook: useMarketPrice()
127
- → fetch /proxy/market/price (same-origin to Vite)
128
- → Vite proxy → Express /proxy/* → OutboundProxy (JWT) → hermod
129
-
130
- Backend route: dataApi.market.price()
131
- → fetch SURF_SANDBOX_PROXY_BASE/market/price
132
- → OutboundProxy (JWT) → hermod
133
-
134
- Deployed (surf.computer):
135
- Frontend hook: useMarketPrice()
136
- → fetch /proxy/market/price (same-origin to Express)
137
- → Express /proxy/* → hermod (APP_TOKEN)
138
-
139
- Backend route: dataApi.market.price()
140
- → fetch http://127.0.0.1:PORT/proxy/market/price (loopback)
141
- → Express /proxy/* → hermod (APP_TOKEN)
142
-
143
- Public (local dev):
144
- Frontend hook: useMarketPrice()
145
- → fetch /proxy/market/price (same-origin to Vite)
146
- → Vite proxy → Express /proxy/* → hermod (GATEWAY_URL + APP_TOKEN)
147
-
148
- Backend route: dataApi.market.price()
149
- → fetch SURF_DEPLOYED_GATEWAY_URL/gateway/v1/market/price (Bearer APP_TOKEN)
150
- ```
151
-
152
- ## Available Categories
153
-
154
- | Category | Example Methods |
155
- |----------|----------------|
156
- | `market` | `price`, `ranking`, `etf`, `futures`, `options`, `fear_greed` |
157
- | `token` | `holders`, `transfers`, `dex_trades`, `tokenomics` |
158
- | `wallet` | `detail`, `net_worth`, `labels_batch`, `transfers` |
159
- | `onchain` | `sql`, `tx`, `gas_price`, `schema`, `structured_query` |
160
- | `social` | `detail`, `mindshare`, `tweets`, `user`, `ranking` |
161
- | `project` | `detail`, `defi_metrics`, `defi_ranking` |
162
- | `news` | `detail`, `feed` |
163
- | `exchange` | `price`, `depth`, `klines`, `funding_history`, `perp` |
164
- | `fund` | `detail`, `portfolio`, `ranking` |
165
- | `search` | `project`, `news`, `wallet`, `web` |
166
- | `web` | `fetch` |
167
- | `polymarket` | `events`, `markets`, `prices`, `volumes` |
168
- | `kalshi` | `events`, `markets`, `prices`, `volumes` |
169
- | `prediction_market` | `category_metrics` |
170
-
171
- ## Codegen
172
-
173
- API methods and React hooks are auto-generated from hermod's OpenAPI spec via the surf CLI:
174
121
 
175
- ```bash
176
- # Regenerate all endpoints
177
- python scripts/gen_sdk.py
178
-
179
- # Regenerate specific endpoints
180
- python scripts/gen_sdk.py --ops market-price token-holders
181
-
182
- # Build
183
- bun run build
184
- ```
122
+ Define tables in `db/schema.js` and the runtime will provision the database and create missing tables and columns during startup.
185
123
 
186
- Requires `surf` CLI installed and authenticated (`surf login`).
124
+ Example schema:
187
125
 
188
- ## Development
126
+ ```js
127
+ const { pgTable, serial, text, timestamp } = require('drizzle-orm/pg-core')
189
128
 
190
- ```bash
191
- bun install
192
- bun run build # compile TypeScript
193
- bun test # run tests
194
- bun run codegen # regenerate from OpenAPI spec
129
+ exports.users = pgTable('users', {
130
+ id: serial('id').primaryKey(),
131
+ name: text('name').notNull(),
132
+ email: text('email'),
133
+ created_at: timestamp('created_at', { withTimezone: true }).defaultNow(),
134
+ })
195
135
  ```
196
136
 
197
- ## Testing
198
-
199
- ```bash
200
- # Unit tests
201
- bun test ./tests/data-client.test.ts
137
+ ## 1.0 migration notes
202
138
 
203
- # E2E (auto-detects mode from env vars)
204
- bun test ./tests/e2e-all-envs.test.ts
205
-
206
- # E2E with specific mode:
207
- SURF_SANDBOX_PROXY_BASE=http://127.0.0.1:9999/s/<session>/proxy bun test ./tests/e2e-all-envs.test.ts
208
- SURF_DEPLOYED_GATEWAY_URL=https://api.ask.surf SURF_DEPLOYED_APP_TOKEN=<token> bun test ./tests/e2e-all-envs.test.ts
209
- ```
139
+ - `SURF_API_BASE_URL` and `SURF_API_KEY` are the only supported SDK auth variables.
140
+ - The runtime no longer mounts `/proxy/*`.
141
+ - The `@surf-ai/sdk/react` subpath has been removed.
142
+ - Route modules must export the handler directly with `module.exports = router`.
143
+ - `createServer()` requires a port from `options.port` or `process.env.PORT`.
@@ -0,0 +1,100 @@
1
+ var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
2
+ get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
3
+ }) : x)(function(x) {
4
+ if (typeof require !== "undefined") return require.apply(this, arguments);
5
+ throw Error('Dynamic require of "' + x + '" is not supported');
6
+ });
7
+
8
+ // src/core/config.ts
9
+ var DEFAULT_API_BASE_URL = "https://api.ask.surf/gateway/v1";
10
+ function trimTrailingSlashes(value) {
11
+ return String(value || "").replace(/\/+$/, "");
12
+ }
13
+ function readSurfApiConfig() {
14
+ return {
15
+ baseUrl: trimTrailingSlashes(process.env.SURF_API_BASE_URL || DEFAULT_API_BASE_URL),
16
+ apiKey: process.env.SURF_API_KEY
17
+ };
18
+ }
19
+ function requireSurfApiConfig() {
20
+ const config = readSurfApiConfig();
21
+ if (!config.apiKey) {
22
+ throw new Error("SURF_API_KEY is required");
23
+ }
24
+ return { baseUrl: config.baseUrl, apiKey: config.apiKey };
25
+ }
26
+ function readAdminApiKey() {
27
+ return process.env.SURF_API_KEY;
28
+ }
29
+
30
+ // src/core/transport.ts
31
+ function sleep(ms) {
32
+ return new Promise((resolve) => setTimeout(resolve, ms));
33
+ }
34
+ function normalizePath(path) {
35
+ return String(path || "").replace(/^\/+/, "");
36
+ }
37
+ function buildUrl(path, params) {
38
+ const { baseUrl } = requireSurfApiConfig();
39
+ const url = new URL(`${baseUrl}/${normalizePath(path)}`);
40
+ if (params) {
41
+ for (const [key, value] of Object.entries(params)) {
42
+ if (value != null) {
43
+ url.searchParams.set(key, String(value));
44
+ }
45
+ }
46
+ }
47
+ return url.toString();
48
+ }
49
+ function buildHeaders(extra) {
50
+ const { apiKey } = requireSurfApiConfig();
51
+ const headers = new Headers(extra);
52
+ headers.set("Authorization", `Bearer ${apiKey}`);
53
+ return headers;
54
+ }
55
+ async function fetchJson(url, init, retries = 1) {
56
+ for (let attempt = 0; attempt <= retries; attempt++) {
57
+ const res = await fetch(url, init);
58
+ if (!res.ok) {
59
+ const text2 = await res.text();
60
+ throw new Error(`API error ${res.status}: ${text2.slice(0, 200)}`);
61
+ }
62
+ const text = await res.text();
63
+ if (text) {
64
+ return JSON.parse(text);
65
+ }
66
+ if (attempt < retries) {
67
+ await sleep(1e3);
68
+ }
69
+ }
70
+ throw new Error(`Empty response from ${url}`);
71
+ }
72
+ async function getJson(path, params) {
73
+ return fetchJson(buildUrl(path, params), {
74
+ headers: buildHeaders()
75
+ });
76
+ }
77
+ async function postJson(path, body) {
78
+ return fetchJson(buildUrl(path), {
79
+ method: "POST",
80
+ headers: buildHeaders({
81
+ "Content-Type": "application/json"
82
+ }),
83
+ body: body ? JSON.stringify(body) : void 0
84
+ });
85
+ }
86
+
87
+ // src/data/client.ts
88
+ async function get(path, params) {
89
+ return getJson(path, params);
90
+ }
91
+ async function post(path, body) {
92
+ return postJson(path, body);
93
+ }
94
+
95
+ export {
96
+ __require,
97
+ readAdminApiKey,
98
+ get,
99
+ post
100
+ };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  get,
3
3
  post
4
- } from "./chunk-J4OMYO3F.js";
4
+ } from "./chunk-4NA3GKVD.js";
5
5
  export {
6
6
  get,
7
7
  post
package/dist/db/index.cjs CHANGED
@@ -28,28 +28,49 @@ __export(db_exports, {
28
28
  });
29
29
  module.exports = __toCommonJS(db_exports);
30
30
 
31
- // src/data/client.ts
32
- var DEFAULT_PUBLIC_URL = "https://api.ask.surf/gateway/v1";
33
- function env(surfName, legacyName) {
34
- return process.env[surfName] || process.env[legacyName];
35
- }
36
- function resolveConfig() {
37
- const proxyBase = env("SURF_SANDBOX_PROXY_BASE", "DATA_PROXY_BASE");
38
- if (proxyBase) {
39
- return { baseUrl: proxyBase, headers: {} };
40
- }
41
- const gatewayUrl = env("SURF_DEPLOYED_GATEWAY_URL", "GATEWAY_URL");
42
- const appToken = env("SURF_DEPLOYED_APP_TOKEN", "APP_TOKEN");
43
- if (gatewayUrl && appToken) {
44
- return {
45
- baseUrl: `${gatewayUrl.replace(/\/$/, "")}/gateway/v1`,
46
- headers: { Authorization: `Bearer ${appToken}` }
47
- };
31
+ // src/core/config.ts
32
+ var DEFAULT_API_BASE_URL = "https://api.ask.surf/gateway/v1";
33
+ function trimTrailingSlashes(value) {
34
+ return String(value || "").replace(/\/+$/, "");
35
+ }
36
+ function readSurfApiConfig() {
37
+ return {
38
+ baseUrl: trimTrailingSlashes(process.env.SURF_API_BASE_URL || DEFAULT_API_BASE_URL),
39
+ apiKey: process.env.SURF_API_KEY
40
+ };
41
+ }
42
+ function requireSurfApiConfig() {
43
+ const config = readSurfApiConfig();
44
+ if (!config.apiKey) {
45
+ throw new Error("SURF_API_KEY is required");
48
46
  }
49
- return { baseUrl: DEFAULT_PUBLIC_URL, headers: {} };
47
+ return { baseUrl: config.baseUrl, apiKey: config.apiKey };
48
+ }
49
+
50
+ // src/core/transport.ts
51
+ function sleep(ms) {
52
+ return new Promise((resolve) => setTimeout(resolve, ms));
50
53
  }
51
54
  function normalizePath(path) {
52
- return String(path || "").replace(/^\/+/, "").replace(/^(?:proxy\/)+/, "");
55
+ return String(path || "").replace(/^\/+/, "");
56
+ }
57
+ function buildUrl(path, params) {
58
+ const { baseUrl } = requireSurfApiConfig();
59
+ const url = new URL(`${baseUrl}/${normalizePath(path)}`);
60
+ if (params) {
61
+ for (const [key, value] of Object.entries(params)) {
62
+ if (value != null) {
63
+ url.searchParams.set(key, String(value));
64
+ }
65
+ }
66
+ }
67
+ return url.toString();
68
+ }
69
+ function buildHeaders(extra) {
70
+ const { apiKey } = requireSurfApiConfig();
71
+ const headers = new Headers(extra);
72
+ headers.set("Authorization", `Bearer ${apiKey}`);
73
+ return headers;
53
74
  }
54
75
  async function fetchJson(url, init, retries = 1) {
55
76
  for (let attempt = 0; attempt <= retries; attempt++) {
@@ -59,39 +80,44 @@ async function fetchJson(url, init, retries = 1) {
59
80
  throw new Error(`API error ${res.status}: ${text2.slice(0, 200)}`);
60
81
  }
61
82
  const text = await res.text();
62
- if (text) return JSON.parse(text);
63
- if (attempt < retries) await new Promise((r) => setTimeout(r, 1e3));
83
+ if (text) {
84
+ return JSON.parse(text);
85
+ }
86
+ if (attempt < retries) {
87
+ await sleep(1e3);
88
+ }
64
89
  }
65
90
  throw new Error(`Empty response from ${url}`);
66
91
  }
67
- async function get(path, params) {
68
- const config = resolveConfig();
69
- const cleaned = {};
70
- if (params) {
71
- for (const [k, v] of Object.entries(params)) {
72
- if (v != null) cleaned[k] = String(v);
73
- }
74
- }
75
- const qs = Object.keys(cleaned).length ? "?" + new URLSearchParams(cleaned).toString() : "";
76
- return fetchJson(`${config.baseUrl}/${normalizePath(path)}${qs}`, {
77
- headers: config.headers
92
+ async function getJson(path, params) {
93
+ return fetchJson(buildUrl(path, params), {
94
+ headers: buildHeaders()
78
95
  });
79
96
  }
80
- async function post(path, body) {
81
- const config = resolveConfig();
82
- return fetchJson(`${config.baseUrl}/${normalizePath(path)}`, {
97
+ async function postJson(path, body) {
98
+ return fetchJson(buildUrl(path), {
83
99
  method: "POST",
84
- headers: { ...config.headers, "Content-Type": "application/json" },
100
+ headers: buildHeaders({
101
+ "Content-Type": "application/json"
102
+ }),
85
103
  body: body ? JSON.stringify(body) : void 0
86
104
  });
87
105
  }
88
106
 
107
+ // src/data/client.ts
108
+ async function get(path, params) {
109
+ return getJson(path, params);
110
+ }
111
+ async function post(path, body) {
112
+ return postJson(path, body);
113
+ }
114
+
89
115
  // src/db/index.ts
90
116
  async function dbProvision() {
91
117
  return post("db/provision");
92
118
  }
93
119
  async function dbQuery(sql, params, options) {
94
- return post("db/query", { sql, params, method: options?.arrayMode ? "all" : "execute" });
120
+ return post("db/query", { sql, params, arrayMode: options?.arrayMode ?? false });
95
121
  }
96
122
  async function dbTables() {
97
123
  return get("db/tables");
@@ -13,7 +13,7 @@
13
13
  * const result = await dbQuery('SELECT * FROM users WHERE id = $1', [userId])
14
14
  */
15
15
  /**
16
- * Provision a database for the current user via /proxy/db/provision.
16
+ * Provision a database for the current user via db/provision.
17
17
  * Returns connection metadata. Neon auto-creates the DB if it doesn't exist.
18
18
  */
19
19
  declare function dbProvision(): Promise<{
@@ -23,8 +23,11 @@ declare function dbProvision(): Promise<{
23
23
  password: string;
24
24
  }>;
25
25
  /**
26
- * Execute a SQL query via /proxy/db/query.
26
+ * Execute a SQL query via db/query.
27
27
  * Uses pg-proxy driver under the hood — Drizzle ORM calls this automatically.
28
+ *
29
+ * @param options.arrayMode - When true, rows are returned as positional arrays
30
+ * instead of objects. Required for Drizzle ORM pg-proxy compatibility.
28
31
  */
29
32
  declare function dbQuery(sql: string, params?: any[], options?: {
30
33
  arrayMode?: boolean;
@@ -13,7 +13,7 @@
13
13
  * const result = await dbQuery('SELECT * FROM users WHERE id = $1', [userId])
14
14
  */
15
15
  /**
16
- * Provision a database for the current user via /proxy/db/provision.
16
+ * Provision a database for the current user via db/provision.
17
17
  * Returns connection metadata. Neon auto-creates the DB if it doesn't exist.
18
18
  */
19
19
  declare function dbProvision(): Promise<{
@@ -23,8 +23,11 @@ declare function dbProvision(): Promise<{
23
23
  password: string;
24
24
  }>;
25
25
  /**
26
- * Execute a SQL query via /proxy/db/query.
26
+ * Execute a SQL query via db/query.
27
27
  * Uses pg-proxy driver under the hood — Drizzle ORM calls this automatically.
28
+ *
29
+ * @param options.arrayMode - When true, rows are returned as positional arrays
30
+ * instead of objects. Required for Drizzle ORM pg-proxy compatibility.
28
31
  */
29
32
  declare function dbQuery(sql: string, params?: any[], options?: {
30
33
  arrayMode?: boolean;