@jango-blockchained/hoox-shared 1.0.8
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 +27 -0
- package/dist/analytics.d.ts +22 -0
- package/dist/analytics.d.ts.map +1 -0
- package/dist/analytics.js +34 -0
- package/dist/api-client.d.ts +34 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +124 -0
- package/dist/colors.d.ts +34 -0
- package/dist/colors.d.ts.map +1 -0
- package/dist/colors.js +42 -0
- package/dist/config.d.ts +24 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +91 -0
- package/dist/d1/index.d.ts +5 -0
- package/dist/d1/index.d.ts.map +1 -0
- package/dist/d1/index.js +316 -0
- package/dist/d1/repository.d.ts +77 -0
- package/dist/d1/repository.d.ts.map +1 -0
- package/dist/d1/schemas.d.ts +85 -0
- package/dist/d1/schemas.d.ts.map +1 -0
- package/dist/errors.d.ts +39 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.js +106 -0
- package/dist/exchange-client.d.ts +84 -0
- package/dist/exchange-client.d.ts.map +1 -0
- package/dist/exchange-client.js +104 -0
- package/dist/format-time.d.ts +24 -0
- package/dist/format-time.d.ts.map +1 -0
- package/dist/format-time.js +57 -0
- package/dist/formatters.d.ts +32 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +155 -0
- package/dist/health.d.ts +19 -0
- package/dist/health.d.ts.map +1 -0
- package/dist/health.js +117 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2581 -0
- package/dist/kvKeys.d.ts +72 -0
- package/dist/kvKeys.d.ts.map +1 -0
- package/dist/kvKeys.js +107 -0
- package/dist/kvUtils.d.ts +26 -0
- package/dist/kvUtils.d.ts.map +1 -0
- package/dist/kvUtils.js +127 -0
- package/dist/legal.d.ts +11 -0
- package/dist/legal.d.ts.map +1 -0
- package/dist/legal.js +40 -0
- package/dist/middleware/auth.d.ts +59 -0
- package/dist/middleware/auth.d.ts.map +1 -0
- package/dist/middleware/cors.d.ts +28 -0
- package/dist/middleware/cors.d.ts.map +1 -0
- package/dist/middleware/index.d.ts +10 -0
- package/dist/middleware/index.d.ts.map +1 -0
- package/dist/middleware/index.js +375 -0
- package/dist/middleware/logger.d.ts +16 -0
- package/dist/middleware/logger.d.ts.map +1 -0
- package/dist/middleware/rate-limit.d.ts +24 -0
- package/dist/middleware/rate-limit.d.ts.map +1 -0
- package/dist/middleware/security-headers.d.ts +39 -0
- package/dist/middleware/security-headers.d.ts.map +1 -0
- package/dist/middleware/validate.d.ts +21 -0
- package/dist/middleware/validate.d.ts.map +1 -0
- package/dist/router.d.ts +11 -0
- package/dist/router.d.ts.map +1 -0
- package/dist/router.js +123 -0
- package/dist/schemas/index.d.ts +4 -0
- package/dist/schemas/index.d.ts.map +1 -0
- package/dist/schemas/index.js +656 -0
- package/dist/schemas/registry.d.ts +8 -0
- package/dist/schemas/registry.d.ts.map +1 -0
- package/dist/schemas/types.d.ts +79 -0
- package/dist/schemas/types.d.ts.map +1 -0
- package/dist/schemas/validators.d.ts +35 -0
- package/dist/schemas/validators.d.ts.map +1 -0
- package/dist/service-bindings.d.ts +32 -0
- package/dist/service-bindings.d.ts.map +1 -0
- package/dist/service-bindings.js +31 -0
- package/dist/session.d.ts +46 -0
- package/dist/session.d.ts.map +1 -0
- package/dist/session.js +79 -0
- package/dist/sse.d.ts +26 -0
- package/dist/sse.d.ts.map +1 -0
- package/dist/sse.js +91 -0
- package/dist/stores/config-store.d.ts +87 -0
- package/dist/stores/config-store.d.ts.map +1 -0
- package/dist/stores/config-store.js +132 -0
- package/dist/stores/service-store.d.ts +206 -0
- package/dist/stores/service-store.d.ts.map +1 -0
- package/dist/stores/service-store.js +359 -0
- package/dist/stores/ui-store.d.ts +63 -0
- package/dist/stores/ui-store.d.ts.map +1 -0
- package/dist/stores/ui-store.js +62 -0
- package/dist/types/errors.d.ts +16 -0
- package/dist/types/errors.d.ts.map +1 -0
- package/dist/types/errors.js +1 -0
- package/dist/types/router.d.ts +22 -0
- package/dist/types/router.d.ts.map +1 -0
- package/dist/types/router.js +1 -0
- package/dist/types.d.ts +317 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +70 -0
- package/dist/wizard/engine.d.ts +42 -0
- package/dist/wizard/engine.d.ts.map +1 -0
- package/dist/wizard/index.d.ts +7 -0
- package/dist/wizard/index.d.ts.map +1 -0
- package/dist/wizard/index.js +519 -0
- package/dist/wizard/persistence.d.ts +14 -0
- package/dist/wizard/persistence.d.ts.map +1 -0
- package/dist/wizard/presets.d.ts +34 -0
- package/dist/wizard/presets.d.ts.map +1 -0
- package/dist/wizard/provisioner.d.ts +11 -0
- package/dist/wizard/provisioner.d.ts.map +1 -0
- package/dist/wizard/types.d.ts +72 -0
- package/dist/wizard/types.d.ts.map +1 -0
- package/package.json +83 -0
package/dist/d1/index.js
ADDED
|
@@ -0,0 +1,316 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// src/errors.ts
|
|
19
|
+
function toError(err, fallback = "Unknown error") {
|
|
20
|
+
if (err instanceof Error)
|
|
21
|
+
return err.message;
|
|
22
|
+
if (typeof err === "string")
|
|
23
|
+
return err;
|
|
24
|
+
if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
25
|
+
return err.message;
|
|
26
|
+
}
|
|
27
|
+
if (err === null || err === undefined)
|
|
28
|
+
return fallback;
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(err) || fallback;
|
|
31
|
+
} catch {
|
|
32
|
+
return fallback;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function sanitizeOutput(data) {
|
|
36
|
+
if (data instanceof Error) {
|
|
37
|
+
return { name: data.name, message: data.message };
|
|
38
|
+
}
|
|
39
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
40
|
+
const sanitized = {};
|
|
41
|
+
for (const [k, v] of Object.entries(data)) {
|
|
42
|
+
if (k === "stack" || k === "cause")
|
|
43
|
+
continue;
|
|
44
|
+
sanitized[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
|
|
45
|
+
}
|
|
46
|
+
return sanitized;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(data)) {
|
|
49
|
+
return data.map(sanitizeOutput);
|
|
50
|
+
}
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
function createJsonResponse(data, status = 200) {
|
|
54
|
+
return new Response(JSON.stringify(sanitizeOutput(data)), {
|
|
55
|
+
status,
|
|
56
|
+
headers: { "Content-Type": "application/json" }
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function createSuccessResponse(result) {
|
|
60
|
+
return createJsonResponse({ success: true, result }, 200);
|
|
61
|
+
}
|
|
62
|
+
function createErrorResponse(error, status) {
|
|
63
|
+
const message = typeof error === "string" ? error : error.message;
|
|
64
|
+
const statusCode = typeof error === "string" ? status ?? 500 : error.status ?? 500;
|
|
65
|
+
const body = { success: false, error: message };
|
|
66
|
+
if (typeof error !== "string" && error.code)
|
|
67
|
+
body.code = error.code;
|
|
68
|
+
if (typeof error !== "string" && error.details !== undefined)
|
|
69
|
+
body.details = error.details;
|
|
70
|
+
return new Response(JSON.stringify(body), {
|
|
71
|
+
status: statusCode,
|
|
72
|
+
headers: { "Content-Type": "application/json" }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
var Errors = {
|
|
76
|
+
badRequest: (message) => createErrorResponse({ message, status: 400, code: "BAD_REQUEST" }),
|
|
77
|
+
unauthorized: (message = "Unauthorized") => createErrorResponse({ message, status: 401, code: "UNAUTHORIZED" }),
|
|
78
|
+
forbidden: (message = "Forbidden") => createErrorResponse({ message, status: 403, code: "FORBIDDEN" }),
|
|
79
|
+
notFound: (message = "Not found") => createErrorResponse({ message, status: 404, code: "NOT_FOUND" }),
|
|
80
|
+
methodNotAllowed: (message = "Method not allowed") => createErrorResponse({ message, status: 405, code: "METHOD_NOT_ALLOWED" }),
|
|
81
|
+
rateLimited: (retryAfter) => {
|
|
82
|
+
const res = createErrorResponse({
|
|
83
|
+
message: "Rate limit exceeded",
|
|
84
|
+
status: 429,
|
|
85
|
+
code: "RATE_LIMITED"
|
|
86
|
+
});
|
|
87
|
+
if (retryAfter)
|
|
88
|
+
res.headers.set("Retry-After", String(retryAfter));
|
|
89
|
+
return res;
|
|
90
|
+
},
|
|
91
|
+
internal: (err) => {
|
|
92
|
+
const message = err ? toError(err, "Internal server error") : "Internal server error";
|
|
93
|
+
return createErrorResponse({
|
|
94
|
+
message,
|
|
95
|
+
status: 500,
|
|
96
|
+
code: "INTERNAL_ERROR"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
|
|
101
|
+
// src/d1/repository.ts
|
|
102
|
+
class D1Repository {
|
|
103
|
+
db;
|
|
104
|
+
constructor(db) {
|
|
105
|
+
this.db = db;
|
|
106
|
+
}
|
|
107
|
+
async query(sql, params = []) {
|
|
108
|
+
try {
|
|
109
|
+
const stmt = this.db.prepare(sql).bind(...params);
|
|
110
|
+
const result = await stmt.all();
|
|
111
|
+
return {
|
|
112
|
+
success: result.success,
|
|
113
|
+
results: result.results,
|
|
114
|
+
error: result.error
|
|
115
|
+
};
|
|
116
|
+
} catch (error) {
|
|
117
|
+
return {
|
|
118
|
+
success: false,
|
|
119
|
+
error: toError(error)
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
async execute(sql, params = []) {
|
|
124
|
+
try {
|
|
125
|
+
const stmt = this.db.prepare(sql).bind(...params);
|
|
126
|
+
const result = await stmt.run();
|
|
127
|
+
return {
|
|
128
|
+
success: result.success,
|
|
129
|
+
meta: {
|
|
130
|
+
last_row_id: result.meta?.last_row_id,
|
|
131
|
+
changes: result.meta?.changes,
|
|
132
|
+
duration: result.meta?.duration
|
|
133
|
+
},
|
|
134
|
+
error: result.error
|
|
135
|
+
};
|
|
136
|
+
} catch (error) {
|
|
137
|
+
return {
|
|
138
|
+
success: false,
|
|
139
|
+
error: toError(error)
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
async batch(statements) {
|
|
144
|
+
try {
|
|
145
|
+
const prepared = statements.map((s) => s.params && s.params.length > 0 ? this.db.prepare(s.query).bind(...s.params) : this.db.prepare(s.query));
|
|
146
|
+
const results = await this.db.batch(...prepared);
|
|
147
|
+
return results.map((r) => ({
|
|
148
|
+
success: r.success,
|
|
149
|
+
meta: r.meta ? { last_row_id: r.meta.last_row_id, changes: r.meta.changes } : undefined,
|
|
150
|
+
error: r.error
|
|
151
|
+
}));
|
|
152
|
+
} catch (error) {
|
|
153
|
+
return statements.map(() => ({
|
|
154
|
+
success: false,
|
|
155
|
+
error: toError(error)
|
|
156
|
+
}));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
prepare(sql) {
|
|
160
|
+
return this.db.prepare(sql);
|
|
161
|
+
}
|
|
162
|
+
async getTrades(limit = 50, offset = 0) {
|
|
163
|
+
return this.query("SELECT * FROM trades ORDER BY timestamp DESC LIMIT ? OFFSET ?", [limit, offset]);
|
|
164
|
+
}
|
|
165
|
+
async insertTrade(trade) {
|
|
166
|
+
return this.execute(`INSERT INTO trades (id, timestamp, exchange, symbol, action, quantity, price, leverage, status, raw_response)
|
|
167
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
168
|
+
trade.id || crypto.randomUUID(),
|
|
169
|
+
trade.timestamp,
|
|
170
|
+
trade.exchange,
|
|
171
|
+
trade.symbol,
|
|
172
|
+
trade.action,
|
|
173
|
+
trade.quantity ?? null,
|
|
174
|
+
trade.price ?? null,
|
|
175
|
+
trade.leverage ?? null,
|
|
176
|
+
trade.status,
|
|
177
|
+
trade.raw_response ?? null
|
|
178
|
+
]);
|
|
179
|
+
}
|
|
180
|
+
async getPositions(status) {
|
|
181
|
+
if (status) {
|
|
182
|
+
return this.query("SELECT * FROM positions WHERE status = ? ORDER BY updated_at DESC", [status]);
|
|
183
|
+
}
|
|
184
|
+
return this.query("SELECT * FROM positions ORDER BY updated_at DESC");
|
|
185
|
+
}
|
|
186
|
+
async upsertPosition(position) {
|
|
187
|
+
return this.execute(`REPLACE INTO positions (id, exchange, symbol, side, entry_price, mark_price, liquidation_price, leverage, size, unrealized_pnl, status, updated_at)
|
|
188
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, [
|
|
189
|
+
position.id,
|
|
190
|
+
position.exchange,
|
|
191
|
+
position.symbol,
|
|
192
|
+
position.side,
|
|
193
|
+
position.entry_price ?? null,
|
|
194
|
+
position.mark_price ?? null,
|
|
195
|
+
position.liquidation_price ?? null,
|
|
196
|
+
position.leverage ?? null,
|
|
197
|
+
position.size,
|
|
198
|
+
position.unrealized_pnl ?? null,
|
|
199
|
+
position.status,
|
|
200
|
+
position.updated_at
|
|
201
|
+
]);
|
|
202
|
+
}
|
|
203
|
+
async getTradeSignals(limit = 10) {
|
|
204
|
+
return this.query(`SELECT signal_id, timestamp, symbol, signal_type, source, processed_at
|
|
205
|
+
FROM trade_signals
|
|
206
|
+
ORDER BY processed_at DESC
|
|
207
|
+
LIMIT ?`, [limit]);
|
|
208
|
+
}
|
|
209
|
+
async insertTradeSignal(signal) {
|
|
210
|
+
return this.execute(`INSERT INTO trade_signals (signal_id, timestamp, symbol, signal_type, source, raw_data)
|
|
211
|
+
VALUES (?, ?, ?, ?, ?, ?)`, [
|
|
212
|
+
signal.signal_id,
|
|
213
|
+
signal.timestamp,
|
|
214
|
+
signal.symbol,
|
|
215
|
+
signal.signal_type,
|
|
216
|
+
signal.source ?? null,
|
|
217
|
+
signal.raw_data ?? null
|
|
218
|
+
]);
|
|
219
|
+
}
|
|
220
|
+
async getSystemLogs(limit = 50) {
|
|
221
|
+
return this.query("SELECT * FROM system_logs ORDER BY timestamp DESC LIMIT ?", [limit]);
|
|
222
|
+
}
|
|
223
|
+
async insertSystemLog(log) {
|
|
224
|
+
return this.execute(`INSERT INTO system_logs (id, level, service, message, details) VALUES (?, ?, ?, ?, ?)`, [
|
|
225
|
+
log.id || crypto.randomUUID(),
|
|
226
|
+
log.level,
|
|
227
|
+
log.service,
|
|
228
|
+
log.message,
|
|
229
|
+
log.details ?? null
|
|
230
|
+
]);
|
|
231
|
+
}
|
|
232
|
+
async getLatestBalances() {
|
|
233
|
+
return this.query(`
|
|
234
|
+
SELECT b.id, b.exchange, b.asset, b.total, b.free, b.used, b.timestamp
|
|
235
|
+
FROM balances b
|
|
236
|
+
INNER JOIN (
|
|
237
|
+
SELECT exchange, asset, MAX(timestamp) as max_time
|
|
238
|
+
FROM balances
|
|
239
|
+
GROUP BY exchange, asset
|
|
240
|
+
) latest ON b.exchange = latest.exchange AND b.asset = latest.asset AND b.timestamp = latest.max_time
|
|
241
|
+
`);
|
|
242
|
+
}
|
|
243
|
+
async healthCheck() {
|
|
244
|
+
try {
|
|
245
|
+
await this.db.prepare("SELECT 1").first();
|
|
246
|
+
return true;
|
|
247
|
+
} catch {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
// src/d1/schemas.ts
|
|
253
|
+
import { z } from "zod";
|
|
254
|
+
var TradeRecordSchema = z.object({
|
|
255
|
+
id: z.string(),
|
|
256
|
+
signal_id: z.string().nullable().optional(),
|
|
257
|
+
timestamp: z.number().int().positive(),
|
|
258
|
+
exchange: z.string().min(1),
|
|
259
|
+
symbol: z.string().min(1),
|
|
260
|
+
action: z.string().min(1),
|
|
261
|
+
quantity: z.number().nullable(),
|
|
262
|
+
price: z.number().nullable(),
|
|
263
|
+
leverage: z.number().nullable(),
|
|
264
|
+
status: z.string().min(1),
|
|
265
|
+
error_message: z.string().nullable().optional(),
|
|
266
|
+
raw_response: z.string().nullable().optional(),
|
|
267
|
+
created_at: z.number().int().optional()
|
|
268
|
+
}).strict();
|
|
269
|
+
var PositionRecordSchema = z.object({
|
|
270
|
+
id: z.string(),
|
|
271
|
+
exchange: z.string().min(1),
|
|
272
|
+
symbol: z.string().min(1),
|
|
273
|
+
side: z.enum(["LONG", "SHORT"]),
|
|
274
|
+
entry_price: z.number().nullable().optional(),
|
|
275
|
+
mark_price: z.number().nullable().optional(),
|
|
276
|
+
liquidation_price: z.number().nullable().optional(),
|
|
277
|
+
leverage: z.number().nullable().optional(),
|
|
278
|
+
size: z.number().nullable(),
|
|
279
|
+
unrealized_pnl: z.number().nullable().optional(),
|
|
280
|
+
status: z.enum(["OPEN", "CLOSED"]),
|
|
281
|
+
updated_at: z.number().int()
|
|
282
|
+
}).strict();
|
|
283
|
+
var BalanceRecordSchema = z.object({
|
|
284
|
+
id: z.string(),
|
|
285
|
+
exchange: z.string().min(1),
|
|
286
|
+
asset: z.string().min(1),
|
|
287
|
+
free: z.number().nullable(),
|
|
288
|
+
used: z.number().nullable(),
|
|
289
|
+
total: z.number().nullable(),
|
|
290
|
+
timestamp: z.number().int().positive()
|
|
291
|
+
}).strict();
|
|
292
|
+
var SystemLogRecordSchema = z.object({
|
|
293
|
+
id: z.string(),
|
|
294
|
+
timestamp: z.number().int().optional(),
|
|
295
|
+
level: z.string().min(1),
|
|
296
|
+
service: z.string().min(1),
|
|
297
|
+
message: z.string().min(1),
|
|
298
|
+
details: z.string().nullable().optional()
|
|
299
|
+
}).strict();
|
|
300
|
+
var TradeSignalRecordSchema = z.object({
|
|
301
|
+
signal_id: z.string(),
|
|
302
|
+
timestamp: z.number().int().positive(),
|
|
303
|
+
symbol: z.string().min(1),
|
|
304
|
+
signal_type: z.string().min(1),
|
|
305
|
+
source: z.string().nullable().optional(),
|
|
306
|
+
raw_data: z.string().nullable().optional(),
|
|
307
|
+
processed_at: z.number().int().optional()
|
|
308
|
+
}).strict();
|
|
309
|
+
export {
|
|
310
|
+
TradeSignalRecordSchema,
|
|
311
|
+
TradeRecordSchema,
|
|
312
|
+
SystemLogRecordSchema,
|
|
313
|
+
PositionRecordSchema,
|
|
314
|
+
D1Repository,
|
|
315
|
+
BalanceRecordSchema
|
|
316
|
+
};
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import type { D1Database, D1PreparedStatement } from "@cloudflare/workers-types";
|
|
2
|
+
import type { TradeRecord, PositionRecord, BalanceRecord, SystemLogRecord, TradeSignalRecord, D1QueryResult } from "./schemas";
|
|
3
|
+
export interface BatchStatement {
|
|
4
|
+
query: string;
|
|
5
|
+
params?: unknown[];
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Typed D1 repository providing convenience methods for the hoox database schema.
|
|
9
|
+
*
|
|
10
|
+
* Tables: trades, positions, balances, system_logs, trade_signals
|
|
11
|
+
*/
|
|
12
|
+
export declare class D1Repository {
|
|
13
|
+
private readonly db;
|
|
14
|
+
constructor(db: D1Database);
|
|
15
|
+
/**
|
|
16
|
+
* Execute a SELECT query and return typed results
|
|
17
|
+
*/
|
|
18
|
+
query<T = Record<string, unknown>>(sql: string, params?: unknown[]): Promise<D1QueryResult<T>>;
|
|
19
|
+
/**
|
|
20
|
+
* Execute a write query (INSERT, UPDATE, DELETE, REPLACE)
|
|
21
|
+
*/
|
|
22
|
+
execute(sql: string, params?: unknown[]): Promise<D1QueryResult>;
|
|
23
|
+
/**
|
|
24
|
+
* Execute multiple statements in a batch transaction
|
|
25
|
+
*/
|
|
26
|
+
batch(statements: BatchStatement[]): Promise<D1QueryResult[]>;
|
|
27
|
+
/**
|
|
28
|
+
* Return a raw D1PreparedStatement for complex or dynamic queries
|
|
29
|
+
*/
|
|
30
|
+
prepare(sql: string): D1PreparedStatement;
|
|
31
|
+
/**
|
|
32
|
+
* Retrieve recent trades ordered by timestamp descending
|
|
33
|
+
*/
|
|
34
|
+
getTrades(limit?: number, offset?: number): Promise<D1QueryResult<TradeRecord>>;
|
|
35
|
+
/**
|
|
36
|
+
* Insert a new trade record
|
|
37
|
+
*/
|
|
38
|
+
insertTrade(trade: Omit<TradeRecord, "id" | "created_at"> & {
|
|
39
|
+
id?: string;
|
|
40
|
+
}): Promise<D1QueryResult>;
|
|
41
|
+
/**
|
|
42
|
+
* Retrieve positions, optionally filtered by status
|
|
43
|
+
*/
|
|
44
|
+
getPositions(status?: "OPEN" | "CLOSED"): Promise<D1QueryResult<PositionRecord>>;
|
|
45
|
+
/**
|
|
46
|
+
* Upsert a position record (REPLACE INTO)
|
|
47
|
+
*/
|
|
48
|
+
upsertPosition(position: PositionRecord): Promise<D1QueryResult>;
|
|
49
|
+
/**
|
|
50
|
+
* Retrieve recent trade signals
|
|
51
|
+
*/
|
|
52
|
+
getTradeSignals(limit?: number): Promise<D1QueryResult<TradeSignalRecord>>;
|
|
53
|
+
/**
|
|
54
|
+
* Insert a new trade signal
|
|
55
|
+
*/
|
|
56
|
+
insertTradeSignal(signal: TradeSignalRecord): Promise<D1QueryResult>;
|
|
57
|
+
/**
|
|
58
|
+
* Retrieve recent system logs
|
|
59
|
+
*/
|
|
60
|
+
getSystemLogs(limit?: number): Promise<D1QueryResult<SystemLogRecord>>;
|
|
61
|
+
/**
|
|
62
|
+
* Insert a new system log entry (id and timestamp are auto-generated)
|
|
63
|
+
*/
|
|
64
|
+
insertSystemLog(log: Pick<SystemLogRecord, "level" | "service" | "message"> & {
|
|
65
|
+
details?: string | null;
|
|
66
|
+
id?: string;
|
|
67
|
+
}): Promise<D1QueryResult>;
|
|
68
|
+
/**
|
|
69
|
+
* Retrieve the latest balance snapshot per exchange and asset
|
|
70
|
+
*/
|
|
71
|
+
getLatestBalances(): Promise<D1QueryResult<BalanceRecord>>;
|
|
72
|
+
/**
|
|
73
|
+
* Quick health check — verifies the database connection is alive
|
|
74
|
+
*/
|
|
75
|
+
healthCheck(): Promise<boolean>;
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=repository.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"repository.d.ts","sourceRoot":"","sources":["../../src/d1/repository.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EAEV,mBAAmB,EACpB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACV,WAAW,EACX,cAAc,EACd,aAAa,EACb,eAAe,EACf,iBAAiB,EACjB,aAAa,EACd,MAAM,WAAW,CAAC;AAGnB,MAAM,WAAW,cAAc;IAC7B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;CACpB;AAED;;;;GAIG;AACH,qBAAa,YAAY;IACX,OAAO,CAAC,QAAQ,CAAC,EAAE;gBAAF,EAAE,EAAE,UAAU;IAE3C;;OAEG;IACG,KAAK,CAAC,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,GAAG,EAAE,MAAM,EACX,MAAM,GAAE,OAAO,EAAO,GACrB,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAiB5B;;OAEG;IACG,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,OAAO,EAAO,GAAG,OAAO,CAAC,aAAa,CAAC;IAqB1E;;OAEG;IACG,KAAK,CAAC,UAAU,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,aAAa,EAAE,CAAC;IAuBnE;;OAEG;IACH,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,mBAAmB;IAMzC;;OAEG;IACG,SAAS,CACb,KAAK,GAAE,MAAW,EAClB,MAAM,GAAE,MAAU,GACjB,OAAO,CAAC,aAAa,CAAC,WAAW,CAAC,CAAC;IAOtC;;OAEG;IACG,WAAW,CACf,KAAK,EAAE,IAAI,CAAC,WAAW,EAAE,IAAI,GAAG,YAAY,CAAC,GAAG;QAAE,EAAE,CAAC,EAAE,MAAM,CAAA;KAAE,GAC9D,OAAO,CAAC,aAAa,CAAC;IAmBzB;;OAEG;IACG,YAAY,CAChB,MAAM,CAAC,EAAE,MAAM,GAAG,QAAQ,GACzB,OAAO,CAAC,aAAa,CAAC,cAAc,CAAC,CAAC;IAYzC;;OAEG;IACG,cAAc,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,aAAa,CAAC;IAqBtE;;OAEG;IACG,eAAe,CACnB,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC;IAU5C;;OAEG;IACG,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,OAAO,CAAC,aAAa,CAAC;IAe1E;;OAEG;IACG,aAAa,CACjB,KAAK,GAAE,MAAW,GACjB,OAAO,CAAC,aAAa,CAAC,eAAe,CAAC,CAAC;IAO1C;;OAEG;IACG,eAAe,CACnB,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,OAAO,GAAG,SAAS,GAAG,SAAS,CAAC,GAAG;QAC5D,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;QACxB,EAAE,CAAC,EAAE,MAAM,CAAC;KACb,GACA,OAAO,CAAC,aAAa,CAAC;IAazB;;OAEG;IACG,iBAAiB,IAAI,OAAO,CAAC,aAAa,CAAC,aAAa,CAAC,CAAC;IAYhE;;OAEG;IACG,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;CAQtC"}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* TypeScript interfaces for D1 database tables
|
|
3
|
+
* Based on the schema defined in workers/trade-worker/schema.sql
|
|
4
|
+
*/
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
export declare const TradeRecordSchema: z.ZodObject<{
|
|
7
|
+
id: z.ZodString;
|
|
8
|
+
signal_id: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
9
|
+
timestamp: z.ZodNumber;
|
|
10
|
+
exchange: z.ZodString;
|
|
11
|
+
symbol: z.ZodString;
|
|
12
|
+
action: z.ZodString;
|
|
13
|
+
quantity: z.ZodNullable<z.ZodNumber>;
|
|
14
|
+
price: z.ZodNullable<z.ZodNumber>;
|
|
15
|
+
leverage: z.ZodNullable<z.ZodNumber>;
|
|
16
|
+
status: z.ZodString;
|
|
17
|
+
error_message: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
18
|
+
raw_response: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
19
|
+
created_at: z.ZodOptional<z.ZodNumber>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
export declare const PositionRecordSchema: z.ZodObject<{
|
|
22
|
+
id: z.ZodString;
|
|
23
|
+
exchange: z.ZodString;
|
|
24
|
+
symbol: z.ZodString;
|
|
25
|
+
side: z.ZodEnum<{
|
|
26
|
+
LONG: "LONG";
|
|
27
|
+
SHORT: "SHORT";
|
|
28
|
+
}>;
|
|
29
|
+
entry_price: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
30
|
+
mark_price: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
31
|
+
liquidation_price: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
32
|
+
leverage: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
33
|
+
size: z.ZodNullable<z.ZodNumber>;
|
|
34
|
+
unrealized_pnl: z.ZodOptional<z.ZodNullable<z.ZodNumber>>;
|
|
35
|
+
status: z.ZodEnum<{
|
|
36
|
+
OPEN: "OPEN";
|
|
37
|
+
CLOSED: "CLOSED";
|
|
38
|
+
}>;
|
|
39
|
+
updated_at: z.ZodNumber;
|
|
40
|
+
}, z.core.$strict>;
|
|
41
|
+
export declare const BalanceRecordSchema: z.ZodObject<{
|
|
42
|
+
id: z.ZodString;
|
|
43
|
+
exchange: z.ZodString;
|
|
44
|
+
asset: z.ZodString;
|
|
45
|
+
free: z.ZodNullable<z.ZodNumber>;
|
|
46
|
+
used: z.ZodNullable<z.ZodNumber>;
|
|
47
|
+
total: z.ZodNullable<z.ZodNumber>;
|
|
48
|
+
timestamp: z.ZodNumber;
|
|
49
|
+
}, z.core.$strict>;
|
|
50
|
+
export declare const SystemLogRecordSchema: z.ZodObject<{
|
|
51
|
+
id: z.ZodString;
|
|
52
|
+
timestamp: z.ZodOptional<z.ZodNumber>;
|
|
53
|
+
level: z.ZodString;
|
|
54
|
+
service: z.ZodString;
|
|
55
|
+
message: z.ZodString;
|
|
56
|
+
details: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
57
|
+
}, z.core.$strict>;
|
|
58
|
+
export declare const TradeSignalRecordSchema: z.ZodObject<{
|
|
59
|
+
signal_id: z.ZodString;
|
|
60
|
+
timestamp: z.ZodNumber;
|
|
61
|
+
symbol: z.ZodString;
|
|
62
|
+
signal_type: z.ZodString;
|
|
63
|
+
source: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
64
|
+
raw_data: z.ZodOptional<z.ZodNullable<z.ZodString>>;
|
|
65
|
+
processed_at: z.ZodOptional<z.ZodNumber>;
|
|
66
|
+
}, z.core.$strict>;
|
|
67
|
+
export type TradeRecord = z.infer<typeof TradeRecordSchema>;
|
|
68
|
+
export type PositionRecord = z.infer<typeof PositionRecordSchema>;
|
|
69
|
+
export type BalanceRecord = z.infer<typeof BalanceRecordSchema>;
|
|
70
|
+
export type SystemLogRecord = z.infer<typeof SystemLogRecordSchema>;
|
|
71
|
+
export type TradeSignalRecord = z.infer<typeof TradeSignalRecordSchema>;
|
|
72
|
+
/**
|
|
73
|
+
* Generic D1 query result wrapper
|
|
74
|
+
*/
|
|
75
|
+
export interface D1QueryResult<T = unknown> {
|
|
76
|
+
success: boolean;
|
|
77
|
+
results?: T[];
|
|
78
|
+
error?: string;
|
|
79
|
+
meta?: {
|
|
80
|
+
last_row_id?: number;
|
|
81
|
+
changes?: number;
|
|
82
|
+
duration?: number;
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
//# sourceMappingURL=schemas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schemas.d.ts","sourceRoot":"","sources":["../../src/d1/schemas.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAIxB,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;kBAgBnB,CAAC;AAEZ,eAAO,MAAM,oBAAoB;;;;;;;;;;;;;;;;;;;kBAetB,CAAC;AAEZ,eAAO,MAAM,mBAAmB;;;;;;;;kBAUrB,CAAC;AAEZ,eAAO,MAAM,qBAAqB;;;;;;;kBASvB,CAAC;AAEZ,eAAO,MAAM,uBAAuB;;;;;;;;kBAUzB,CAAC;AAIZ,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAC;AAC5D,MAAM,MAAM,cAAc,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAClE,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,iBAAiB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,uBAAuB,CAAC,CAAC;AAExE;;GAEG;AACH,MAAM,WAAW,aAAa,CAAC,CAAC,GAAG,OAAO;IACxC,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,CAAC,EAAE,CAAC;IACd,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE;QACL,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;CACH"}
|
package/dist/errors.d.ts
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Error handling utilities for Cloudflare Workers and Dashboard
|
|
3
|
+
* Centralizes error response creation across the monorepo
|
|
4
|
+
*/
|
|
5
|
+
import type { AppError } from "./types/errors";
|
|
6
|
+
/**
|
|
7
|
+
* Safely extract an error message from any caught value.
|
|
8
|
+
* Eliminates the duplicated `error instanceof Error ? error.message : String(error)`
|
|
9
|
+
* pattern that appears ~40+ times across the codebase.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* try { ... } catch (err) {
|
|
13
|
+
* console.error(toError(err));
|
|
14
|
+
* }
|
|
15
|
+
*/
|
|
16
|
+
export declare function toError(err: unknown, fallback?: string): string;
|
|
17
|
+
export declare function createJsonResponse(data: unknown, status?: number): Response;
|
|
18
|
+
/**
|
|
19
|
+
* Create a success response with standard format
|
|
20
|
+
*/
|
|
21
|
+
export declare function createSuccessResponse(result?: unknown): Response;
|
|
22
|
+
/**
|
|
23
|
+
* Create a Cloudflare Worker error response
|
|
24
|
+
* Includes `success: false` in the response body for consistent client-side handling
|
|
25
|
+
*/
|
|
26
|
+
export declare function createErrorResponse(error: AppError | string, status?: number): Response;
|
|
27
|
+
/**
|
|
28
|
+
* Predefined error factories for common error types
|
|
29
|
+
*/
|
|
30
|
+
export declare const Errors: {
|
|
31
|
+
badRequest: (message: string) => Response;
|
|
32
|
+
unauthorized: (message?: string) => Response;
|
|
33
|
+
forbidden: (message?: string) => Response;
|
|
34
|
+
notFound: (message?: string) => Response;
|
|
35
|
+
methodNotAllowed: (message?: string) => Response;
|
|
36
|
+
rateLimited: (retryAfter?: number) => Response;
|
|
37
|
+
internal: (err?: unknown) => Response;
|
|
38
|
+
};
|
|
39
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAiB,MAAM,gBAAgB,CAAC;AAE9D;;;;;;;;;GASG;AACH,wBAAgB,OAAO,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,SAAkB,GAAG,MAAM,CAkBxE;AA+BD,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,SAAM,GAAG,QAAQ,CAKxE;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,CAAC,EAAE,OAAO,GAAG,QAAQ,CAEhE;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,QAAQ,GAAG,MAAM,EACxB,MAAM,CAAC,EAAE,MAAM,GACd,QAAQ,CAcV;AAED;;GAEG;AACH,eAAO,MAAM,MAAM;0BACK,MAAM;;;;;+BAUD,MAAM;qBAShB,OAAO;CAUzB,CAAC"}
|
package/dist/errors.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
// @bun
|
|
2
|
+
var __defProp = Object.defineProperty;
|
|
3
|
+
var __returnValue = (v) => v;
|
|
4
|
+
function __exportSetter(name, newValue) {
|
|
5
|
+
this[name] = __returnValue.bind(null, newValue);
|
|
6
|
+
}
|
|
7
|
+
var __export = (target, all) => {
|
|
8
|
+
for (var name in all)
|
|
9
|
+
__defProp(target, name, {
|
|
10
|
+
get: all[name],
|
|
11
|
+
enumerable: true,
|
|
12
|
+
configurable: true,
|
|
13
|
+
set: __exportSetter.bind(all, name)
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
|
|
17
|
+
|
|
18
|
+
// src/errors.ts
|
|
19
|
+
function toError(err, fallback = "Unknown error") {
|
|
20
|
+
if (err instanceof Error)
|
|
21
|
+
return err.message;
|
|
22
|
+
if (typeof err === "string")
|
|
23
|
+
return err;
|
|
24
|
+
if (err && typeof err === "object" && "message" in err && typeof err.message === "string") {
|
|
25
|
+
return err.message;
|
|
26
|
+
}
|
|
27
|
+
if (err === null || err === undefined)
|
|
28
|
+
return fallback;
|
|
29
|
+
try {
|
|
30
|
+
return JSON.stringify(err) || fallback;
|
|
31
|
+
} catch {
|
|
32
|
+
return fallback;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function sanitizeOutput(data) {
|
|
36
|
+
if (data instanceof Error) {
|
|
37
|
+
return { name: data.name, message: data.message };
|
|
38
|
+
}
|
|
39
|
+
if (data && typeof data === "object" && !Array.isArray(data)) {
|
|
40
|
+
const sanitized = {};
|
|
41
|
+
for (const [k, v] of Object.entries(data)) {
|
|
42
|
+
if (k === "stack" || k === "cause")
|
|
43
|
+
continue;
|
|
44
|
+
sanitized[k] = v instanceof Error ? { name: v.name, message: v.message } : sanitizeOutput(v);
|
|
45
|
+
}
|
|
46
|
+
return sanitized;
|
|
47
|
+
}
|
|
48
|
+
if (Array.isArray(data)) {
|
|
49
|
+
return data.map(sanitizeOutput);
|
|
50
|
+
}
|
|
51
|
+
return data;
|
|
52
|
+
}
|
|
53
|
+
function createJsonResponse(data, status = 200) {
|
|
54
|
+
return new Response(JSON.stringify(sanitizeOutput(data)), {
|
|
55
|
+
status,
|
|
56
|
+
headers: { "Content-Type": "application/json" }
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
function createSuccessResponse(result) {
|
|
60
|
+
return createJsonResponse({ success: true, result }, 200);
|
|
61
|
+
}
|
|
62
|
+
function createErrorResponse(error, status) {
|
|
63
|
+
const message = typeof error === "string" ? error : error.message;
|
|
64
|
+
const statusCode = typeof error === "string" ? status ?? 500 : error.status ?? 500;
|
|
65
|
+
const body = { success: false, error: message };
|
|
66
|
+
if (typeof error !== "string" && error.code)
|
|
67
|
+
body.code = error.code;
|
|
68
|
+
if (typeof error !== "string" && error.details !== undefined)
|
|
69
|
+
body.details = error.details;
|
|
70
|
+
return new Response(JSON.stringify(body), {
|
|
71
|
+
status: statusCode,
|
|
72
|
+
headers: { "Content-Type": "application/json" }
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
var Errors = {
|
|
76
|
+
badRequest: (message) => createErrorResponse({ message, status: 400, code: "BAD_REQUEST" }),
|
|
77
|
+
unauthorized: (message = "Unauthorized") => createErrorResponse({ message, status: 401, code: "UNAUTHORIZED" }),
|
|
78
|
+
forbidden: (message = "Forbidden") => createErrorResponse({ message, status: 403, code: "FORBIDDEN" }),
|
|
79
|
+
notFound: (message = "Not found") => createErrorResponse({ message, status: 404, code: "NOT_FOUND" }),
|
|
80
|
+
methodNotAllowed: (message = "Method not allowed") => createErrorResponse({ message, status: 405, code: "METHOD_NOT_ALLOWED" }),
|
|
81
|
+
rateLimited: (retryAfter) => {
|
|
82
|
+
const res = createErrorResponse({
|
|
83
|
+
message: "Rate limit exceeded",
|
|
84
|
+
status: 429,
|
|
85
|
+
code: "RATE_LIMITED"
|
|
86
|
+
});
|
|
87
|
+
if (retryAfter)
|
|
88
|
+
res.headers.set("Retry-After", String(retryAfter));
|
|
89
|
+
return res;
|
|
90
|
+
},
|
|
91
|
+
internal: (err) => {
|
|
92
|
+
const message = err ? toError(err, "Internal server error") : "Internal server error";
|
|
93
|
+
return createErrorResponse({
|
|
94
|
+
message,
|
|
95
|
+
status: 500,
|
|
96
|
+
code: "INTERNAL_ERROR"
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
export {
|
|
101
|
+
toError,
|
|
102
|
+
createSuccessResponse,
|
|
103
|
+
createJsonResponse,
|
|
104
|
+
createErrorResponse,
|
|
105
|
+
Errors
|
|
106
|
+
};
|