@surf-ai/sdk 1.0.0-alpha.0 → 1.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/db/index.cjs +154 -3
- package/dist/db/index.d.cts +35 -1
- package/dist/db/index.d.ts +35 -1
- package/dist/db/index.js +148 -2
- package/dist/server/index.cjs +171 -186
- package/dist/server/index.js +252 -134
- package/package.json +1 -1
- 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.ask.surf/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,152 @@ 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 existing = (await get("db/tables")).map((t) => t.name);
|
|
199
|
+
const missing = tables.filter((t) => !existing.includes(getTableConfig(t).name));
|
|
200
|
+
if (missing.length > 0) {
|
|
201
|
+
const { generateDrizzleJson, generateMigration } = require("drizzle-kit/api");
|
|
202
|
+
const missingSchema = {};
|
|
203
|
+
for (const t of missing) missingSchema[getTableConfig(t).name] = t;
|
|
204
|
+
const sqls = await generateMigration(generateDrizzleJson({}), generateDrizzleJson(missingSchema));
|
|
205
|
+
for (const sql of sqls) {
|
|
206
|
+
for (let attempt = 0; attempt < 2; attempt++) {
|
|
207
|
+
try {
|
|
208
|
+
await post("db/query", { sql, params: [] });
|
|
209
|
+
console.log(`DB: Executed: ${sql.slice(0, 80)}...`);
|
|
210
|
+
break;
|
|
211
|
+
} catch (err) {
|
|
212
|
+
if (attempt === 0) {
|
|
213
|
+
console.warn(`DB: Retrying after: ${err.message}`);
|
|
214
|
+
await new Promise((r) => setTimeout(r, 1500));
|
|
215
|
+
} else {
|
|
216
|
+
console.error(`DB: Failed: ${sql.slice(0, 80)}... \u2014 ${err.message}`);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const existingTables = tables.filter((t) => existing.includes(getTableConfig(t).name));
|
|
223
|
+
for (const t of existingTables) {
|
|
224
|
+
const cfg = getTableConfig(t);
|
|
225
|
+
try {
|
|
226
|
+
const live = await get("db/table-schema", { table: cfg.name });
|
|
227
|
+
const liveCols = new Set((live.columns || []).map((c) => c.name));
|
|
228
|
+
for (const col of cfg.columns) {
|
|
229
|
+
if (!liveCols.has(col.name)) {
|
|
230
|
+
const colType = col.getSQLType();
|
|
231
|
+
const ddl = `ALTER TABLE "${cfg.name}" ADD COLUMN IF NOT EXISTS "${col.name}" ${colType}`;
|
|
232
|
+
try {
|
|
233
|
+
await post("db/query", { sql: ddl, params: [] });
|
|
234
|
+
console.log(`DB: Added column ${col.name} to ${cfg.name}`);
|
|
235
|
+
} catch (err) {
|
|
236
|
+
console.warn(`DB: Failed to add column ${col.name} to ${cfg.name}: ${err.message}`);
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
} catch (err) {
|
|
241
|
+
console.warn(`DB: Column check failed for ${cfg.name}: ${err.message}`);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
} finally {
|
|
245
|
+
syncing = false;
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
function watchSchema(schemaPath, options) {
|
|
249
|
+
const { debounceMs = 1e3, retries = 2, retryDelay = 1500 } = options || {};
|
|
250
|
+
if (!import_fs.default.existsSync(schemaPath)) return () => {
|
|
251
|
+
};
|
|
252
|
+
let debounce = null;
|
|
253
|
+
import_fs.default.watchFile(schemaPath, { interval: 2e3 }, () => {
|
|
254
|
+
if (debounce) clearTimeout(debounce);
|
|
255
|
+
debounce = setTimeout(async () => {
|
|
256
|
+
console.log("DB: schema file changed, re-syncing tables...");
|
|
257
|
+
try {
|
|
258
|
+
await syncSchema({ schemaPath, retries, retryDelay });
|
|
259
|
+
console.log("DB: schema re-sync complete");
|
|
260
|
+
} catch (err) {
|
|
261
|
+
console.error(`DB: schema re-sync failed: ${err.message}`);
|
|
262
|
+
}
|
|
263
|
+
}, debounceMs);
|
|
264
|
+
});
|
|
265
|
+
return () => {
|
|
266
|
+
import_fs.default.unwatchFile(schemaPath);
|
|
267
|
+
if (debounce) clearTimeout(debounce);
|
|
268
|
+
};
|
|
269
|
+
}
|
|
150
270
|
|
|
151
271
|
// 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
272
|
function requireBearerAuth(req, res, next) {
|
|
159
273
|
const apiKey = readAdminApiKey();
|
|
160
274
|
if (!apiKey) {
|
|
@@ -166,9 +280,10 @@ function requireBearerAuth(req, res, next) {
|
|
|
166
280
|
next();
|
|
167
281
|
}
|
|
168
282
|
function createServer(options = {}) {
|
|
169
|
-
const
|
|
283
|
+
const rawPort = process.env.BACKEND_PORT;
|
|
284
|
+
const port = options.port ?? (rawPort ? Number.parseInt(rawPort, 10) : void 0);
|
|
170
285
|
if (!Number.isInteger(port)) {
|
|
171
|
-
throw new Error("createServer requires a port via options.port or
|
|
286
|
+
throw new Error("createServer requires a port via options.port or BACKEND_PORT env var");
|
|
172
287
|
}
|
|
173
288
|
const routesDir = options.routesDir || import_path.default.join(process.cwd(), "routes");
|
|
174
289
|
const cronDir = options.cronDir || process.cwd();
|
|
@@ -178,8 +293,8 @@ function createServer(options = {}) {
|
|
|
178
293
|
app.get("/api/health", (_req, res) => {
|
|
179
294
|
res.json({ status: "ok" });
|
|
180
295
|
});
|
|
181
|
-
if (
|
|
182
|
-
for (const file of
|
|
296
|
+
if (import_fs2.default.existsSync(routesDir)) {
|
|
297
|
+
for (const file of import_fs2.default.readdirSync(routesDir)) {
|
|
183
298
|
if (!file.endsWith(".js") && !file.endsWith(".ts")) continue;
|
|
184
299
|
const name = file.replace(/\.(js|ts)$/, "");
|
|
185
300
|
try {
|
|
@@ -217,108 +332,11 @@ var schemaSync = { run: async () => {
|
|
|
217
332
|
}, watch: () => {
|
|
218
333
|
} };
|
|
219
334
|
function setupSchemaSync(app, schemaDir) {
|
|
220
|
-
let syncing = false;
|
|
221
335
|
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
|
-
}
|
|
336
|
+
const schemaPath = import_path.default.join(schemaDir, "schema.js");
|
|
319
337
|
app.post("/api/__sync-schema", requireBearerAuth, async (_req, res) => {
|
|
320
338
|
try {
|
|
321
|
-
await
|
|
339
|
+
await syncSchema({ schemaPath, retries: 2, retryDelay: 1500 });
|
|
322
340
|
res.json({ ok: true });
|
|
323
341
|
} catch (err) {
|
|
324
342
|
res.status(500).json({ ok: false, error: err.message });
|
|
@@ -330,7 +348,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
330
348
|
});
|
|
331
349
|
schemaSync.run = async () => {
|
|
332
350
|
try {
|
|
333
|
-
await
|
|
351
|
+
await syncSchema({ schemaPath });
|
|
334
352
|
schemaReady = true;
|
|
335
353
|
console.log("Schema sync complete, API ready");
|
|
336
354
|
} catch {
|
|
@@ -339,21 +357,7 @@ function setupSchemaSync(app, schemaDir) {
|
|
|
339
357
|
}
|
|
340
358
|
};
|
|
341
359
|
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
|
-
});
|
|
360
|
+
watchSchema(schemaPath);
|
|
357
361
|
};
|
|
358
362
|
}
|
|
359
363
|
function setupCron(app, cronDir) {
|
|
@@ -369,10 +373,10 @@ function setupCron(app, cronDir) {
|
|
|
369
373
|
}
|
|
370
374
|
cronJobs.clear();
|
|
371
375
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
372
|
-
if (!
|
|
376
|
+
if (!import_fs2.default.existsSync(cronPath)) return;
|
|
373
377
|
let tasks;
|
|
374
378
|
try {
|
|
375
|
-
tasks = JSON.parse(
|
|
379
|
+
tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf-8"));
|
|
376
380
|
} catch (e) {
|
|
377
381
|
console.error("Failed to parse cron.json:", e.message);
|
|
378
382
|
return;
|
|
@@ -453,7 +457,7 @@ function setupCron(app, cronDir) {
|
|
|
453
457
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
454
458
|
let tasks = [];
|
|
455
459
|
try {
|
|
456
|
-
if (
|
|
460
|
+
if (import_fs2.default.existsSync(cronPath)) tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf8"));
|
|
457
461
|
} catch {
|
|
458
462
|
tasks = [];
|
|
459
463
|
}
|
|
@@ -462,7 +466,7 @@ function setupCron(app, cronDir) {
|
|
|
462
466
|
}
|
|
463
467
|
const newTask = { id, name, schedule, handler, enabled, timeout };
|
|
464
468
|
tasks.push(newTask);
|
|
465
|
-
|
|
469
|
+
import_fs2.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
466
470
|
loadCronJobs();
|
|
467
471
|
res.status(201).json(newTask);
|
|
468
472
|
});
|
|
@@ -470,7 +474,7 @@ function setupCron(app, cronDir) {
|
|
|
470
474
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
471
475
|
let tasks = [];
|
|
472
476
|
try {
|
|
473
|
-
if (
|
|
477
|
+
if (import_fs2.default.existsSync(cronPath)) tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf8"));
|
|
474
478
|
} catch {
|
|
475
479
|
tasks = [];
|
|
476
480
|
}
|
|
@@ -491,7 +495,7 @@ function setupCron(app, cronDir) {
|
|
|
491
495
|
}
|
|
492
496
|
}
|
|
493
497
|
tasks[idx] = { ...tasks[idx], ...updates, id: req.params.id };
|
|
494
|
-
|
|
498
|
+
import_fs2.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
495
499
|
loadCronJobs();
|
|
496
500
|
res.json(tasks[idx]);
|
|
497
501
|
});
|
|
@@ -499,14 +503,14 @@ function setupCron(app, cronDir) {
|
|
|
499
503
|
const cronPath = import_path.default.join(cronDir, "cron.json");
|
|
500
504
|
let tasks = [];
|
|
501
505
|
try {
|
|
502
|
-
if (
|
|
506
|
+
if (import_fs2.default.existsSync(cronPath)) tasks = JSON.parse(import_fs2.default.readFileSync(cronPath, "utf8"));
|
|
503
507
|
} catch {
|
|
504
508
|
tasks = [];
|
|
505
509
|
}
|
|
506
510
|
const idx = tasks.findIndex((t) => t.id === req.params.id);
|
|
507
511
|
if (idx === -1) return res.status(404).json({ error: "Task not found" });
|
|
508
512
|
tasks.splice(idx, 1);
|
|
509
|
-
|
|
513
|
+
import_fs2.default.writeFileSync(cronPath, JSON.stringify(tasks, null, 2));
|
|
510
514
|
cronState.delete(req.params.id);
|
|
511
515
|
loadCronJobs();
|
|
512
516
|
res.json({ ok: true });
|
|
@@ -524,11 +528,7 @@ function setupCron(app, cronDir) {
|
|
|
524
528
|
loadCronJobs();
|
|
525
529
|
}
|
|
526
530
|
|
|
527
|
-
// src/data/data-api.ts
|
|
528
|
-
init_client();
|
|
529
|
-
|
|
530
531
|
// src/data/categories/exchange.ts
|
|
531
|
-
init_client();
|
|
532
532
|
var exchange = {
|
|
533
533
|
/** 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
534
|
depth: (params) => get("exchange/depth", params),
|
|
@@ -547,7 +547,6 @@ var exchange = {
|
|
|
547
547
|
};
|
|
548
548
|
|
|
549
549
|
// src/data/categories/fund.ts
|
|
550
|
-
init_client();
|
|
551
550
|
var fund = {
|
|
552
551
|
/** 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
552
|
detail: (params) => get("fund/detail", params),
|
|
@@ -558,7 +557,6 @@ var fund = {
|
|
|
558
557
|
};
|
|
559
558
|
|
|
560
559
|
// src/data/categories/kalshi.ts
|
|
561
|
-
init_client();
|
|
562
560
|
var kalshi = {
|
|
563
561
|
/** 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
562
|
events: (params) => get("prediction-market/kalshi/events", params),
|
|
@@ -577,7 +575,6 @@ var kalshi = {
|
|
|
577
575
|
};
|
|
578
576
|
|
|
579
577
|
// src/data/categories/market.ts
|
|
580
|
-
init_client();
|
|
581
578
|
var market = {
|
|
582
579
|
/** 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
580
|
etf: (params) => get("market/etf", params),
|
|
@@ -604,7 +601,6 @@ var market = {
|
|
|
604
601
|
};
|
|
605
602
|
|
|
606
603
|
// src/data/categories/matching.ts
|
|
607
|
-
init_client();
|
|
608
604
|
var matching = {
|
|
609
605
|
/** 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
606
|
market_daily: (params) => get("prediction-market/matching/daily", params),
|
|
@@ -615,7 +611,6 @@ var matching = {
|
|
|
615
611
|
};
|
|
616
612
|
|
|
617
613
|
// src/data/categories/news.ts
|
|
618
|
-
init_client();
|
|
619
614
|
var news = {
|
|
620
615
|
/** Returns the full content of a single news article by its ID (returned as `id` in feed and search results). */
|
|
621
616
|
detail: (params) => get("news/detail", params),
|
|
@@ -624,7 +619,6 @@ var news = {
|
|
|
624
619
|
};
|
|
625
620
|
|
|
626
621
|
// src/data/categories/onchain.ts
|
|
627
|
-
init_client();
|
|
628
622
|
var onchain = {
|
|
629
623
|
/** Returns bridge protocols ranked by total USD volume over a specified time range. */
|
|
630
624
|
bridge_ranking: (params) => get("onchain/bridge/ranking", params),
|
|
@@ -643,7 +637,6 @@ var onchain = {
|
|
|
643
637
|
};
|
|
644
638
|
|
|
645
639
|
// src/data/categories/polymarket.ts
|
|
646
|
-
init_client();
|
|
647
640
|
var polymarket = {
|
|
648
641
|
/** Returns trade and redemption activity for a Polymarket wallet. **Data refresh:** ~30 minutes */
|
|
649
642
|
activity: (params) => get("prediction-market/polymarket/activity", params),
|
|
@@ -666,14 +659,12 @@ var polymarket = {
|
|
|
666
659
|
};
|
|
667
660
|
|
|
668
661
|
// src/data/categories/prediction_market.ts
|
|
669
|
-
init_client();
|
|
670
662
|
var prediction_market = {
|
|
671
663
|
/** Returns daily notional volume and open interest aggregated by category across Kalshi and Polymarket. **Filters:** `source` or `category`. **Data refresh:** daily */
|
|
672
664
|
category_metrics: (params) => get("prediction-market/category-metrics", params)
|
|
673
665
|
};
|
|
674
666
|
|
|
675
667
|
// src/data/categories/project.ts
|
|
676
|
-
init_client();
|
|
677
668
|
var project = {
|
|
678
669
|
/** 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
670
|
defi_metrics: (params) => get("project/defi/metrics", params),
|
|
@@ -684,7 +675,6 @@ var project = {
|
|
|
684
675
|
};
|
|
685
676
|
|
|
686
677
|
// src/data/categories/search.ts
|
|
687
|
-
init_client();
|
|
688
678
|
var search = {
|
|
689
679
|
/** Searches and filters airdrop opportunities. **Filters:** keyword, status, reward type, task type. Returns paginated results with optional task details. */
|
|
690
680
|
airdrop: (params) => get("search/airdrop", params),
|
|
@@ -711,7 +701,6 @@ var search = {
|
|
|
711
701
|
};
|
|
712
702
|
|
|
713
703
|
// src/data/categories/social.ts
|
|
714
|
-
init_client();
|
|
715
704
|
var social = {
|
|
716
705
|
/** 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
706
|
detail: (params) => get("social/detail", params),
|
|
@@ -738,7 +727,6 @@ var social = {
|
|
|
738
727
|
};
|
|
739
728
|
|
|
740
729
|
// src/data/categories/token.ts
|
|
741
|
-
init_client();
|
|
742
730
|
var token = {
|
|
743
731
|
/** 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
732
|
dex_trades: (params) => get("token/dex-trades", params),
|
|
@@ -751,7 +739,6 @@ var token = {
|
|
|
751
739
|
};
|
|
752
740
|
|
|
753
741
|
// src/data/categories/v2.ts
|
|
754
|
-
init_client();
|
|
755
742
|
var v2 = {
|
|
756
743
|
/** Historical orderbook snapshots for a Kalshi market. Returns yes-side bid/ask levels. Timestamps in milliseconds, prices in cents. Data refresh: ~5 minutes */
|
|
757
744
|
kalshi_orderbooks: (params) => get("gateway/v2/kalshi/orderbooks", params),
|
|
@@ -766,7 +753,6 @@ var v2 = {
|
|
|
766
753
|
};
|
|
767
754
|
|
|
768
755
|
// src/data/categories/wallet.ts
|
|
769
|
-
init_client();
|
|
770
756
|
var wallet = {
|
|
771
757
|
/** 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
758
|
detail: (params) => get("wallet/detail", params),
|
|
@@ -783,7 +769,6 @@ var wallet = {
|
|
|
783
769
|
};
|
|
784
770
|
|
|
785
771
|
// src/data/categories/web.ts
|
|
786
|
-
init_client();
|
|
787
772
|
var web = {
|
|
788
773
|
/** 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
774
|
fetch: (params) => get("web/fetch", params)
|