@radishbot/sdk 0.3.1 → 0.5.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 +35 -1
- package/dist/cli.d.ts +2 -0
- package/dist/connection.d.ts +24 -0
- package/dist/index.d.ts +34 -0
- package/dist/index.js +709 -0
- package/dist/module_bindings/action_table.d.ts +17 -0
- package/dist/module_bindings/add_log_reducer.d.ts +8 -0
- package/dist/module_bindings/add_logs_batch_reducer.d.ts +6 -0
- package/dist/module_bindings/api_key_table.d.ts +14 -0
- package/dist/module_bindings/check_timeouts_reducer.d.ts +4 -0
- package/dist/module_bindings/create_root_flow_reducer.d.ts +7 -0
- package/dist/module_bindings/create_sub_flow_reducer.d.ts +8 -0
- package/dist/module_bindings/error_group_table.d.ts +24 -0
- package/dist/module_bindings/finish_action_reducer.d.ts +6 -0
- package/dist/module_bindings/finish_flow_reducer.d.ts +7 -0
- package/dist/module_bindings/flow_table.d.ts +28 -0
- package/dist/module_bindings/log_entry_table.d.ts +15 -0
- package/dist/module_bindings/register_key_reducer.d.ts +6 -0
- package/dist/module_bindings/start_action_reducer.d.ts +6 -0
- package/dist/module_bindings/types/procedures.d.ts +1 -0
- package/dist/module_bindings/types/reducers.d.ts +21 -0
- package/dist/module_bindings/types.d.ts +59 -0
- package/dist/module_bindings/update_error_group_status_reducer.d.ts +6 -0
- package/package.json +11 -4
- package/src/cli.ts +26 -12
- package/src/index.ts +32 -4
- package/src/module_bindings/create_root_flow_reducer.ts +1 -0
- package/src/module_bindings/flow_table.ts +1 -0
- package/src/module_bindings/types.ts +1 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,709 @@
|
|
|
1
|
+
// src/module_bindings/index.ts
|
|
2
|
+
import {
|
|
3
|
+
DbConnectionBuilder as __DbConnectionBuilder,
|
|
4
|
+
DbConnectionImpl as __DbConnectionImpl,
|
|
5
|
+
SubscriptionBuilderImpl as __SubscriptionBuilderImpl,
|
|
6
|
+
convertToAccessorMap as __convertToAccessorMap,
|
|
7
|
+
makeQueryBuilder as __makeQueryBuilder,
|
|
8
|
+
procedures as __procedures,
|
|
9
|
+
reducerSchema as __reducerSchema,
|
|
10
|
+
reducers as __reducers,
|
|
11
|
+
schema as __schema,
|
|
12
|
+
table as __table
|
|
13
|
+
} from "spacetimedb";
|
|
14
|
+
|
|
15
|
+
// src/module_bindings/add_log_reducer.ts
|
|
16
|
+
import {
|
|
17
|
+
t as __t
|
|
18
|
+
} from "spacetimedb";
|
|
19
|
+
var add_log_reducer_default = {
|
|
20
|
+
keyHash: __t.string(),
|
|
21
|
+
flowId: __t.u64(),
|
|
22
|
+
level: __t.string(),
|
|
23
|
+
message: __t.string(),
|
|
24
|
+
data: __t.string()
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
// src/module_bindings/add_logs_batch_reducer.ts
|
|
28
|
+
import {
|
|
29
|
+
t as __t2
|
|
30
|
+
} from "spacetimedb";
|
|
31
|
+
var add_logs_batch_reducer_default = {
|
|
32
|
+
keyHash: __t2.string(),
|
|
33
|
+
flowId: __t2.u64(),
|
|
34
|
+
entries: __t2.string()
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
// src/module_bindings/check_timeouts_reducer.ts
|
|
38
|
+
import {
|
|
39
|
+
t as __t3
|
|
40
|
+
} from "spacetimedb";
|
|
41
|
+
var check_timeouts_reducer_default = {
|
|
42
|
+
keyHash: __t3.string()
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
// src/module_bindings/create_root_flow_reducer.ts
|
|
46
|
+
import {
|
|
47
|
+
t as __t4
|
|
48
|
+
} from "spacetimedb";
|
|
49
|
+
var create_root_flow_reducer_default = {
|
|
50
|
+
keyHash: __t4.string(),
|
|
51
|
+
runId: __t4.string(),
|
|
52
|
+
timeoutSeconds: __t4.u64(),
|
|
53
|
+
exportToken: __t4.string(),
|
|
54
|
+
release: __t4.string()
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/module_bindings/create_sub_flow_reducer.ts
|
|
58
|
+
import {
|
|
59
|
+
t as __t5
|
|
60
|
+
} from "spacetimedb";
|
|
61
|
+
var create_sub_flow_reducer_default = {
|
|
62
|
+
keyHash: __t5.string(),
|
|
63
|
+
parentFlowId: __t5.u64(),
|
|
64
|
+
name: __t5.string(),
|
|
65
|
+
timeoutSeconds: __t5.u64(),
|
|
66
|
+
exportToken: __t5.string()
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/module_bindings/finish_action_reducer.ts
|
|
70
|
+
import {
|
|
71
|
+
t as __t6
|
|
72
|
+
} from "spacetimedb";
|
|
73
|
+
var finish_action_reducer_default = {
|
|
74
|
+
keyHash: __t6.string(),
|
|
75
|
+
actionId: __t6.u64(),
|
|
76
|
+
status: __t6.string()
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
// src/module_bindings/finish_flow_reducer.ts
|
|
80
|
+
import {
|
|
81
|
+
t as __t7
|
|
82
|
+
} from "spacetimedb";
|
|
83
|
+
var finish_flow_reducer_default = {
|
|
84
|
+
keyHash: __t7.string(),
|
|
85
|
+
flowId: __t7.u64(),
|
|
86
|
+
status: __t7.string(),
|
|
87
|
+
errorMessage: __t7.string()
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
// src/module_bindings/register_key_reducer.ts
|
|
91
|
+
import {
|
|
92
|
+
t as __t8
|
|
93
|
+
} from "spacetimedb";
|
|
94
|
+
var register_key_reducer_default = {
|
|
95
|
+
keyHash: __t8.string(),
|
|
96
|
+
label: __t8.string(),
|
|
97
|
+
retentionDays: __t8.u64()
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
// src/module_bindings/start_action_reducer.ts
|
|
101
|
+
import {
|
|
102
|
+
t as __t9
|
|
103
|
+
} from "spacetimedb";
|
|
104
|
+
var start_action_reducer_default = {
|
|
105
|
+
keyHash: __t9.string(),
|
|
106
|
+
flowId: __t9.u64(),
|
|
107
|
+
name: __t9.string()
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
// src/module_bindings/update_error_group_status_reducer.ts
|
|
111
|
+
import {
|
|
112
|
+
t as __t10
|
|
113
|
+
} from "spacetimedb";
|
|
114
|
+
var update_error_group_status_reducer_default = {
|
|
115
|
+
keyHash: __t10.string(),
|
|
116
|
+
errorGroupId: __t10.u64(),
|
|
117
|
+
status: __t10.string()
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
// src/module_bindings/action_table.ts
|
|
121
|
+
import {
|
|
122
|
+
t as __t11
|
|
123
|
+
} from "spacetimedb";
|
|
124
|
+
var action_table_default = __t11.row({
|
|
125
|
+
id: __t11.u64().primaryKey(),
|
|
126
|
+
flowId: __t11.u64().name("flow_id"),
|
|
127
|
+
name: __t11.string(),
|
|
128
|
+
status: __t11.string(),
|
|
129
|
+
createdAt: __t11.timestamp().name("created_at"),
|
|
130
|
+
finishedAt: __t11.u64().name("finished_at")
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// src/module_bindings/api_key_table.ts
|
|
134
|
+
import {
|
|
135
|
+
t as __t12
|
|
136
|
+
} from "spacetimedb";
|
|
137
|
+
var api_key_table_default = __t12.row({
|
|
138
|
+
keyHash: __t12.string().primaryKey().name("key_hash"),
|
|
139
|
+
label: __t12.string(),
|
|
140
|
+
retentionDays: __t12.u64().name("retention_days"),
|
|
141
|
+
createdAt: __t12.timestamp().name("created_at")
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// src/module_bindings/error_group_table.ts
|
|
145
|
+
import {
|
|
146
|
+
t as __t13
|
|
147
|
+
} from "spacetimedb";
|
|
148
|
+
var error_group_table_default = __t13.row({
|
|
149
|
+
id: __t13.u64().primaryKey(),
|
|
150
|
+
keyHash: __t13.string().name("key_hash"),
|
|
151
|
+
fingerprint: __t13.string(),
|
|
152
|
+
message: __t13.string(),
|
|
153
|
+
path: __t13.string(),
|
|
154
|
+
release: __t13.string(),
|
|
155
|
+
count: __t13.u64(),
|
|
156
|
+
status: __t13.string(),
|
|
157
|
+
firstSeenAt: __t13.timestamp().name("first_seen_at"),
|
|
158
|
+
lastSeenAt: __t13.u64().name("last_seen_at"),
|
|
159
|
+
lastFlowId: __t13.u64().name("last_flow_id")
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// src/module_bindings/flow_table.ts
|
|
163
|
+
import {
|
|
164
|
+
t as __t14
|
|
165
|
+
} from "spacetimedb";
|
|
166
|
+
var flow_table_default = __t14.row({
|
|
167
|
+
id: __t14.u64().primaryKey(),
|
|
168
|
+
keyHash: __t14.string().name("key_hash"),
|
|
169
|
+
runId: __t14.string().name("run_id"),
|
|
170
|
+
parentFlowId: __t14.u64().name("parent_flow_id"),
|
|
171
|
+
name: __t14.string(),
|
|
172
|
+
path: __t14.string(),
|
|
173
|
+
status: __t14.string(),
|
|
174
|
+
release: __t14.string(),
|
|
175
|
+
timeoutSeconds: __t14.u64().name("timeout_seconds"),
|
|
176
|
+
createdAt: __t14.timestamp().name("created_at"),
|
|
177
|
+
finishedAt: __t14.u64().name("finished_at"),
|
|
178
|
+
exportToken: __t14.string().name("export_token")
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// src/module_bindings/log_entry_table.ts
|
|
182
|
+
import {
|
|
183
|
+
t as __t15
|
|
184
|
+
} from "spacetimedb";
|
|
185
|
+
var log_entry_table_default = __t15.row({
|
|
186
|
+
id: __t15.u64().primaryKey(),
|
|
187
|
+
flowId: __t15.u64().name("flow_id"),
|
|
188
|
+
level: __t15.string(),
|
|
189
|
+
message: __t15.string(),
|
|
190
|
+
data: __t15.string(),
|
|
191
|
+
createdAt: __t15.timestamp().name("created_at")
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
// src/module_bindings/index.ts
|
|
195
|
+
var tablesSchema = __schema({
|
|
196
|
+
action: __table({
|
|
197
|
+
name: "action",
|
|
198
|
+
indexes: [
|
|
199
|
+
{ name: "id", algorithm: "btree", columns: [
|
|
200
|
+
"id"
|
|
201
|
+
] }
|
|
202
|
+
],
|
|
203
|
+
constraints: [
|
|
204
|
+
{ name: "action_id_key", constraint: "unique", columns: ["id"] }
|
|
205
|
+
]
|
|
206
|
+
}, action_table_default),
|
|
207
|
+
apiKey: __table({
|
|
208
|
+
name: "api_key",
|
|
209
|
+
indexes: [
|
|
210
|
+
{ name: "keyHash", algorithm: "btree", columns: [
|
|
211
|
+
"keyHash"
|
|
212
|
+
] }
|
|
213
|
+
],
|
|
214
|
+
constraints: [
|
|
215
|
+
{ name: "api_key_key_hash_key", constraint: "unique", columns: ["keyHash"] }
|
|
216
|
+
]
|
|
217
|
+
}, api_key_table_default),
|
|
218
|
+
errorGroup: __table({
|
|
219
|
+
name: "error_group",
|
|
220
|
+
indexes: [
|
|
221
|
+
{ name: "fingerprint", algorithm: "btree", columns: [
|
|
222
|
+
"fingerprint"
|
|
223
|
+
] },
|
|
224
|
+
{ name: "id", algorithm: "btree", columns: [
|
|
225
|
+
"id"
|
|
226
|
+
] }
|
|
227
|
+
],
|
|
228
|
+
constraints: [
|
|
229
|
+
{ name: "error_group_fingerprint_key", constraint: "unique", columns: ["fingerprint"] },
|
|
230
|
+
{ name: "error_group_id_key", constraint: "unique", columns: ["id"] }
|
|
231
|
+
]
|
|
232
|
+
}, error_group_table_default),
|
|
233
|
+
flow: __table({
|
|
234
|
+
name: "flow",
|
|
235
|
+
indexes: [
|
|
236
|
+
{ name: "exportToken", algorithm: "btree", columns: [
|
|
237
|
+
"exportToken"
|
|
238
|
+
] },
|
|
239
|
+
{ name: "id", algorithm: "btree", columns: [
|
|
240
|
+
"id"
|
|
241
|
+
] }
|
|
242
|
+
],
|
|
243
|
+
constraints: [
|
|
244
|
+
{ name: "flow_export_token_key", constraint: "unique", columns: ["exportToken"] },
|
|
245
|
+
{ name: "flow_id_key", constraint: "unique", columns: ["id"] }
|
|
246
|
+
]
|
|
247
|
+
}, flow_table_default),
|
|
248
|
+
logEntry: __table({
|
|
249
|
+
name: "log_entry",
|
|
250
|
+
indexes: [
|
|
251
|
+
{ name: "id", algorithm: "btree", columns: [
|
|
252
|
+
"id"
|
|
253
|
+
] }
|
|
254
|
+
],
|
|
255
|
+
constraints: [
|
|
256
|
+
{ name: "log_entry_id_key", constraint: "unique", columns: ["id"] }
|
|
257
|
+
]
|
|
258
|
+
}, log_entry_table_default)
|
|
259
|
+
});
|
|
260
|
+
var reducersSchema = __reducers(__reducerSchema("add_log", add_log_reducer_default), __reducerSchema("add_logs_batch", add_logs_batch_reducer_default), __reducerSchema("check_timeouts", check_timeouts_reducer_default), __reducerSchema("create_root_flow", create_root_flow_reducer_default), __reducerSchema("create_sub_flow", create_sub_flow_reducer_default), __reducerSchema("finish_action", finish_action_reducer_default), __reducerSchema("finish_flow", finish_flow_reducer_default), __reducerSchema("register_key", register_key_reducer_default), __reducerSchema("start_action", start_action_reducer_default), __reducerSchema("update_error_group_status", update_error_group_status_reducer_default));
|
|
261
|
+
var proceduresSchema = __procedures();
|
|
262
|
+
var REMOTE_MODULE = {
|
|
263
|
+
versionInfo: {
|
|
264
|
+
cliVersion: "2.0.3"
|
|
265
|
+
},
|
|
266
|
+
tables: tablesSchema.schemaType.tables,
|
|
267
|
+
reducers: reducersSchema.reducersType.reducers,
|
|
268
|
+
...proceduresSchema
|
|
269
|
+
};
|
|
270
|
+
var tables = __makeQueryBuilder(tablesSchema.schemaType);
|
|
271
|
+
var reducers = __convertToAccessorMap(reducersSchema.reducersType.reducers);
|
|
272
|
+
|
|
273
|
+
class SubscriptionBuilder extends __SubscriptionBuilderImpl {
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
class DbConnectionBuilder extends __DbConnectionBuilder {
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
class DbConnection extends __DbConnectionImpl {
|
|
280
|
+
static builder = () => {
|
|
281
|
+
return new DbConnectionBuilder(REMOTE_MODULE, (config) => new DbConnection(config));
|
|
282
|
+
};
|
|
283
|
+
subscriptionBuilder = () => {
|
|
284
|
+
return new SubscriptionBuilder(this);
|
|
285
|
+
};
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
// src/connection.ts
|
|
289
|
+
class SdkConnection {
|
|
290
|
+
_host;
|
|
291
|
+
_dbName;
|
|
292
|
+
_conn = null;
|
|
293
|
+
_connectPromise = null;
|
|
294
|
+
_timeoutInterval = null;
|
|
295
|
+
_keyHash = null;
|
|
296
|
+
_flowWaiters = new Map;
|
|
297
|
+
constructor(host, dbName) {
|
|
298
|
+
this._host = host;
|
|
299
|
+
this._dbName = dbName;
|
|
300
|
+
}
|
|
301
|
+
setKeyHash(keyHash) {
|
|
302
|
+
this._keyHash = keyHash;
|
|
303
|
+
}
|
|
304
|
+
async connect() {
|
|
305
|
+
if (this._conn)
|
|
306
|
+
return this._conn;
|
|
307
|
+
if (this._connectPromise)
|
|
308
|
+
return this._connectPromise;
|
|
309
|
+
this._connectPromise = new Promise((resolve, reject) => {
|
|
310
|
+
const conn = DbConnection.builder().withUri(this._host).withDatabaseName(this._dbName).onConnect((c, _identity, _token) => {
|
|
311
|
+
this._conn = c;
|
|
312
|
+
c.subscriptionBuilder().onApplied(() => {
|
|
313
|
+
resolve(c);
|
|
314
|
+
}).subscribeToAllTables();
|
|
315
|
+
this._timeoutInterval = setInterval(() => {
|
|
316
|
+
if (this._keyHash) {
|
|
317
|
+
c.reducers.checkTimeouts({ keyHash: this._keyHash });
|
|
318
|
+
}
|
|
319
|
+
}, 30000);
|
|
320
|
+
}).onConnectError((_ctx, err) => {
|
|
321
|
+
reject(new Error(`SpacetimeDB connection failed: ${err}`));
|
|
322
|
+
}).build();
|
|
323
|
+
});
|
|
324
|
+
return this._connectPromise;
|
|
325
|
+
}
|
|
326
|
+
get conn() {
|
|
327
|
+
if (!this._conn)
|
|
328
|
+
throw new Error("Not connected");
|
|
329
|
+
return this._conn;
|
|
330
|
+
}
|
|
331
|
+
async createFlowAndResolveId(reducerCall, exportToken) {
|
|
332
|
+
const conn = this.conn;
|
|
333
|
+
for (const f of conn.db.flow.iter()) {
|
|
334
|
+
if (f.exportToken === exportToken)
|
|
335
|
+
return f.id;
|
|
336
|
+
}
|
|
337
|
+
return new Promise((resolve) => {
|
|
338
|
+
this._flowWaiters.set(exportToken, resolve);
|
|
339
|
+
reducerCall();
|
|
340
|
+
const check = () => {
|
|
341
|
+
for (const f of conn.db.flow.iter()) {
|
|
342
|
+
if (f.exportToken === exportToken) {
|
|
343
|
+
this._flowWaiters.delete(exportToken);
|
|
344
|
+
resolve(f.id);
|
|
345
|
+
return true;
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
return false;
|
|
349
|
+
};
|
|
350
|
+
let attempts = 0;
|
|
351
|
+
const interval = setInterval(() => {
|
|
352
|
+
if (check() || attempts++ > 100) {
|
|
353
|
+
clearInterval(interval);
|
|
354
|
+
if (attempts > 100) {
|
|
355
|
+
this._flowWaiters.delete(exportToken);
|
|
356
|
+
throw new Error(`Flow creation timed out for token ${exportToken}`);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}, 100);
|
|
360
|
+
});
|
|
361
|
+
}
|
|
362
|
+
disconnect() {
|
|
363
|
+
if (this._timeoutInterval) {
|
|
364
|
+
clearInterval(this._timeoutInterval);
|
|
365
|
+
this._timeoutInterval = null;
|
|
366
|
+
}
|
|
367
|
+
if (this._conn) {
|
|
368
|
+
this._conn.disconnect();
|
|
369
|
+
this._conn = null;
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// src/index.ts
|
|
375
|
+
var _origConsole = {
|
|
376
|
+
log: console.log,
|
|
377
|
+
info: console.info,
|
|
378
|
+
warn: console.warn,
|
|
379
|
+
error: console.error,
|
|
380
|
+
debug: console.debug
|
|
381
|
+
};
|
|
382
|
+
var _flowStack = [];
|
|
383
|
+
function _patchConsole() {
|
|
384
|
+
const capture = (level) => (...args) => {
|
|
385
|
+
const flow = _flowStack[_flowStack.length - 1];
|
|
386
|
+
if (flow) {
|
|
387
|
+
const msg = args.map((a) => {
|
|
388
|
+
if (typeof a === "string")
|
|
389
|
+
return a;
|
|
390
|
+
if (a instanceof Error)
|
|
391
|
+
return a.message;
|
|
392
|
+
try {
|
|
393
|
+
return JSON.stringify(a);
|
|
394
|
+
} catch {
|
|
395
|
+
return String(a);
|
|
396
|
+
}
|
|
397
|
+
}).join(" ");
|
|
398
|
+
const data = args.length <= 1 ? undefined : args.length === 2 ? args[1] : args.slice(1);
|
|
399
|
+
flow.log(msg, data, level);
|
|
400
|
+
}
|
|
401
|
+
_origConsole[level === "info" ? "log" : level](...args);
|
|
402
|
+
};
|
|
403
|
+
console.log = capture("info");
|
|
404
|
+
console.info = capture("info");
|
|
405
|
+
console.warn = capture("warn");
|
|
406
|
+
console.error = capture("error");
|
|
407
|
+
console.debug = capture("debug");
|
|
408
|
+
}
|
|
409
|
+
function _restoreConsole() {
|
|
410
|
+
console.log = _origConsole.log;
|
|
411
|
+
console.info = _origConsole.info;
|
|
412
|
+
console.warn = _origConsole.warn;
|
|
413
|
+
console.error = _origConsole.error;
|
|
414
|
+
console.debug = _origConsole.debug;
|
|
415
|
+
}
|
|
416
|
+
function serialize(value) {
|
|
417
|
+
if (value === undefined || value === null)
|
|
418
|
+
return "{}";
|
|
419
|
+
if (value instanceof Error)
|
|
420
|
+
return JSON.stringify({
|
|
421
|
+
name: value.name,
|
|
422
|
+
message: value.message,
|
|
423
|
+
stack: value.stack
|
|
424
|
+
});
|
|
425
|
+
try {
|
|
426
|
+
return JSON.stringify(value, (_key, v) => typeof v === "bigint" ? v.toString() : v);
|
|
427
|
+
} catch {
|
|
428
|
+
return JSON.stringify({ value: String(value) });
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
async function hashKey(secretKey) {
|
|
432
|
+
const encoder = new TextEncoder;
|
|
433
|
+
const data = encoder.encode(secretKey);
|
|
434
|
+
const hashBuffer = await crypto.subtle.digest("SHA-256", data);
|
|
435
|
+
const hashArray = Array.from(new Uint8Array(hashBuffer));
|
|
436
|
+
return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
437
|
+
}
|
|
438
|
+
function generateToken() {
|
|
439
|
+
const bytes = new Uint8Array(16);
|
|
440
|
+
crypto.getRandomValues(bytes);
|
|
441
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
class Flow {
|
|
445
|
+
_sdk;
|
|
446
|
+
_keyHash;
|
|
447
|
+
_id = null;
|
|
448
|
+
_exportToken;
|
|
449
|
+
_ready;
|
|
450
|
+
_resolveReady;
|
|
451
|
+
_pendingLogs = [];
|
|
452
|
+
_flushScheduled = false;
|
|
453
|
+
_isReady = false;
|
|
454
|
+
_finished = false;
|
|
455
|
+
_parentId;
|
|
456
|
+
_name;
|
|
457
|
+
_timeoutSeconds;
|
|
458
|
+
_release;
|
|
459
|
+
_runId;
|
|
460
|
+
constructor(sdk, keyHash, parentId, name, timeoutSeconds, release = "", runId = "") {
|
|
461
|
+
this._sdk = sdk;
|
|
462
|
+
this._keyHash = keyHash;
|
|
463
|
+
this._parentId = parentId;
|
|
464
|
+
this._name = name;
|
|
465
|
+
this._timeoutSeconds = !timeoutSeconds || timeoutSeconds === Infinity ? 0n : BigInt(timeoutSeconds);
|
|
466
|
+
this._release = release;
|
|
467
|
+
this._runId = runId;
|
|
468
|
+
this._exportToken = generateToken();
|
|
469
|
+
this._ready = new Promise((resolve) => {
|
|
470
|
+
this._resolveReady = resolve;
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
async _create() {
|
|
474
|
+
const conn = this._sdk.conn;
|
|
475
|
+
const exportToken = this._exportToken;
|
|
476
|
+
const keyHash = this._keyHash;
|
|
477
|
+
const timeoutSeconds = this._timeoutSeconds;
|
|
478
|
+
if (this._parentId === 0n) {
|
|
479
|
+
const id = await this._sdk.createFlowAndResolveId(() => conn.reducers.createRootFlow({
|
|
480
|
+
keyHash,
|
|
481
|
+
runId: this._runId,
|
|
482
|
+
timeoutSeconds,
|
|
483
|
+
exportToken,
|
|
484
|
+
release: this._release
|
|
485
|
+
}), exportToken);
|
|
486
|
+
this._id = id;
|
|
487
|
+
} else {
|
|
488
|
+
const parentFlowId = this._parentId;
|
|
489
|
+
const name = this._name;
|
|
490
|
+
const id = await this._sdk.createFlowAndResolveId(() => conn.reducers.createSubFlow({
|
|
491
|
+
keyHash,
|
|
492
|
+
parentFlowId,
|
|
493
|
+
name,
|
|
494
|
+
timeoutSeconds,
|
|
495
|
+
exportToken
|
|
496
|
+
}), exportToken);
|
|
497
|
+
this._id = id;
|
|
498
|
+
}
|
|
499
|
+
this._isReady = true;
|
|
500
|
+
this._resolveReady();
|
|
501
|
+
this._drain();
|
|
502
|
+
}
|
|
503
|
+
static MAX_PENDING = 1e4;
|
|
504
|
+
log(message, data, level = "info") {
|
|
505
|
+
if (this._finished) {
|
|
506
|
+
console.warn(`[radish] Cannot log to finished flow`);
|
|
507
|
+
return this;
|
|
508
|
+
}
|
|
509
|
+
if (this._pendingLogs.length >= Flow.MAX_PENDING) {
|
|
510
|
+
const dropped = this._pendingLogs.length - Flow.MAX_PENDING + 1;
|
|
511
|
+
this._pendingLogs.splice(0, dropped);
|
|
512
|
+
}
|
|
513
|
+
this._pendingLogs.push({
|
|
514
|
+
level,
|
|
515
|
+
message,
|
|
516
|
+
data: data !== undefined ? serialize(data) : "{}"
|
|
517
|
+
});
|
|
518
|
+
this._scheduleFlush();
|
|
519
|
+
return this;
|
|
520
|
+
}
|
|
521
|
+
info(message, data) {
|
|
522
|
+
return this.log(message, data, "info");
|
|
523
|
+
}
|
|
524
|
+
warn(message, data) {
|
|
525
|
+
return this.log(message, data, "warn");
|
|
526
|
+
}
|
|
527
|
+
error(message, data) {
|
|
528
|
+
return this.log(message, data, "error");
|
|
529
|
+
}
|
|
530
|
+
debug(message, data) {
|
|
531
|
+
return this.log(message, data, "debug");
|
|
532
|
+
}
|
|
533
|
+
action(name, timeoutSeconds = 100) {
|
|
534
|
+
const child = new Flow(this._sdk, this._keyHash, 0n, name, timeoutSeconds, this._release, this._runId);
|
|
535
|
+
this._ready.then(() => {
|
|
536
|
+
child._parentId = this._id;
|
|
537
|
+
child._create();
|
|
538
|
+
});
|
|
539
|
+
return child;
|
|
540
|
+
}
|
|
541
|
+
flow(name, timeoutSeconds = 100) {
|
|
542
|
+
return this.action(name, timeoutSeconds);
|
|
543
|
+
}
|
|
544
|
+
async a(name, fn, timeoutSeconds = 100) {
|
|
545
|
+
const child = this.action(name, timeoutSeconds);
|
|
546
|
+
_flowStack.push(child);
|
|
547
|
+
_patchConsole();
|
|
548
|
+
try {
|
|
549
|
+
const result = await fn(child);
|
|
550
|
+
await child.finish();
|
|
551
|
+
return result;
|
|
552
|
+
} catch (e) {
|
|
553
|
+
await child.finishWithError(e instanceof Error ? e : String(e));
|
|
554
|
+
throw e;
|
|
555
|
+
} finally {
|
|
556
|
+
_flowStack.pop();
|
|
557
|
+
if (_flowStack.length === 0)
|
|
558
|
+
_restoreConsole();
|
|
559
|
+
}
|
|
560
|
+
}
|
|
561
|
+
async finish() {
|
|
562
|
+
await this._ready;
|
|
563
|
+
this._drain();
|
|
564
|
+
this._finished = true;
|
|
565
|
+
this._sdk.conn.reducers.finishFlow({
|
|
566
|
+
keyHash: this._keyHash,
|
|
567
|
+
flowId: this._id,
|
|
568
|
+
status: "finished",
|
|
569
|
+
errorMessage: ""
|
|
570
|
+
});
|
|
571
|
+
}
|
|
572
|
+
async finishWithError(err) {
|
|
573
|
+
const errorMessage = err ? typeof err === "string" ? err : err.message : "";
|
|
574
|
+
if (err) {
|
|
575
|
+
this.error(errorMessage, err);
|
|
576
|
+
}
|
|
577
|
+
await this._ready;
|
|
578
|
+
this._drain();
|
|
579
|
+
this._finished = true;
|
|
580
|
+
this._sdk.conn.reducers.finishFlow({
|
|
581
|
+
keyHash: this._keyHash,
|
|
582
|
+
flowId: this._id,
|
|
583
|
+
status: "error",
|
|
584
|
+
errorMessage
|
|
585
|
+
});
|
|
586
|
+
}
|
|
587
|
+
get runId() {
|
|
588
|
+
return this._runId;
|
|
589
|
+
}
|
|
590
|
+
async exportID() {
|
|
591
|
+
await this._ready;
|
|
592
|
+
return JSON.stringify({
|
|
593
|
+
flowId: this._id.toString(),
|
|
594
|
+
exportToken: this._exportToken,
|
|
595
|
+
keyHash: this._keyHash,
|
|
596
|
+
runId: this._runId
|
|
597
|
+
});
|
|
598
|
+
}
|
|
599
|
+
async getId() {
|
|
600
|
+
await this._ready;
|
|
601
|
+
return this._id;
|
|
602
|
+
}
|
|
603
|
+
disconnect() {
|
|
604
|
+
this._sdk.disconnect();
|
|
605
|
+
}
|
|
606
|
+
async finishAndDisconnect() {
|
|
607
|
+
await this.finish();
|
|
608
|
+
await new Promise((r) => setTimeout(r, 100));
|
|
609
|
+
this.disconnect();
|
|
610
|
+
}
|
|
611
|
+
_scheduleFlush() {
|
|
612
|
+
if (this._flushScheduled)
|
|
613
|
+
return;
|
|
614
|
+
this._flushScheduled = true;
|
|
615
|
+
queueMicrotask(() => {
|
|
616
|
+
this._flushScheduled = false;
|
|
617
|
+
this._drain();
|
|
618
|
+
});
|
|
619
|
+
}
|
|
620
|
+
_drain() {
|
|
621
|
+
if (!this._isReady || this._pendingLogs.length === 0)
|
|
622
|
+
return;
|
|
623
|
+
const batch = this._pendingLogs.splice(0);
|
|
624
|
+
const conn = this._sdk.conn;
|
|
625
|
+
if (batch.length === 1) {
|
|
626
|
+
const e = batch[0];
|
|
627
|
+
conn.reducers.addLog({
|
|
628
|
+
keyHash: this._keyHash,
|
|
629
|
+
flowId: this._id,
|
|
630
|
+
level: e.level,
|
|
631
|
+
message: e.message,
|
|
632
|
+
data: e.data
|
|
633
|
+
});
|
|
634
|
+
} else {
|
|
635
|
+
conn.reducers.addLogsBatch({
|
|
636
|
+
keyHash: this._keyHash,
|
|
637
|
+
flowId: this._id,
|
|
638
|
+
entries: JSON.stringify(batch)
|
|
639
|
+
});
|
|
640
|
+
}
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
function parseRetention(retention) {
|
|
644
|
+
const match = retention.match(/^(\d+)d$/);
|
|
645
|
+
if (!match)
|
|
646
|
+
throw new Error('Invalid retention format. Use e.g. "30d"');
|
|
647
|
+
return parseInt(match[1], 10);
|
|
648
|
+
}
|
|
649
|
+
async function RL(secretKey, options = {}) {
|
|
650
|
+
const {
|
|
651
|
+
host = "wss://maincloud.spacetimedb.com",
|
|
652
|
+
dbName = "radish-log",
|
|
653
|
+
label = "",
|
|
654
|
+
defaultTimeout = 100,
|
|
655
|
+
release = "",
|
|
656
|
+
retention = "30d",
|
|
657
|
+
runId = generateRunId()
|
|
658
|
+
} = options;
|
|
659
|
+
const retentionDays = parseRetention(retention);
|
|
660
|
+
const keyHash = await hashKey(secretKey);
|
|
661
|
+
const sdk = new SdkConnection(host, dbName);
|
|
662
|
+
sdk.setKeyHash(keyHash);
|
|
663
|
+
try {
|
|
664
|
+
await sdk.connect();
|
|
665
|
+
} catch (e) {
|
|
666
|
+
throw new Error(`Failed to connect to Radish (${host}/${dbName}): ${e instanceof Error ? e.message : e}`);
|
|
667
|
+
}
|
|
668
|
+
try {
|
|
669
|
+
sdk.conn.reducers.registerKey({ keyHash, label, retentionDays: BigInt(retentionDays) });
|
|
670
|
+
} catch {}
|
|
671
|
+
try {
|
|
672
|
+
sdk.conn.reducers.checkTimeouts({ keyHash });
|
|
673
|
+
} catch {}
|
|
674
|
+
const root = new Flow(sdk, keyHash, 0n, "/", 0, release, runId);
|
|
675
|
+
root._create();
|
|
676
|
+
return root;
|
|
677
|
+
}
|
|
678
|
+
async function restoreFlow(secretKey, exportedId, options = {}) {
|
|
679
|
+
const { host = "wss://maincloud.spacetimedb.com", dbName = "radish-log" } = options;
|
|
680
|
+
const parsed = JSON.parse(exportedId);
|
|
681
|
+
const keyHash = await hashKey(secretKey);
|
|
682
|
+
if (keyHash !== parsed.keyHash) {
|
|
683
|
+
throw new Error("Secret key does not match the flow owner");
|
|
684
|
+
}
|
|
685
|
+
const sdk = new SdkConnection(host, dbName);
|
|
686
|
+
await sdk.connect();
|
|
687
|
+
const flow = new Flow(sdk, keyHash, 0n, "restored", 100, "", parsed.runId || "");
|
|
688
|
+
flow._id = BigInt(parsed.flowId);
|
|
689
|
+
flow._exportToken = parsed.exportToken;
|
|
690
|
+
flow._resolveReady();
|
|
691
|
+
return flow;
|
|
692
|
+
}
|
|
693
|
+
function generateKey() {
|
|
694
|
+
const bytes = new Uint8Array(32);
|
|
695
|
+
crypto.getRandomValues(bytes);
|
|
696
|
+
return "rl_" + Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
697
|
+
}
|
|
698
|
+
function generateRunId() {
|
|
699
|
+
const bytes = new Uint8Array(8);
|
|
700
|
+
crypto.getRandomValues(bytes);
|
|
701
|
+
return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
702
|
+
}
|
|
703
|
+
export {
|
|
704
|
+
restoreFlow,
|
|
705
|
+
generateRunId,
|
|
706
|
+
generateKey,
|
|
707
|
+
RL,
|
|
708
|
+
Flow
|
|
709
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
declare const _default: import("spacetimedb").RowBuilder<{
|
|
2
|
+
id: import("spacetimedb").U64ColumnBuilder<{
|
|
3
|
+
isPrimaryKey: true;
|
|
4
|
+
}>;
|
|
5
|
+
flowId: import("spacetimedb").U64ColumnBuilder<{
|
|
6
|
+
name: "flow_id";
|
|
7
|
+
}>;
|
|
8
|
+
name: import("spacetimedb").StringBuilder;
|
|
9
|
+
status: import("spacetimedb").StringBuilder;
|
|
10
|
+
createdAt: import("spacetimedb").TimestampColumnBuilder<{
|
|
11
|
+
name: "created_at";
|
|
12
|
+
}>;
|
|
13
|
+
finishedAt: import("spacetimedb").U64ColumnBuilder<{
|
|
14
|
+
name: "finished_at";
|
|
15
|
+
}>;
|
|
16
|
+
}>;
|
|
17
|
+
export default _default;
|