@surf-ai/sdk 1.0.0-alpha.0 → 1.0.0-alpha.10
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 +1 -1
- package/dist/db/index.cjs +157 -4
- package/dist/db/index.d.cts +35 -1
- package/dist/db/index.d.ts +35 -1
- package/dist/db/index.js +151 -3
- package/dist/server/index.cjs +173 -186
- package/dist/server/index.js +254 -134
- package/package.json +2 -2
- package/dist/chunk-4NA3GKVD.js +0 -100
- package/dist/client-Z45B2GYT.js +0 -8
package/dist/server/index.cjs
CHANGED
|
@@ -5,9 +5,6 @@ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
|
5
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
6
|
var __getProtoOf = Object.getPrototypeOf;
|
|
7
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
-
var __esm = (fn, res) => function __init() {
|
|
9
|
-
return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
|
|
10
|
-
};
|
|
11
8
|
var __export = (target, all) => {
|
|
12
9
|
for (var name in all)
|
|
13
10
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
@@ -30,7 +27,23 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
30
27
|
));
|
|
31
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
32
29
|
|
|
30
|
+
// src/server/index.ts
|
|
31
|
+
var server_exports = {};
|
|
32
|
+
__export(server_exports, {
|
|
33
|
+
createServer: () => createServer,
|
|
34
|
+
dataApi: () => dataApi
|
|
35
|
+
});
|
|
36
|
+
module.exports = __toCommonJS(server_exports);
|
|
37
|
+
|
|
38
|
+
// src/server/runtime.ts
|
|
39
|
+
var import_express = __toESM(require("express"), 1);
|
|
40
|
+
var import_cors = __toESM(require("cors"), 1);
|
|
41
|
+
var import_fs2 = __toESM(require("fs"), 1);
|
|
42
|
+
var import_path = __toESM(require("path"), 1);
|
|
43
|
+
var import_croner = require("croner");
|
|
44
|
+
|
|
33
45
|
// src/core/config.ts
|
|
46
|
+
var DEFAULT_API_BASE_URL = "https://api.asksurf.ai/gateway/v1";
|
|
34
47
|
function trimTrailingSlashes(value) {
|
|
35
48
|
return String(value || "").replace(/\/+$/, "");
|
|
36
49
|
}
|
|
@@ -50,13 +63,9 @@ function requireSurfApiConfig() {
|
|
|
50
63
|
function readAdminApiKey() {
|
|
51
64
|
return process.env.SURF_API_KEY;
|
|
52
65
|
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
"use strict";
|
|
57
|
-
DEFAULT_API_BASE_URL = "https://api.ask.surf/gateway/v1";
|
|
58
|
-
}
|
|
59
|
-
});
|
|
66
|
+
|
|
67
|
+
// src/db/schema-sync.ts
|
|
68
|
+
var import_fs = __toESM(require("fs"), 1);
|
|
60
69
|
|
|
61
70
|
// src/core/transport.ts
|
|
62
71
|
function sleep(ms) {
|
|
@@ -114,47 +123,154 @@ async function postJson(path2, body) {
|
|
|
114
123
|
body: body ? JSON.stringify(body) : void 0
|
|
115
124
|
});
|
|
116
125
|
}
|
|
117
|
-
var init_transport = __esm({
|
|
118
|
-
"src/core/transport.ts"() {
|
|
119
|
-
"use strict";
|
|
120
|
-
init_config();
|
|
121
|
-
}
|
|
122
|
-
});
|
|
123
126
|
|
|
124
127
|
// src/data/client.ts
|
|
125
|
-
var client_exports = {};
|
|
126
|
-
__export(client_exports, {
|
|
127
|
-
get: () => get,
|
|
128
|
-
post: () => post
|
|
129
|
-
});
|
|
130
128
|
async function get(path2, params) {
|
|
131
129
|
return getJson(path2, params);
|
|
132
130
|
}
|
|
133
131
|
async function post(path2, body) {
|
|
134
132
|
return postJson(path2, body);
|
|
135
133
|
}
|
|
136
|
-
var init_client = __esm({
|
|
137
|
-
"src/data/client.ts"() {
|
|
138
|
-
"use strict";
|
|
139
|
-
init_transport();
|
|
140
|
-
}
|
|
141
|
-
});
|
|
142
134
|
|
|
143
|
-
// src/
|
|
144
|
-
var
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
135
|
+
// src/db/schema-sync.ts
|
|
136
|
+
var syncing = false;
|
|
137
|
+
async function syncSchema(options) {
|
|
138
|
+
const { schemaPath, retries = 3, retryDelay = 2e3 } = options;
|
|
139
|
+
for (let i = 0; i < retries; i++) {
|
|
140
|
+
try {
|
|
141
|
+
await doSyncSchema(schemaPath);
|
|
142
|
+
return;
|
|
143
|
+
} catch (err) {
|
|
144
|
+
console.error(`DB schema sync attempt ${i + 1}/${retries} failed: ${err.message}`);
|
|
145
|
+
if (i < retries - 1) await new Promise((r) => setTimeout(r, retryDelay * (i + 1)));
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
console.error("DB schema sync failed after all retries");
|
|
149
|
+
}
|
|
150
|
+
async function doSyncSchema(schemaPath) {
|
|
151
|
+
if (syncing) return;
|
|
152
|
+
syncing = true;
|
|
153
|
+
try {
|
|
154
|
+
if (!import_fs.default.existsSync(schemaPath)) return;
|
|
155
|
+
let schema;
|
|
156
|
+
if (schemaPath.endsWith(".ts")) {
|
|
157
|
+
try {
|
|
158
|
+
schema = await import(`${schemaPath}?t=${Date.now()}`);
|
|
159
|
+
} catch (err) {
|
|
160
|
+
if (err instanceof SyntaxError) {
|
|
161
|
+
console.log("DB: schema file has syntax error, waiting for next change...");
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
if (err.message.includes("Cannot find module") || err.message.includes("is not a function")) {
|
|
165
|
+
return;
|
|
166
|
+
}
|
|
167
|
+
throw err;
|
|
168
|
+
}
|
|
169
|
+
} else {
|
|
170
|
+
try {
|
|
171
|
+
delete require.cache[require.resolve(schemaPath)];
|
|
172
|
+
} catch {
|
|
173
|
+
}
|
|
174
|
+
try {
|
|
175
|
+
schema = require(schemaPath);
|
|
176
|
+
} catch (err) {
|
|
177
|
+
if (err instanceof SyntaxError) {
|
|
178
|
+
console.log("DB: schema file has syntax error, waiting for next change...");
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (err.message.includes("Cannot find module") || err.message.includes("is not a function")) {
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
throw err;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
let getTableConfig;
|
|
188
|
+
try {
|
|
189
|
+
getTableConfig = require("drizzle-orm/pg-core").getTableConfig;
|
|
190
|
+
} catch {
|
|
191
|
+
return;
|
|
192
|
+
}
|
|
193
|
+
const tables = Object.values(schema).filter(
|
|
194
|
+
(t) => t && typeof t === "object" && /* @__PURE__ */ Symbol.for("drizzle:Name") in t
|
|
195
|
+
);
|
|
196
|
+
if (tables.length === 0) return;
|
|
197
|
+
await post("db/provision", {});
|
|
198
|
+
const tablesResult = await get("db/tables");
|
|
199
|
+
const tablesList = Array.isArray(tablesResult) ? tablesResult : tablesResult.tables || [];
|
|
200
|
+
const existing = tablesList.map((t) => typeof t === "string" ? t : t.name);
|
|
201
|
+
const missing = tables.filter((t) => !existing.includes(getTableConfig(t).name));
|
|
202
|
+
if (missing.length > 0) {
|
|
203
|
+
const { generateDrizzleJson, generateMigration } = require("drizzle-kit/api");
|
|
204
|
+
const missingSchema = {};
|
|
205
|
+
for (const t of missing) missingSchema[getTableConfig(t).name] = t;
|
|
206
|
+
const sqls = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(missingSchema));
|
|
207
|
+
for (const sql of sqls) {
|
|
208
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
209
|
+
try {
|
|
210
|
+
await post("db/query", { sql, params: [] });
|
|
211
|
+
console.log(`DB: Executed: ${sql.slice(0, 80)}...`);
|
|
212
|
+
break;
|
|
213
|
+
} catch (err) {
|
|
214
|
+
if (attempt === 0) {
|
|
215
|
+
console.warn(`DB: Retrying after: ${err.message}`);
|
|
216
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
217
|
+
} else {
|
|
218
|
+
console.error(`DB: Failed: ${sql.slice(0, 80)}... \u2014 ${err.message}`);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const existingTables = tables.filter((t) => existing.includes(getTableConfig(t).name));
|
|
225
|
+
for (const t of existingTables) {
|
|
226
|
+
const cfg = getTableConfig(t);
|
|
227
|
+
try {
|
|
228
|
+
const live = await get("db/table-schema", { table: cfg.name });
|
|
229
|
+
const liveCols = new Set((live.columns || []).map((c) => c.name));
|
|
230
|
+
for (const col of cfg.columns) {
|
|
231
|
+
if (!liveCols.has(col.name)) {
|
|
232
|
+
const colType = col.getSQLType();
|
|
233
|
+
const ddl = `ALTER TABLE "${cfg.name}" ADD COLUMN IF NOT EXISTS "${col.name}" ${colType}`;
|
|
234
|
+
try {
|
|
235
|
+
await post("db/query", { sql: ddl, params: [] });
|
|
236
|
+
console.log(`DB: Added column ${col.name} to ${cfg.name}`);
|
|
237
|
+
} catch (err) {
|
|
238
|
+
console.warn(`DB: Failed to add column ${col.name} to ${cfg.name}: ${err.message}`);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
}
|
|
242
|
+
} catch (err) {
|
|
243
|
+
console.warn(`DB: Column check failed for ${cfg.name}: ${err.message}`);
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
} finally {
|
|
247
|
+
syncing = false;
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
function watchSchema(schemaPath, options) {
|
|
251
|
+
const { debounceMs = 1e3, retries = 2, retryDelay = 1500 } = options || {};
|
|
252
|
+
if (!import_fs.default.existsSync(schemaPath)) return () => {
|
|
253
|
+
};
|
|
254
|
+
let debounce = null;
|
|
255
|
+
import_fs.default.watchFile(schemaPath, { interval: 2e3 }, () => {
|
|
256
|
+
if (debounce) clearTimeout(debounce);
|
|
257
|
+
debounce = setTimeout(async () => {
|
|
258
|
+
console.log("DB: schema file changed, re-syncing tables...");
|
|
259
|
+
try {
|
|
260
|
+
await syncSchema({ schemaPath, retries, retryDelay });
|
|
261
|
+
console.log("DB: schema re-sync complete");
|
|
262
|
+
} catch (err) {
|
|
263
|
+
console.error(`DB: schema re-sync failed: ${err.message}`);
|
|
264
|
+
}
|
|
265
|
+
}, debounceMs);
|
|
266
|
+
});
|
|
267
|
+
return () => {
|
|
268
|
+
import_fs.default.unwatchFile(schemaPath);
|
|
269
|
+
if (debounce) clearTimeout(debounce);
|
|
270
|
+
};
|
|
271
|
+
}
|
|
150
272
|
|
|
151
273
|
// src/server/runtime.ts
|
|
152
|
-
var import_express = __toESM(require("express"), 1);
|
|
153
|
-
var import_cors = __toESM(require("cors"), 1);
|
|
154
|
-
var import_fs = __toESM(require("fs"), 1);
|
|
155
|
-
var import_path = __toESM(require("path"), 1);
|
|
156
|
-
var import_croner = require("croner");
|
|
157
|
-
init_config();
|
|
158
274
|
function requireBearerAuth(req, res, next) {
|
|
159
275
|
const apiKey = readAdminApiKey();
|
|
160
276
|
if (!apiKey) {
|
|
@@ -166,9 +282,10 @@ function requireBearerAuth(req, res, next) {
|
|
|
166
282
|
next();
|
|
167
283
|
}
|
|
168
284
|
function createServer(options = {}) {
|
|
169
|
-
const
|
|
285
|
+
const rawPort = process.env.BACKEND_PORT;
|
|
286
|
+
const port = options.port ?? (rawPort ? Number.parseInt(rawPort, 10) : void 0);
|
|
170
287
|
if (!Number.isInteger(port)) {
|
|
171
|
-
throw new Error("createServer requires a port via options.port or
|
|
288
|
+
throw new Error("createServer requires a port via options.port or BACKEND_PORT env var");
|
|
172
289
|
}
|
|
173
290
|
const routesDir = options.routesDir || import_path.default.join(process.cwd(), "routes");
|
|
174
291
|
const cronDir = options.cronDir || process.cwd();
|
|
@@ -178,8 +295,8 @@ function createServer(options = {}) {
|
|
|
178
295
|
app.get("/api/health", (_req, res) => {
|
|
179
296
|
res.json({ status: "ok" });
|
|
180
297
|
});
|
|
181
|
-
if (
|
|
182
|
-
for (const file of
|
|
298
|
+
if (import_fs2.default.existsSync(routesDir)) {
|
|
299
|
+
for (const file of import_fs2.default.readdirSync(routesDir)) {
|
|
183
300
|
if (!file.endsWith(".js") && !file.endsWith(".ts")) continue;
|
|
184
301
|
const name = file.replace(/\.(js|ts)$/, "");
|
|
185
302
|
try {
|
|
@@ -217,108 +334,11 @@ var schemaSync = { run: async () => {
|
|
|
217
334
|
}, watch: () => {
|
|
218
335
|
} };
|
|
219
336
|
function setupSchemaSync(app, schemaDir) {
|
|
220
|
-
let syncing = false;
|
|
221
337
|
let schemaReady = false;
|
|
222
|
-
|
|
223
|
-
if (syncing) return;
|
|
224
|
-
syncing = true;
|
|
225
|
-
try {
|
|
226
|
-
const schemaPath = import_path.default.join(schemaDir, "schema.js");
|
|
227
|
-
if (!import_fs.default.existsSync(schemaPath)) return;
|
|
228
|
-
try {
|
|
229
|
-
delete require.cache[require.resolve(schemaPath)];
|
|
230
|
-
} catch {
|
|
231
|
-
}
|
|
232
|
-
let schema;
|
|
233
|
-
try {
|
|
234
|
-
schema = require(schemaPath);
|
|
235
|
-
} catch (err) {
|
|
236
|
-
if (err instanceof SyntaxError) {
|
|
237
|
-
console.log("DB: schema.js has syntax error, waiting for next change...");
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
if (err.message.includes("Cannot find module") || err.message.includes("is not a function")) {
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
throw err;
|
|
244
|
-
}
|
|
245
|
-
let getTableConfig;
|
|
246
|
-
try {
|
|
247
|
-
getTableConfig = require("drizzle-orm/pg-core").getTableConfig;
|
|
248
|
-
} catch {
|
|
249
|
-
return;
|
|
250
|
-
}
|
|
251
|
-
const tables = Object.values(schema).filter(
|
|
252
|
-
(t) => t && typeof t === "object" && /* @__PURE__ */ Symbol.for("drizzle:Name") in t
|
|
253
|
-
);
|
|
254
|
-
if (tables.length === 0) return;
|
|
255
|
-
const { get: dbGet, post: dbPost } = await Promise.resolve().then(() => (init_client(), client_exports));
|
|
256
|
-
await dbPost("db/provision");
|
|
257
|
-
const existing = (await dbGet("db/tables")).map((t) => t.name);
|
|
258
|
-
const missing = tables.filter((t) => !existing.includes(getTableConfig(t).name));
|
|
259
|
-
if (missing.length > 0) {
|
|
260
|
-
const { generateDrizzleJson, generateMigration } = require("drizzle-kit/api");
|
|
261
|
-
const missingSchema = {};
|
|
262
|
-
for (const t of missing) missingSchema[getTableConfig(t).name] = t;
|
|
263
|
-
const sqls = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(missingSchema));
|
|
264
|
-
for (const sql of sqls) {
|
|
265
|
-
for (let attempt = 0; attempt < 2; attempt++) {
|
|
266
|
-
try {
|
|
267
|
-
await dbPost("db/query", { sql, params: [] });
|
|
268
|
-
console.log(`DB: Executed: ${sql.slice(0, 80)}...`);
|
|
269
|
-
break;
|
|
270
|
-
} catch (err) {
|
|
271
|
-
if (attempt === 0) {
|
|
272
|
-
console.warn(`DB: Retrying after: ${err.message}`);
|
|
273
|
-
await new Promise((r) => setTimeout(r, 1500));
|
|
274
|
-
} else {
|
|
275
|
-
console.error(`DB: Failed: ${sql.slice(0, 80)}... \u2014 ${err.message}`);
|
|
276
|
-
}
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
}
|
|
281
|
-
const existingTables = tables.filter((t) => existing.includes(getTableConfig(t).name));
|
|
282
|
-
for (const t of existingTables) {
|
|
283
|
-
const cfg = getTableConfig(t);
|
|
284
|
-
try {
|
|
285
|
-
const live = await dbGet("db/table-schema", { table: cfg.name });
|
|
286
|
-
const liveCols = new Set((live.columns || []).map((c) => c.name));
|
|
287
|
-
for (const col of cfg.columns) {
|
|
288
|
-
if (!liveCols.has(col.name)) {
|
|
289
|
-
const colType = col.getSQLType();
|
|
290
|
-
const ddl = `ALTER TABLE "${cfg.name}" ADD COLUMN IF NOT EXISTS "${col.name}" ${colType}`;
|
|
291
|
-
try {
|
|
292
|
-
await dbPost("db/query", { sql: ddl, params: [] });
|
|
293
|
-
console.log(`DB: Added column ${col.name} to ${cfg.name}`);
|
|
294
|
-
} catch (err) {
|
|
295
|
-
console.warn(`DB: Failed to add column ${col.name} to ${cfg.name}: ${err.message}`);
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
}
|
|
299
|
-
} catch (err) {
|
|
300
|
-
console.warn(`DB: Column check failed for ${cfg.name}: ${err.message}`);
|
|
301
|
-
}
|
|
302
|
-
}
|
|
303
|
-
} finally {
|
|
304
|
-
syncing = false;
|
|
305
|
-
}
|
|
306
|
-
}
|
|
307
|
-
async function syncWithRetry(retries = 3, delay = 2e3) {
|
|
308
|
-
for (let i = 0; i < retries; i++) {
|
|
309
|
-
try {
|
|
310
|
-
await doSyncSchema();
|
|
311
|
-
return;
|
|
312
|
-
} catch (err) {
|
|
313
|
-
console.error(`DB schema sync attempt ${i + 1}/${retries} failed: ${err.message}`);
|
|
314
|
-
if (i < retries - 1) await new Promise((r) => setTimeout(r, delay * (i + 1)));
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
console.error("DB schema sync failed after all retries");
|
|
318
|
-
}
|
|
338
|
+
const schemaPath = import_path.default.join(schemaDir, "schema.js");
|
|
319
339
|
app.post("/api/__sync-schema", requireBearerAuth, async (_req, res) => {
|
|
320
340
|
try {
|
|
321
|
-
await
|
|
341
|
+
await syncSchema({ schemaPath, retries: 2, retryDelay: 1500 });
|
|
322
342
|
res.json({ ok: true });
|
|
323
343
|
} catch (err) {
|
|
324
344
|
res.status(500).json({ ok: false, error: err.message });
|
|
@@ -330,7 +350,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
330
350
|
});
|
|
331
351
|
schemaSync.run = async () => {
|
|
332
352
|
try {
|
|
333
|
-
await
|
|
353
|
+
await syncSchema({ schemaPath });
|
|
334
354
|
schemaReady = true;
|
|
335
355
|
console.log("Schema sync complete, API ready");
|
|
336
356
|
} catch {
|
|
@@ -339,21 +359,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
339
359
|
}
|
|
340
360
|
};
|
|
341
361
|
schemaSync.watch = () => {
|
|
342
|
-
|
|
343
|
-
if (!import_fs.default.existsSync(schemaPath)) return;
|
|
344
|
-
let debounce = null;
|
|
345
|
-
import_fs.default.watchFile(schemaPath, { interval: 2e3 }, () => {
|
|
346
|
-
if (debounce) clearTimeout(debounce);
|
|
347
|
-
debounce = setTimeout(async () => {
|
|
348
|
-
console.log("DB: schema.js changed, re-syncing tables...");
|
|
349
|
-
try {
|
|
350
|
-
await syncWithRetry(2, 1500);
|
|
351
|
-
console.log("DB: schema re-sync complete");
|
|
352
|
-
} catch (err) {
|
|
353
|
-
console.error(`DB: schema re-sync failed: ${err.message}`);
|
|
354
|
-
}
|
|
355
|
-
}, 1e3);
|
|
356
|
-
});
|
|
362
|
+
watchSchema(schemaPath);
|
|
357
363
|
};
|
|
358
364
|
}
|
|
359
365
|
function setupCron(app, cronDir) {
|
|
@@ -369,10 +375,10 @@ function setupCron(app, cronDir) {
|
|
|
369
375
|
}
|
|
370
376
|
cronJobs.clear();
|
|
371
377
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
372
|
-
if (!
|
|
378
|
+
if (!import_fs2.default.existsSync(cronPath)) return;
|
|
373
379
|
let tasks;
|
|
374
380
|
try {
|
|
375
|
-
tasks = JSON.parse(
|
|
381
|
+
tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf-8"));
|
|
376
382
|
} catch (e) {
|
|
377
383
|
console.error("Failed to parse cron.json:", e.message);
|
|
378
384
|
return;
|
|
@@ -453,7 +459,7 @@ function setupCron(app, cronDir) {
|
|
|
453
459
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
454
460
|
let tasks = [];
|
|
455
461
|
try {
|
|
456
|
-
if (
|
|
462
|
+
if (import_fs2.default.existsSync(cronPath)) tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf8"));
|
|
457
463
|
} catch {
|
|
458
464
|
tasks = [];
|
|
459
465
|
}
|
|
@@ -462,7 +468,7 @@ function setupCron(app, cronDir) {
|
|
|
462
468
|
}
|
|
463
469
|
const newTask = { id, name, schedule, handler, enabled, timeout };
|
|
464
470
|
tasks.push(newTask);
|
|
465
|
-
|
|
471
|
+
import_fs2.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
466
472
|
loadCronJobs();
|
|
467
473
|
res.status(201).json(newTask);
|
|
468
474
|
});
|
|
@@ -470,7 +476,7 @@ function setupCron(app, cronDir) {
|
|
|
470
476
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
471
477
|
let tasks = [];
|
|
472
478
|
try {
|
|
473
|
-
if (
|
|
479
|
+
if (import_fs2.default.existsSync(cronPath)) tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf8"));
|
|
474
480
|
} catch {
|
|
475
481
|
tasks = [];
|
|
476
482
|
}
|
|
@@ -491,7 +497,7 @@ function setupCron(app, cronDir) {
|
|
|
491
497
|
}
|
|
492
498
|
}
|
|
493
499
|
tasks[idx] = { ...tasks[idx], ...updates, id: req.params.id };
|
|
494
|
-
|
|
500
|
+
import_fs2.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
495
501
|
loadCronJobs();
|
|
496
502
|
res.json(tasks[idx]);
|
|
497
503
|
});
|
|
@@ -499,14 +505,14 @@ function setupCron(app, cronDir) {
|
|
|
499
505
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
500
506
|
let tasks = [];
|
|
501
507
|
try {
|
|
502
|
-
if (
|
|
508
|
+
if (import_fs2.default.existsSync(cronPath)) tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf8"));
|
|
503
509
|
} catch {
|
|
504
510
|
tasks = [];
|
|
505
511
|
}
|
|
506
512
|
const idx = tasks.findIndex((t) => t.id === req.params.id);
|
|
507
513
|
if (idx === -1) return res.status(404).json({ error: "Task not found" });
|
|
508
514
|
tasks.splice(idx, 1);
|
|
509
|
-
|
|
515
|
+
import_fs2.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
510
516
|
cronState.delete(req.params.id);
|
|
511
517
|
loadCronJobs();
|
|
512
518
|
res.json({ ok: true });
|
|
@@ -524,11 +530,7 @@ function setupCron(app, cronDir) {
|
|
|
524
530
|
loadCronJobs();
|
|
525
531
|
}
|
|
526
532
|
|
|
527
|
-
// src/data/data-api.ts
|
|
528
|
-
init_client();
|
|
529
|
-
|
|
530
533
|
// src/data/categories/exchange.ts
|
|
531
|
-
init_client();
|
|
532
534
|
var exchange = {
|
|
533
535
|
/** Returns order book bid/ask levels with computed stats. **Included fields:** spread, spread percentage, mid-price, and total bid/ask depth. Use `limit` to control the number of price levels (1–100, default 20). Set `type=swap` to query perpetual contract order books instead of spot. */
|
|
534
536
|
depth: (params) => get("exchange/depth", params),
|
|
@@ -547,7 +549,6 @@ var exchange = {
|
|
|
547
549
|
};
|
|
548
550
|
|
|
549
551
|
// src/data/categories/fund.ts
|
|
550
|
-
init_client();
|
|
551
552
|
var fund = {
|
|
552
553
|
/** Returns a fund's **profile metadata**. **Included fields:** X accounts, team members, recent research, invested project count. This does NOT return the list of investments — use `/fund/portfolio` for that. **Lookup:** by UUID (`id`) or name (`q`). Returns 404 if not found. */
|
|
553
554
|
detail: (params) => get("fund/detail", params),
|
|
@@ -558,7 +559,6 @@ var fund = {
|
|
|
558
559
|
};
|
|
559
560
|
|
|
560
561
|
// src/data/categories/kalshi.ts
|
|
561
|
-
init_client();
|
|
562
562
|
var kalshi = {
|
|
563
563
|
/** Returns Kalshi events with nested markets, optionally filtered by `event_ticker`. Each event includes market count and a list of markets. **Data refresh:** ~30 minutes */
|
|
564
564
|
events: (params) => get("prediction-market/kalshi/events", params),
|
|
@@ -577,7 +577,6 @@ var kalshi = {
|
|
|
577
577
|
};
|
|
578
578
|
|
|
579
579
|
// src/data/categories/market.ts
|
|
580
|
-
init_client();
|
|
581
580
|
var market = {
|
|
582
581
|
/** Returns daily ETF flow history for US spot ETFs. **Included fields:** net flow (USD), token price, per-ticker breakdown. Sorted by date descending. `symbol`: `BTC` or `ETH`. */
|
|
583
582
|
etf: (params) => get("market/etf", params),
|
|
@@ -604,7 +603,6 @@ var market = {
|
|
|
604
603
|
};
|
|
605
604
|
|
|
606
605
|
// src/data/categories/matching.ts
|
|
607
|
-
init_client();
|
|
608
606
|
var matching = {
|
|
609
607
|
/** Returns daily volume and open interest comparison for a specific matched market pair. Both `polymarket_condition_id` and `kalshi_market_ticker` are required. **Volume units:** Polymarket = USD, Kalshi = contracts. */
|
|
610
608
|
market_daily: (params) => get("prediction-market/matching/daily", params),
|
|
@@ -615,7 +613,6 @@ var matching = {
|
|
|
615
613
|
};
|
|
616
614
|
|
|
617
615
|
// src/data/categories/news.ts
|
|
618
|
-
init_client();
|
|
619
616
|
var news = {
|
|
620
617
|
/** Returns the full content of a single news article by its ID (returned as `id` in feed and search results). */
|
|
621
618
|
detail: (params) => get("news/detail", params),
|
|
@@ -624,7 +621,6 @@ var news = {
|
|
|
624
621
|
};
|
|
625
622
|
|
|
626
623
|
// src/data/categories/onchain.ts
|
|
627
|
-
init_client();
|
|
628
624
|
var onchain = {
|
|
629
625
|
/** Returns bridge protocols ranked by total USD volume over a specified time range. */
|
|
630
626
|
bridge_ranking: (params) => get("onchain/bridge/ranking", params),
|
|
@@ -643,7 +639,6 @@ var onchain = {
|
|
|
643
639
|
};
|
|
644
640
|
|
|
645
641
|
// src/data/categories/polymarket.ts
|
|
646
|
-
init_client();
|
|
647
642
|
var polymarket = {
|
|
648
643
|
/** Returns trade and redemption activity for a Polymarket wallet. **Data refresh:** ~30 minutes */
|
|
649
644
|
activity: (params) => get("prediction-market/polymarket/activity", params),
|
|
@@ -666,14 +661,12 @@ var polymarket = {
|
|
|
666
661
|
};
|
|
667
662
|
|
|
668
663
|
// src/data/categories/prediction_market.ts
|
|
669
|
-
init_client();
|
|
670
664
|
var prediction_market = {
|
|
671
665
|
/** Returns daily notional volume and open interest aggregated by category across Kalshi and Polymarket. **Filters:** `source` or `category`. **Data refresh:** daily */
|
|
672
666
|
category_metrics: (params) => get("prediction-market/category-metrics", params)
|
|
673
667
|
};
|
|
674
668
|
|
|
675
669
|
// src/data/categories/project.ts
|
|
676
|
-
init_client();
|
|
677
670
|
var project = {
|
|
678
671
|
/** Returns time-series DeFi metrics for a project. **Available metrics:** `volume`, `fee`, `fees`, `revenue`, `tvl`, `users`. **Lookup:** by UUID (`id`) or name (`q`). Filter by `chain` and date range (`from`/`to`). Returns 404 if the project is not found. **Note:** this endpoint only returns data for DeFi protocol projects (e.g. `aave`, `uniswap`, `lido`, `makerdao`). Use `q` with a DeFi protocol name. */
|
|
679
672
|
defi_metrics: (params) => get("project/defi/metrics", params),
|
|
@@ -684,7 +677,6 @@ var project = {
|
|
|
684
677
|
};
|
|
685
678
|
|
|
686
679
|
// src/data/categories/search.ts
|
|
687
|
-
init_client();
|
|
688
680
|
var search = {
|
|
689
681
|
/** Searches and filters airdrop opportunities. **Filters:** keyword, status, reward type, task type. Returns paginated results with optional task details. */
|
|
690
682
|
airdrop: (params) => get("search/airdrop", params),
|
|
@@ -711,7 +703,6 @@ var search = {
|
|
|
711
703
|
};
|
|
712
704
|
|
|
713
705
|
// src/data/categories/social.ts
|
|
714
|
-
init_client();
|
|
715
706
|
var social = {
|
|
716
707
|
/** Returns a **point-in-time snapshot** of social analytics for a project. **Available fields** (via `fields`): `sentiment`, `follower_geo`, `smart_followers`. **Lookup:** by X account ID (`x_id`) or project name (`q`, e.g. `uniswap`, `solana`). The `q` parameter must be a crypto project name, not a personal Twitter handle. Returns 404 if the project has no linked Twitter account. For sentiment **trends over time**, use `/social/mindshare` instead. */
|
|
717
708
|
detail: (params) => get("social/detail", params),
|
|
@@ -738,7 +729,6 @@ var social = {
|
|
|
738
729
|
};
|
|
739
730
|
|
|
740
731
|
// src/data/categories/token.ts
|
|
741
|
-
init_client();
|
|
742
732
|
var token = {
|
|
743
733
|
/** Returns recent DEX swap events for a token contract address. **Covered DEXes:** `uniswap`, `sushiswap`, `curve`, `balancer`. **Included fields:** trading pair, amounts, USD value, taker address. **Data refresh:** ~24 hours · **Chains:** Ethereum, Base */
|
|
744
734
|
dex_trades: (params) => get("token/dex-trades", params),
|
|
@@ -751,7 +741,6 @@ var token = {
|
|
|
751
741
|
};
|
|
752
742
|
|
|
753
743
|
// src/data/categories/v2.ts
|
|
754
|
-
init_client();
|
|
755
744
|
var v2 = {
|
|
756
745
|
/** Historical orderbook snapshots for a Kalshi market. Returns yes-side bid/ask levels. Timestamps in milliseconds, prices in cents. Data refresh: ~5 minutes */
|
|
757
746
|
kalshi_orderbooks: (params) => get("gateway/v2/kalshi/orderbooks", params),
|
|
@@ -766,7 +755,6 @@ var v2 = {
|
|
|
766
755
|
};
|
|
767
756
|
|
|
768
757
|
// src/data/categories/wallet.ts
|
|
769
|
-
init_client();
|
|
770
758
|
var wallet = {
|
|
771
759
|
/** Returns multiple wallet sub-resources in a single request. **Available fields** (via `fields`): `balance`, `tokens`, `labels`, `nft`. **Lookup:** by `address`. Partial failures return available fields with per-field error info. Returns 422 if `fields` is invalid. */
|
|
772
760
|
detail: (params) => get("wallet/detail", params),
|
|
@@ -783,7 +771,6 @@ var wallet = {
|
|
|
783
771
|
};
|
|
784
772
|
|
|
785
773
|
// src/data/categories/web.ts
|
|
786
|
-
init_client();
|
|
787
774
|
var web = {
|
|
788
775
|
/** Fetches a web page and converts it to clean, LLM-friendly markdown. **Options:** - `target_selector` — extract specific page sections - `remove_selector` — strip unwanted elements Returns 400 if the URL is invalid or unreachable. */
|
|
789
776
|
fetch: (params) => get("web/fetch", params)
|