@surf-ai/sdk 0.1.6-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 +86 -375
- package/dist/chunk-4NA3GKVD.js +100 -0
- package/dist/{client-3YMIRPDV.js → client-Z45B2GYT.js} +1 -1
- package/dist/db/index.cjs +62 -36
- package/dist/db/index.d.cts +2 -2
- package/dist/db/index.d.ts +2 -2
- package/dist/db/index.js +62 -36
- package/dist/server/index.cjs +101 -104
- package/dist/server/index.d.cts +9 -15
- package/dist/server/index.d.ts +9 -15
- package/dist/server/index.js +23 -67
- package/package.json +6 -29
- package/dist/chunk-J4OMYO3F.js +0 -70
- package/dist/react/index.d.ts +0 -3650
- package/dist/react/index.js +0 -1175
- package/src/theme/index.css +0 -314
package/dist/server/index.js
CHANGED
|
@@ -1,26 +1,35 @@
|
|
|
1
1
|
import {
|
|
2
2
|
__require,
|
|
3
3
|
get,
|
|
4
|
-
post
|
|
5
|
-
|
|
4
|
+
post,
|
|
5
|
+
readAdminApiKey
|
|
6
|
+
} from "../chunk-4NA3GKVD.js";
|
|
6
7
|
|
|
7
8
|
// src/server/runtime.ts
|
|
8
9
|
import express from "express";
|
|
9
10
|
import cors from "cors";
|
|
10
11
|
import fs from "fs";
|
|
11
12
|
import path from "path";
|
|
12
|
-
import { createProxyMiddleware, responseInterceptor } from "http-proxy-middleware";
|
|
13
13
|
import { Cron } from "croner";
|
|
14
|
+
function requireBearerAuth(req, res, next) {
|
|
15
|
+
const apiKey = readAdminApiKey();
|
|
16
|
+
if (!apiKey) {
|
|
17
|
+
return res.status(503).json({ error: "SURF_API_KEY is not configured" });
|
|
18
|
+
}
|
|
19
|
+
if (req.headers.authorization !== `Bearer ${apiKey}`) {
|
|
20
|
+
return res.status(401).json({ error: "Unauthorized" });
|
|
21
|
+
}
|
|
22
|
+
next();
|
|
23
|
+
}
|
|
14
24
|
function createServer(options = {}) {
|
|
15
|
-
const port = options.port
|
|
25
|
+
const port = options.port ?? (process.env.PORT ? Number.parseInt(process.env.PORT, 10) : void 0);
|
|
26
|
+
if (!Number.isInteger(port)) {
|
|
27
|
+
throw new Error("createServer requires a port via options.port or process.env.PORT");
|
|
28
|
+
}
|
|
16
29
|
const routesDir = options.routesDir || path.join(process.cwd(), "routes");
|
|
17
30
|
const cronDir = options.cronDir || process.cwd();
|
|
18
|
-
const enableProxy = options.proxy !== false;
|
|
19
31
|
const app = express();
|
|
20
32
|
app.use(cors());
|
|
21
|
-
if (enableProxy) {
|
|
22
|
-
setupProxy(app, port);
|
|
23
|
-
}
|
|
24
33
|
app.use(express.json());
|
|
25
34
|
app.get("/api/health", (_req, res) => {
|
|
26
35
|
res.json({ status: "ok" });
|
|
@@ -30,12 +39,13 @@ function createServer(options = {}) {
|
|
|
30
39
|
if (!file.endsWith(".js") && !file.endsWith(".ts")) continue;
|
|
31
40
|
const name = file.replace(/\.(js|ts)$/, "");
|
|
32
41
|
try {
|
|
33
|
-
const
|
|
34
|
-
const handler = route.default || route;
|
|
42
|
+
const handler = __require(path.join(routesDir, file));
|
|
35
43
|
if (typeof handler === "function") {
|
|
36
44
|
app.use(`/api/${name}`, handler);
|
|
37
45
|
console.log(`Route registered: /api/${name}`);
|
|
46
|
+
continue;
|
|
38
47
|
}
|
|
48
|
+
throw new Error(`Route module must export a handler function via module.exports`);
|
|
39
49
|
} catch (err) {
|
|
40
50
|
console.error(`Failed to load route ${file}: ${err.message}`);
|
|
41
51
|
}
|
|
@@ -59,51 +69,6 @@ function createServer(options = {}) {
|
|
|
59
69
|
}
|
|
60
70
|
};
|
|
61
71
|
}
|
|
62
|
-
function env(surfName, legacyName) {
|
|
63
|
-
return process.env[surfName] || process.env[legacyName];
|
|
64
|
-
}
|
|
65
|
-
function setupProxy(app, port) {
|
|
66
|
-
const gatewayUrl = env("SURF_DEPLOYED_GATEWAY_URL", "GATEWAY_URL");
|
|
67
|
-
const appToken = env("SURF_DEPLOYED_APP_TOKEN", "APP_TOKEN");
|
|
68
|
-
const proxyBase = env("SURF_SANDBOX_PROXY_BASE", "DATA_PROXY_BASE");
|
|
69
|
-
const isDeployed = Boolean(gatewayUrl && appToken);
|
|
70
|
-
const bufferResponse = responseInterceptor(async (buf) => buf);
|
|
71
|
-
if (isDeployed) {
|
|
72
|
-
app.use("/proxy", createProxyMiddleware({
|
|
73
|
-
target: gatewayUrl,
|
|
74
|
-
changeOrigin: true,
|
|
75
|
-
selfHandleResponse: true,
|
|
76
|
-
pathRewrite: (p) => "/gateway/v1" + p,
|
|
77
|
-
headers: {
|
|
78
|
-
Authorization: `Bearer ${appToken}`,
|
|
79
|
-
"Accept-Encoding": "identity"
|
|
80
|
-
},
|
|
81
|
-
on: { proxyRes: bufferResponse }
|
|
82
|
-
}));
|
|
83
|
-
const loopback = `http://127.0.0.1:${port}/proxy`;
|
|
84
|
-
process.env.SURF_SANDBOX_PROXY_BASE = loopback;
|
|
85
|
-
process.env.DATA_PROXY_BASE = loopback;
|
|
86
|
-
} else if (proxyBase) {
|
|
87
|
-
const target = proxyBase.replace(/\/proxy$/, "");
|
|
88
|
-
app.use(createProxyMiddleware({
|
|
89
|
-
target,
|
|
90
|
-
changeOrigin: true,
|
|
91
|
-
pathFilter: "/proxy",
|
|
92
|
-
on: {
|
|
93
|
-
proxyReq: (proxyReq, req) => {
|
|
94
|
-
console.log(`[proxy] >> ${req.method} ${req.originalUrl}`);
|
|
95
|
-
},
|
|
96
|
-
proxyRes: (proxyRes, req) => {
|
|
97
|
-
console.log(`[proxy] << ${proxyRes.statusCode} ${req.method} ${req.originalUrl}`);
|
|
98
|
-
},
|
|
99
|
-
error: (err, req, res) => {
|
|
100
|
-
console.error(`[proxy] !! ${req.method} ${req.originalUrl} error: ${err.message}`);
|
|
101
|
-
if (!res.headersSent) res.status(502).json({ error: err.message });
|
|
102
|
-
}
|
|
103
|
-
}
|
|
104
|
-
}));
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
72
|
var schemaSync = { run: async () => {
|
|
108
73
|
}, watch: () => {
|
|
109
74
|
} };
|
|
@@ -143,7 +108,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
143
108
|
(t) => t && typeof t === "object" && /* @__PURE__ */ Symbol.for("drizzle:Name") in t
|
|
144
109
|
);
|
|
145
110
|
if (tables.length === 0) return;
|
|
146
|
-
const { get: dbGet, post: dbPost } = await import("../client-
|
|
111
|
+
const { get: dbGet, post: dbPost } = await import("../client-Z45B2GYT.js");
|
|
147
112
|
await dbPost("db/provision");
|
|
148
113
|
const existing = (await dbGet("db/tables")).map((t) => t.name);
|
|
149
114
|
const missing = tables.filter((t) => !existing.includes(getTableConfig(t).name));
|
|
@@ -207,7 +172,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
207
172
|
}
|
|
208
173
|
console.error("DB schema sync failed after all retries");
|
|
209
174
|
}
|
|
210
|
-
app.post("/api/__sync-schema", async (_req, res) => {
|
|
175
|
+
app.post("/api/__sync-schema", requireBearerAuth, async (_req, res) => {
|
|
211
176
|
try {
|
|
212
177
|
await syncWithRetry(2, 1500);
|
|
213
178
|
res.json({ ok: true });
|
|
@@ -311,16 +276,7 @@ function setupCron(app, cronDir) {
|
|
|
311
276
|
console.log(`Cron registered: [${task.id}] ${task.name} (${task.schedule})`);
|
|
312
277
|
}
|
|
313
278
|
}
|
|
314
|
-
|
|
315
|
-
app.use("/api/cron", (req, res, next) => {
|
|
316
|
-
const appToken = envFn("SURF_DEPLOYED_APP_TOKEN", "APP_TOKEN");
|
|
317
|
-
if (!appToken) return next();
|
|
318
|
-
const auth = req.headers.authorization;
|
|
319
|
-
if (!auth || auth !== `Bearer ${appToken}`) {
|
|
320
|
-
return res.status(401).json({ error: "Unauthorized" });
|
|
321
|
-
}
|
|
322
|
-
next();
|
|
323
|
-
});
|
|
279
|
+
app.use("/api/cron", requireBearerAuth);
|
|
324
280
|
app.get("/api/cron", (_req, res) => {
|
|
325
281
|
res.json(cronTasks.map((t) => {
|
|
326
282
|
const state = cronState.get(t.id) || { lastRunAt: null, lastStatus: null, lastError: null };
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@surf-ai/sdk",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Surf platform SDK — data API client, server runtime,
|
|
3
|
+
"version": "1.0.0-alpha.0",
|
|
4
|
+
"description": "Surf platform SDK — data API client, server runtime, and database helpers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"exports": {
|
|
7
7
|
"./server": {
|
|
@@ -9,20 +9,14 @@
|
|
|
9
9
|
"import": "./dist/server/index.js",
|
|
10
10
|
"require": "./dist/server/index.cjs"
|
|
11
11
|
},
|
|
12
|
-
"./react": {
|
|
13
|
-
"types": "./dist/react/index.d.ts",
|
|
14
|
-
"import": "./dist/react/index.js"
|
|
15
|
-
},
|
|
16
12
|
"./db": {
|
|
17
13
|
"types": "./dist/db/index.d.ts",
|
|
18
14
|
"import": "./dist/db/index.js",
|
|
19
15
|
"require": "./dist/db/index.cjs"
|
|
20
|
-
}
|
|
21
|
-
"./theme.css": "./src/theme/index.css"
|
|
16
|
+
}
|
|
22
17
|
},
|
|
23
18
|
"files": [
|
|
24
|
-
"dist"
|
|
25
|
-
"src/theme"
|
|
19
|
+
"dist"
|
|
26
20
|
],
|
|
27
21
|
"scripts": {
|
|
28
22
|
"build": "tsup",
|
|
@@ -33,30 +27,13 @@
|
|
|
33
27
|
"dependencies": {
|
|
34
28
|
"express": "^4.22.0",
|
|
35
29
|
"cors": "^2.8.5",
|
|
36
|
-
"http-proxy-middleware": "^3.0.0",
|
|
37
30
|
"croner": "^9.0.0",
|
|
38
|
-
"drizzle-orm": "^0.44.0"
|
|
39
|
-
"@tanstack/react-query": "^5.94.0",
|
|
40
|
-
"clsx": "^2.1.0",
|
|
41
|
-
"tailwind-merge": "^2.6.0"
|
|
42
|
-
},
|
|
43
|
-
"peerDependencies": {
|
|
44
|
-
"react": "^19.0.0",
|
|
45
|
-
"react-dom": "^19.0.0"
|
|
46
|
-
},
|
|
47
|
-
"peerDependenciesMeta": {
|
|
48
|
-
"react": {
|
|
49
|
-
"optional": true
|
|
50
|
-
},
|
|
51
|
-
"react-dom": {
|
|
52
|
-
"optional": true
|
|
53
|
-
}
|
|
31
|
+
"drizzle-orm": "^0.44.0"
|
|
54
32
|
},
|
|
55
33
|
"devDependencies": {
|
|
56
34
|
"tsup": "^8.0.0",
|
|
57
35
|
"typescript": "^5.9.0",
|
|
58
36
|
"@types/express": "^5.0.0",
|
|
59
|
-
"@types/cors": "^2.8.0"
|
|
60
|
-
"@types/react": "^19.0.0"
|
|
37
|
+
"@types/cors": "^2.8.0"
|
|
61
38
|
}
|
|
62
39
|
}
|
package/dist/chunk-J4OMYO3F.js
DELETED
|
@@ -1,70 +0,0 @@
|
|
|
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/data/client.ts
|
|
9
|
-
var DEFAULT_PUBLIC_URL = "https://api.ask.surf/gateway/v1";
|
|
10
|
-
function env(surfName, legacyName) {
|
|
11
|
-
return process.env[surfName] || process.env[legacyName];
|
|
12
|
-
}
|
|
13
|
-
function resolveConfig() {
|
|
14
|
-
const proxyBase = env("SURF_SANDBOX_PROXY_BASE", "DATA_PROXY_BASE");
|
|
15
|
-
if (proxyBase) {
|
|
16
|
-
return { baseUrl: proxyBase, headers: {} };
|
|
17
|
-
}
|
|
18
|
-
const gatewayUrl = env("SURF_DEPLOYED_GATEWAY_URL", "GATEWAY_URL");
|
|
19
|
-
const appToken = env("SURF_DEPLOYED_APP_TOKEN", "APP_TOKEN");
|
|
20
|
-
if (gatewayUrl && appToken) {
|
|
21
|
-
return {
|
|
22
|
-
baseUrl: `${gatewayUrl.replace(/\/$/, "")}/gateway/v1`,
|
|
23
|
-
headers: { Authorization: `Bearer ${appToken}` }
|
|
24
|
-
};
|
|
25
|
-
}
|
|
26
|
-
return { baseUrl: DEFAULT_PUBLIC_URL, headers: {} };
|
|
27
|
-
}
|
|
28
|
-
function normalizePath(path) {
|
|
29
|
-
return String(path || "").replace(/^\/+/, "").replace(/^(?:proxy\/)+/, "");
|
|
30
|
-
}
|
|
31
|
-
async function fetchJson(url, init, retries = 1) {
|
|
32
|
-
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
33
|
-
const res = await fetch(url, init);
|
|
34
|
-
if (!res.ok) {
|
|
35
|
-
const text2 = await res.text();
|
|
36
|
-
throw new Error(`API error ${res.status}: ${text2.slice(0, 200)}`);
|
|
37
|
-
}
|
|
38
|
-
const text = await res.text();
|
|
39
|
-
if (text) return JSON.parse(text);
|
|
40
|
-
if (attempt < retries) await new Promise((r) => setTimeout(r, 1e3));
|
|
41
|
-
}
|
|
42
|
-
throw new Error(`Empty response from ${url}`);
|
|
43
|
-
}
|
|
44
|
-
async function get(path, params) {
|
|
45
|
-
const config = resolveConfig();
|
|
46
|
-
const cleaned = {};
|
|
47
|
-
if (params) {
|
|
48
|
-
for (const [k, v] of Object.entries(params)) {
|
|
49
|
-
if (v != null) cleaned[k] = String(v);
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const qs = Object.keys(cleaned).length ? "?" + new URLSearchParams(cleaned).toString() : "";
|
|
53
|
-
return fetchJson(`${config.baseUrl}/${normalizePath(path)}${qs}`, {
|
|
54
|
-
headers: config.headers
|
|
55
|
-
});
|
|
56
|
-
}
|
|
57
|
-
async function post(path, body) {
|
|
58
|
-
const config = resolveConfig();
|
|
59
|
-
return fetchJson(`${config.baseUrl}/${normalizePath(path)}`, {
|
|
60
|
-
method: "POST",
|
|
61
|
-
headers: { ...config.headers, "Content-Type": "application/json" },
|
|
62
|
-
body: body ? JSON.stringify(body) : void 0
|
|
63
|
-
});
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
export {
|
|
67
|
-
__require,
|
|
68
|
-
get,
|
|
69
|
-
post
|
|
70
|
-
};
|