@radishbot/sdk 0.1.0 → 0.2.1

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 CHANGED
@@ -13,20 +13,20 @@ npm install @radishbot/sdk
13
13
  ## Quick Start
14
14
 
15
15
  ```ts
16
- import { RL, generateKey } from '@radishbot/sdk';
16
+ import { RL, generateKey } from "@radishbot/sdk";
17
17
 
18
18
  const key = generateKey(); // save this — it's your dashboard login
19
19
 
20
- const root = await RL(key);
20
+ const root = await RL(key, { release: "v1.0.0", retention: "30d" });
21
21
 
22
- await root.a('handle-request', async (req) => {
23
- console.log('GET /api/users'); // automatically captured
22
+ await root.a("handle-request", async (req) => {
23
+ console.log("GET /api/users"); // automatically captured
24
24
 
25
- const users = await req.a('db-query', async () => {
26
- return await db.query('SELECT * FROM users');
25
+ const users = await req.a("db-query", async () => {
26
+ return await db.query("SELECT * FROM users");
27
27
  });
28
28
 
29
- console.log('Done', { count: users.length });
29
+ console.log("Done", { count: users.length });
30
30
  });
31
31
 
32
32
  await root.finish();
@@ -39,12 +39,12 @@ Open the dashboard, paste your key, see everything.
39
39
  Inside `.a()` callbacks, `console.log/warn/error/debug` are automatically captured as action logs. Output still prints to the terminal — but it also gets sent to the dashboard with full context.
40
40
 
41
41
  ```ts
42
- await root.a('migrate', async () => {
43
- console.log('Starting migration'); // → info log
44
- console.warn('Deprecated column found'); // → warn log
45
- console.error('Failed to migrate users'); // → error log
46
- console.debug('SQL: ALTER TABLE ...'); // → debug log
47
- console.log('Done', { tables: 5 }); // → info log with data
42
+ await root.a("migrate", async () => {
43
+ console.log("Starting migration"); // → info log
44
+ console.warn("Deprecated column found"); // → warn log
45
+ console.error("Failed to migrate users"); // → error log
46
+ console.debug("SQL: ALTER TABLE ..."); // → debug log
47
+ console.log("Done", { tables: 5 }); // → info log with data
48
48
  });
49
49
  ```
50
50
 
@@ -57,15 +57,15 @@ Actions are nested scopes. Every `RL()` call creates a root action at `/`. Sub-a
57
57
  ```ts
58
58
  const root = await RL(key);
59
59
 
60
- await root.a('request', async (req) => {
61
- console.log('handling request');
60
+ await root.a("request", async (req) => {
61
+ console.log("handling request");
62
62
 
63
- await req.a('database', async () => {
64
- console.log('querying users');
63
+ await req.a("database", async () => {
64
+ console.log("querying users");
65
65
  }); // auto-finished
66
66
 
67
- await req.a('response', async () => {
68
- console.log('sending 200');
67
+ await req.a("response", async () => {
68
+ console.log("sending 200");
69
69
  }); // auto-finished
70
70
  }); // auto-finished
71
71
 
@@ -75,16 +75,16 @@ await root.finish();
75
75
  If your function throws, the action is marked as errored and the exception propagates:
76
76
 
77
77
  ```ts
78
- await root.a('risky-op', async () => {
79
- throw new Error('something broke');
78
+ await root.a("risky-op", async () => {
79
+ throw new Error("something broke");
80
80
  }); // action → error, rethrows
81
81
  ```
82
82
 
83
83
  Return values pass through:
84
84
 
85
85
  ```ts
86
- const users = await flow.a('db-query', async () => {
87
- return await db.query('SELECT * FROM users');
86
+ const users = await flow.a("db-query", async () => {
87
+ return await db.query("SELECT * FROM users");
88
88
  });
89
89
  // users is the query result
90
90
  ```
@@ -92,9 +92,9 @@ const users = await flow.a('db-query', async () => {
92
92
  Duplicate names are allowed — the dashboard shows them as `request:1`, `request:2`, etc:
93
93
 
94
94
  ```ts
95
- await root.a('batch', async (batch) => {
95
+ await root.a("batch", async (batch) => {
96
96
  for (const item of items) {
97
- await batch.a('request', async () => {
97
+ await batch.a("request", async () => {
98
98
  await processItem(item);
99
99
  });
100
100
  }
@@ -104,10 +104,10 @@ await root.a('batch', async (batch) => {
104
104
  For long-lived actions (websockets, streams), use the manual API:
105
105
 
106
106
  ```ts
107
- const stream = root.action('websocket');
108
- stream.info('connected');
107
+ const stream = root.action("websocket");
108
+ stream.info("connected");
109
109
  // ... hours later ...
110
- stream.info('disconnected');
110
+ stream.info("disconnected");
111
111
  await stream.finish();
112
112
  ```
113
113
 
@@ -116,10 +116,10 @@ await stream.finish();
116
116
  Outside of `.a()` callbacks, or when you want to log to a specific action:
117
117
 
118
118
  ```ts
119
- action.info('request received', { method: 'POST', path: '/api/users' });
120
- action.warn('rate limit approaching', { remaining: 3 });
121
- action.error('query failed', new Error('connection refused'));
122
- action.debug('cache hit', { key: 'user:123', ttl: 300 });
119
+ action.info("request received", { method: "POST", path: "/api/users" });
120
+ action.warn("rate limit approaching", { remaining: 3 });
121
+ action.error("query failed", new Error("connection refused"));
122
+ action.debug("cache hit", { key: "user:123", ttl: 300 });
123
123
  ```
124
124
 
125
125
  ## Cross-Context Actions
@@ -128,35 +128,74 @@ Export an action's handle to continue logging from another process, worker, or s
128
128
 
129
129
  ```ts
130
130
  // Service A
131
- const action = root.action('job');
131
+ const action = root.action("job");
132
132
  const handle = await action.exportID();
133
133
  // pass handle to service B via queue, HTTP, etc.
134
134
 
135
135
  // Service B
136
- import { restoreFlow } from '@radishbot/sdk';
136
+ import { restoreFlow } from "@radishbot/sdk";
137
137
  const action = await restoreFlow(key, handle);
138
- action.info('continuing from service B');
138
+ action.info("continuing from service B");
139
139
  await action.finish();
140
140
  ```
141
141
 
142
+ ## Release Tracking
143
+
144
+ Tag every flow with a version or commit SHA. Errors are tracked per-release, and regressions (errors reappearing after being resolved) are detected automatically.
145
+
146
+ ```ts
147
+ const root = await RL(key, { release: "v1.2.3" });
148
+ // or
149
+ const root = await RL(key, { release: process.env.GIT_SHA });
150
+ ```
151
+
152
+ Sub-flows inherit the release from the root automatically.
153
+
154
+ ## Retention & Garbage Collection
155
+
156
+ Each key has a retention period. Flows older than the retention window are automatically deleted (along with their logs and actions) by a GC job that runs every 3 hours.
157
+
158
+ ```ts
159
+ const root = await RL(key, { retention: "30d" }); // default
160
+ const root = await RL(key, { retention: "7d" }); // keep 1 week
161
+ const root = await RL(key, { retention: "90d" }); // keep 3 months
162
+ ```
163
+
164
+ Calling `RL()` with a new retention value updates the stored retention for that key.
165
+
166
+ ## Error Grouping
167
+
168
+ Errors are automatically deduplicated by normalizing the error message (stripping numbers, UUIDs) and combining it with the flow path. This means:
169
+
170
+ - `"User 123 not found"` and `"User 456 not found"` at `/request/db-query` group together
171
+ - Each group tracks: count, first/last seen, latest flow, and release
172
+ - Resolving an error group and seeing it again marks it as **regressed**
173
+ - Groups can be marked as `resolved` or `ignored` from the dashboard
174
+
142
175
  ## Configuration
143
176
 
144
177
  ```ts
145
178
  const root = await RL(key, {
146
- host: 'wss://maincloud.spacetimedb.com', // default
147
- dbName: 'radish-log', // default
148
- defaultTimeout: 100, // seconds, default 100
179
+ host: "wss://maincloud.spacetimedb.com", // default
180
+ dbName: "radish-log", // default
181
+ defaultTimeout: 100, // seconds, default 100
182
+ release: "v1.0.0", // version or commit SHA
183
+ retention: "30d", // data retention period
149
184
  });
150
185
  ```
151
186
 
152
187
  Sub-actions can have their own timeout:
153
188
 
154
189
  ```ts
155
- await root.a('quick-task', async () => {
156
- // ...
157
- }, 10); // 10 second timeout
158
-
159
- const slow = root.action('batch-job', 3600); // 1 hour
190
+ await root.a(
191
+ "quick-task",
192
+ async () => {
193
+ // ...
194
+ },
195
+ 10,
196
+ ); // 10 second timeout
197
+
198
+ const slow = root.action("batch-job", 3600); // 1 hour
160
199
  ```
161
200
 
162
201
  Actions that exceed their timeout are automatically marked as timed out.
@@ -165,7 +204,7 @@ Actions that exceed their timeout are automatically marked as timed out.
165
204
 
166
205
  ### `RL(secretKey, options?) → Promise<Flow>`
167
206
 
168
- Connect and create a root action.
207
+ Connect and create a root action. Options: `host`, `dbName`, `defaultTimeout`, `release`, `retention`.
169
208
 
170
209
  ### `generateKey() → string`
171
210
 
@@ -177,16 +216,16 @@ Restore an action from an exported handle string.
177
216
 
178
217
  ### Flow (Action)
179
218
 
180
- | Method | Description |
181
- |---|---|
182
- | `.a(name, fn, timeout?)` | Run a sub-action. Auto-finish, console capture. Returns fn result. |
183
- | `.action(name, timeout?)` | Create a sub-action manually. |
184
- | `.finish()` | Finish the action. |
185
- | `.finishWithError(err?)` | Finish the action as errored. |
186
- | `.info(msg, data?)` | Log at info level. |
187
- | `.warn(msg, data?)` | Log at warn level. |
188
- | `.error(msg, data?)` | Log at error level. |
189
- | `.debug(msg, data?)` | Log at debug level. |
190
- | `.log(msg, data?, level?)` | Log at any level. |
191
- | `.exportID()` | Export handle for cross-context restore. |
192
- | `.getId()` | Get server-assigned action ID. |
219
+ | Method | Description |
220
+ | -------------------------- | ------------------------------------------------------------------ |
221
+ | `.a(name, fn, timeout?)` | Run a sub-action. Auto-finish, console capture. Returns fn result. |
222
+ | `.action(name, timeout?)` | Create a sub-action manually. |
223
+ | `.finish()` | Finish the action. |
224
+ | `.finishWithError(err?)` | Finish the action as errored. |
225
+ | `.info(msg, data?)` | Log at info level. |
226
+ | `.warn(msg, data?)` | Log at warn level. |
227
+ | `.error(msg, data?)` | Log at error level. |
228
+ | `.debug(msg, data?)` | Log at debug level. |
229
+ | `.log(msg, data?, level?)` | Log at any level. |
230
+ | `.exportID()` | Export handle for cross-context restore. |
231
+ | `.getId()` | Get server-assigned action ID. |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@radishbot/sdk",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
package/src/cli.ts CHANGED
File without changes
package/src/index.ts CHANGED
@@ -1,6 +1,6 @@
1
- import { SdkConnection } from './connection';
1
+ import { SdkConnection } from "./connection";
2
2
 
3
- export type LogLevel = 'info' | 'warn' | 'error' | 'debug';
3
+ export type LogLevel = "info" | "warn" | "error" | "debug";
4
4
 
5
5
  // ── Console capture ──────────────────────────────────────────
6
6
 
@@ -15,26 +15,32 @@ const _origConsole = {
15
15
  const _flowStack: Flow[] = [];
16
16
 
17
17
  function _patchConsole() {
18
- const capture = (level: LogLevel) => (...args: unknown[]) => {
19
- const flow = _flowStack[_flowStack.length - 1];
20
- if (flow) {
21
- const msg = args.map(a => {
22
- if (typeof a === 'string') return a;
23
- if (a instanceof Error) return a.message;
24
- try { return JSON.stringify(a); } catch { return String(a); }
25
- }).join(' ');
26
- const data = args.length <= 1 ? undefined
27
- : args.length === 2 ? args[1]
28
- : args.slice(1);
29
- flow.log(msg, data, level);
30
- }
31
- _origConsole[level === 'info' ? 'log' : level](...args);
32
- };
33
- console.log = capture('info');
34
- console.info = capture('info');
35
- console.warn = capture('warn');
36
- console.error = capture('error');
37
- console.debug = capture('debug');
18
+ const capture =
19
+ (level: LogLevel) =>
20
+ (...args: unknown[]) => {
21
+ const flow = _flowStack[_flowStack.length - 1];
22
+ if (flow) {
23
+ const msg = args
24
+ .map((a) => {
25
+ if (typeof a === "string") return a;
26
+ if (a instanceof Error) return a.message;
27
+ try {
28
+ return JSON.stringify(a);
29
+ } catch {
30
+ return String(a);
31
+ }
32
+ })
33
+ .join(" ");
34
+ const data = args.length <= 1 ? undefined : args.length === 2 ? args[1] : args.slice(1);
35
+ flow.log(msg, data, level);
36
+ }
37
+ _origConsole[level === "info" ? "log" : level](...args);
38
+ };
39
+ console.log = capture("info");
40
+ console.info = capture("info");
41
+ console.warn = capture("warn");
42
+ console.error = capture("error");
43
+ console.debug = capture("debug");
38
44
  }
39
45
 
40
46
  function _restoreConsole() {
@@ -46,7 +52,7 @@ function _restoreConsole() {
46
52
  }
47
53
 
48
54
  function serialize(value: unknown): string {
49
- if (value === undefined || value === null) return '{}';
55
+ if (value === undefined || value === null) return "{}";
50
56
  if (value instanceof Error)
51
57
  return JSON.stringify({
52
58
  name: value.name,
@@ -54,9 +60,7 @@ function serialize(value: unknown): string {
54
60
  stack: value.stack,
55
61
  });
56
62
  try {
57
- return JSON.stringify(value, (_key, v) =>
58
- typeof v === 'bigint' ? v.toString() : v
59
- );
63
+ return JSON.stringify(value, (_key, v) => (typeof v === "bigint" ? v.toString() : v));
60
64
  } catch {
61
65
  return JSON.stringify({ value: String(value) });
62
66
  }
@@ -65,17 +69,17 @@ function serialize(value: unknown): string {
65
69
  async function hashKey(secretKey: string): Promise<string> {
66
70
  const encoder = new TextEncoder();
67
71
  const data = encoder.encode(secretKey);
68
- const hashBuffer = await crypto.subtle.digest('SHA-256', data);
72
+ const hashBuffer = await crypto.subtle.digest("SHA-256", data);
69
73
  const hashArray = Array.from(new Uint8Array(hashBuffer));
70
- return hashArray.map((b) => b.toString(16).padStart(2, '0')).join('');
74
+ return hashArray.map((b) => b.toString(16).padStart(2, "0")).join("");
71
75
  }
72
76
 
73
77
  function generateToken(): string {
74
78
  const bytes = new Uint8Array(16);
75
79
  crypto.getRandomValues(bytes);
76
80
  return Array.from(bytes)
77
- .map((b) => b.toString(16).padStart(2, '0'))
78
- .join('');
81
+ .map((b) => b.toString(16).padStart(2, "0"))
82
+ .join("");
79
83
  }
80
84
 
81
85
  export class Flow {
@@ -92,6 +96,7 @@ export class Flow {
92
96
  private _parentId: bigint;
93
97
  private _name: string;
94
98
  private _timeoutSeconds: bigint;
99
+ private _release: string;
95
100
 
96
101
  /** @internal */
97
102
  constructor(
@@ -99,13 +104,15 @@ export class Flow {
99
104
  keyHash: string,
100
105
  parentId: bigint,
101
106
  name: string,
102
- timeoutSeconds: number
107
+ timeoutSeconds: number,
108
+ release: string = "",
103
109
  ) {
104
110
  this._sdk = sdk;
105
111
  this._keyHash = keyHash;
106
112
  this._parentId = parentId;
107
113
  this._name = name;
108
- this._timeoutSeconds = (!timeoutSeconds || timeoutSeconds === Infinity) ? 0n : BigInt(timeoutSeconds);
114
+ this._timeoutSeconds = !timeoutSeconds || timeoutSeconds === Infinity ? 0n : BigInt(timeoutSeconds);
115
+ this._release = release;
109
116
  this._exportToken = generateToken();
110
117
  this._ready = new Promise<void>((resolve) => {
111
118
  this._resolveReady = resolve;
@@ -121,8 +128,8 @@ export class Flow {
121
128
 
122
129
  if (this._parentId === 0n) {
123
130
  const id = await this._sdk.createFlowAndResolveId(
124
- () => conn.reducers.createRootFlow({ keyHash, timeoutSeconds, exportToken }),
125
- exportToken
131
+ () => conn.reducers.createRootFlow({ keyHash, timeoutSeconds, exportToken, release: this._release }),
132
+ exportToken,
126
133
  );
127
134
  this._id = id;
128
135
  } else {
@@ -137,7 +144,7 @@ export class Flow {
137
144
  timeoutSeconds,
138
145
  exportToken,
139
146
  }),
140
- exportToken
147
+ exportToken,
141
148
  );
142
149
  this._id = id;
143
150
  }
@@ -147,7 +154,7 @@ export class Flow {
147
154
  }
148
155
 
149
156
  /** Log a message with optional data */
150
- log(message: string, data?: unknown, level: LogLevel = 'info'): this {
157
+ log(message: string, data?: unknown, level: LogLevel = "info"): this {
151
158
  if (this._finished) {
152
159
  console.warn(`[radish] Cannot log to finished flow`);
153
160
  return this;
@@ -155,28 +162,28 @@ export class Flow {
155
162
  this._pendingLogs.push({
156
163
  level,
157
164
  message,
158
- data: data !== undefined ? serialize(data) : '{}',
165
+ data: data !== undefined ? serialize(data) : "{}",
159
166
  });
160
167
  this._scheduleFlush();
161
168
  return this;
162
169
  }
163
170
 
164
171
  info(message: string, data?: unknown): this {
165
- return this.log(message, data, 'info');
172
+ return this.log(message, data, "info");
166
173
  }
167
174
  warn(message: string, data?: unknown): this {
168
- return this.log(message, data, 'warn');
175
+ return this.log(message, data, "warn");
169
176
  }
170
177
  error(message: string, data?: unknown): this {
171
- return this.log(message, data, 'error');
178
+ return this.log(message, data, "error");
172
179
  }
173
180
  debug(message: string, data?: unknown): this {
174
- return this.log(message, data, 'debug');
181
+ return this.log(message, data, "debug");
175
182
  }
176
183
 
177
184
  /** Create a sub-action. Returns immediately — creation runs in background. */
178
185
  action(name: string, timeoutSeconds = 100): Flow {
179
- const child = new Flow(this._sdk, this._keyHash, 0n, name, timeoutSeconds);
186
+ const child = new Flow(this._sdk, this._keyHash, 0n, name, timeoutSeconds, this._release);
180
187
  this._ready.then(() => {
181
188
  (child as any)._parentId = this._id!;
182
189
  child._create();
@@ -215,14 +222,16 @@ export class Flow {
215
222
  this._sdk.conn.reducers.finishFlow({
216
223
  keyHash: this._keyHash,
217
224
  flowId: this._id!,
218
- status: 'finished',
225
+ status: "finished",
226
+ errorMessage: "",
219
227
  });
220
228
  }
221
229
 
222
230
  /** Finish this flow with error status */
223
231
  async finishWithError(err?: Error | string): Promise<void> {
232
+ const errorMessage = err ? (typeof err === "string" ? err : err.message) : "";
224
233
  if (err) {
225
- this.error(typeof err === 'string' ? err : err.message, err);
234
+ this.error(errorMessage, err);
226
235
  }
227
236
  await this._ready;
228
237
  this._drain();
@@ -230,7 +239,8 @@ export class Flow {
230
239
  this._sdk.conn.reducers.finishFlow({
231
240
  keyHash: this._keyHash,
232
241
  flowId: this._id!,
233
- status: 'error',
242
+ status: "error",
243
+ errorMessage,
234
244
  });
235
245
  }
236
246
 
@@ -290,6 +300,14 @@ export interface RLOptions {
290
300
  dbName?: string;
291
301
  label?: string;
292
302
  defaultTimeout?: number;
303
+ release?: string;
304
+ retention?: string;
305
+ }
306
+
307
+ function parseRetention(retention: string): number {
308
+ const match = retention.match(/^(\d+)d$/);
309
+ if (!match) throw new Error('Invalid retention format. Use e.g. "30d"');
310
+ return parseInt(match[1], 10);
293
311
  }
294
312
 
295
313
  /**
@@ -315,20 +333,23 @@ export interface RLOptions {
315
333
  */
316
334
  export async function RL(secretKey: string, options: RLOptions = {}): Promise<Flow> {
317
335
  const {
318
- host = 'wss://maincloud.spacetimedb.com',
319
- dbName = 'radish-log',
320
- label = '',
336
+ host = "wss://maincloud.spacetimedb.com",
337
+ dbName = "radish-log",
338
+ label = "",
321
339
  defaultTimeout = 100,
340
+ release = "",
341
+ retention = "30d",
322
342
  } = options;
323
343
 
344
+ const retentionDays = parseRetention(retention);
324
345
  const keyHash = await hashKey(secretKey);
325
346
  const sdk = new SdkConnection(host, dbName);
326
347
  sdk.setKeyHash(keyHash);
327
348
  await sdk.connect();
328
349
 
329
- // Register key (idempotent — server rejects dupes)
350
+ // Register key (idempotent — updates retention if changed)
330
351
  try {
331
- sdk.conn.reducers.registerKey({ keyHash, label });
352
+ sdk.conn.reducers.registerKey({ keyHash, label, retentionDays: BigInt(retentionDays) });
332
353
  } catch {
333
354
  // Already registered
334
355
  }
@@ -341,29 +362,25 @@ export async function RL(secretKey: string, options: RLOptions = {}): Promise<Fl
341
362
  }
342
363
 
343
364
  // Root flow — creation runs in background, logs queue until ready
344
- const root = new Flow(sdk, keyHash, 0n, '/', 0);
365
+ const root = new Flow(sdk, keyHash, 0n, "/", 0, release);
345
366
  root._create(); // fire-and-forget — resolves _ready when server assigns ID
346
367
  return root;
347
368
  }
348
369
 
349
370
  /** Restore a flow from an exported ID string */
350
- export async function restoreFlow(
351
- secretKey: string,
352
- exportedId: string,
353
- options: RLOptions = {}
354
- ): Promise<Flow> {
355
- const { host = 'wss://maincloud.spacetimedb.com', dbName = 'radish-log' } = options;
371
+ export async function restoreFlow(secretKey: string, exportedId: string, options: RLOptions = {}): Promise<Flow> {
372
+ const { host = "wss://maincloud.spacetimedb.com", dbName = "radish-log" } = options;
356
373
 
357
374
  const parsed = JSON.parse(exportedId);
358
375
  const keyHash = await hashKey(secretKey);
359
376
  if (keyHash !== parsed.keyHash) {
360
- throw new Error('Secret key does not match the flow owner');
377
+ throw new Error("Secret key does not match the flow owner");
361
378
  }
362
379
 
363
380
  const sdk = new SdkConnection(host, dbName);
364
381
  await sdk.connect();
365
382
 
366
- const flow = new Flow(sdk, keyHash, 0n, 'restored', 100);
383
+ const flow = new Flow(sdk, keyHash, 0n, "restored", 100);
367
384
  (flow as any)._id = BigInt(parsed.flowId);
368
385
  (flow as any)._exportToken = parsed.exportToken;
369
386
  (flow as any)._resolveReady();
@@ -375,9 +392,9 @@ export function generateKey(): string {
375
392
  const bytes = new Uint8Array(32);
376
393
  crypto.getRandomValues(bytes);
377
394
  return (
378
- 'rl_' +
395
+ "rl_" +
379
396
  Array.from(bytes)
380
- .map((b) => b.toString(16).padStart(2, '0'))
381
- .join('')
397
+ .map((b) => b.toString(16).padStart(2, "0"))
398
+ .join("")
382
399
  );
383
400
  }
@@ -13,5 +13,6 @@ import {
13
13
  export default __t.row({
14
14
  keyHash: __t.string().primaryKey().name("key_hash"),
15
15
  label: __t.string(),
16
+ retentionDays: __t.u64().name("retention_days"),
16
17
  createdAt: __t.timestamp().name("created_at"),
17
18
  });
@@ -14,4 +14,5 @@ export default {
14
14
  keyHash: __t.string(),
15
15
  timeoutSeconds: __t.u64(),
16
16
  exportToken: __t.string(),
17
+ release: __t.string(),
17
18
  };
@@ -0,0 +1,25 @@
1
+ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
2
+ // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
3
+
4
+ /* eslint-disable */
5
+ /* tslint:disable */
6
+ import {
7
+ TypeBuilder as __TypeBuilder,
8
+ t as __t,
9
+ type AlgebraicTypeType as __AlgebraicTypeType,
10
+ type Infer as __Infer,
11
+ } from "spacetimedb";
12
+
13
+ export default __t.row({
14
+ id: __t.u64().primaryKey(),
15
+ keyHash: __t.string().name("key_hash"),
16
+ fingerprint: __t.string(),
17
+ message: __t.string(),
18
+ path: __t.string(),
19
+ release: __t.string(),
20
+ count: __t.u64(),
21
+ status: __t.string(),
22
+ firstSeenAt: __t.timestamp().name("first_seen_at"),
23
+ lastSeenAt: __t.u64().name("last_seen_at"),
24
+ lastFlowId: __t.u64().name("last_flow_id"),
25
+ });
@@ -14,4 +14,5 @@ export default {
14
14
  keyHash: __t.string(),
15
15
  flowId: __t.u64(),
16
16
  status: __t.string(),
17
+ errorMessage: __t.string(),
17
18
  };
@@ -17,6 +17,7 @@ export default __t.row({
17
17
  name: __t.string(),
18
18
  path: __t.string(),
19
19
  status: __t.string(),
20
+ release: __t.string(),
20
21
  timeoutSeconds: __t.u64().name("timeout_seconds"),
21
22
  createdAt: __t.timestamp().name("created_at"),
22
23
  finishedAt: __t.u64().name("finished_at"),
@@ -43,12 +43,14 @@ import FinishActionReducer from "./finish_action_reducer";
43
43
  import FinishFlowReducer from "./finish_flow_reducer";
44
44
  import RegisterKeyReducer from "./register_key_reducer";
45
45
  import StartActionReducer from "./start_action_reducer";
46
+ import UpdateErrorGroupStatusReducer from "./update_error_group_status_reducer";
46
47
 
47
48
  // Import all procedure arg schemas
48
49
 
49
50
  // Import all table schema definitions
50
51
  import ActionRow from "./action_table";
51
52
  import ApiKeyRow from "./api_key_table";
53
+ import ErrorGroupRow from "./error_group_table";
52
54
  import FlowRow from "./flow_table";
53
55
  import LogEntryRow from "./log_entry_table";
54
56
 
@@ -78,6 +80,21 @@ const tablesSchema = __schema({
78
80
  { name: 'api_key_key_hash_key', constraint: 'unique', columns: ['keyHash'] },
79
81
  ],
80
82
  }, ApiKeyRow),
83
+ errorGroup: __table({
84
+ name: 'error_group',
85
+ indexes: [
86
+ { name: 'fingerprint', algorithm: 'btree', columns: [
87
+ 'fingerprint',
88
+ ] },
89
+ { name: 'id', algorithm: 'btree', columns: [
90
+ 'id',
91
+ ] },
92
+ ],
93
+ constraints: [
94
+ { name: 'error_group_fingerprint_key', constraint: 'unique', columns: ['fingerprint'] },
95
+ { name: 'error_group_id_key', constraint: 'unique', columns: ['id'] },
96
+ ],
97
+ }, ErrorGroupRow),
81
98
  flow: __table({
82
99
  name: 'flow',
83
100
  indexes: [
@@ -117,6 +134,7 @@ const reducersSchema = __reducers(
117
134
  __reducerSchema("finish_flow", FinishFlowReducer),
118
135
  __reducerSchema("register_key", RegisterKeyReducer),
119
136
  __reducerSchema("start_action", StartActionReducer),
137
+ __reducerSchema("update_error_group_status", UpdateErrorGroupStatusReducer),
120
138
  );
121
139
 
122
140
  /** The schema information for all procedures in this module. This is defined the same way as the procedures would have been defined in the server. */
@@ -13,4 +13,5 @@ import {
13
13
  export default {
14
14
  keyHash: __t.string(),
15
15
  label: __t.string(),
16
+ retentionDays: __t.u64(),
16
17
  };
@@ -15,6 +15,7 @@ import FinishActionReducer from "../finish_action_reducer";
15
15
  import FinishFlowReducer from "../finish_flow_reducer";
16
16
  import RegisterKeyReducer from "../register_key_reducer";
17
17
  import StartActionReducer from "../start_action_reducer";
18
+ import UpdateErrorGroupStatusReducer from "../update_error_group_status_reducer";
18
19
 
19
20
  export type AddLogParams = __Infer<typeof AddLogReducer>;
20
21
  export type AddLogsBatchParams = __Infer<typeof AddLogsBatchReducer>;
@@ -25,4 +26,5 @@ export type FinishActionParams = __Infer<typeof FinishActionReducer>;
25
26
  export type FinishFlowParams = __Infer<typeof FinishFlowReducer>;
26
27
  export type RegisterKeyParams = __Infer<typeof RegisterKeyReducer>;
27
28
  export type StartActionParams = __Infer<typeof StartActionReducer>;
29
+ export type UpdateErrorGroupStatusParams = __Infer<typeof UpdateErrorGroupStatusReducer>;
28
30
 
@@ -23,10 +23,26 @@ export type Action = __Infer<typeof Action>;
23
23
  export const ApiKey = __t.object("ApiKey", {
24
24
  keyHash: __t.string(),
25
25
  label: __t.string(),
26
+ retentionDays: __t.u64(),
26
27
  createdAt: __t.timestamp(),
27
28
  });
28
29
  export type ApiKey = __Infer<typeof ApiKey>;
29
30
 
31
+ export const ErrorGroup = __t.object("ErrorGroup", {
32
+ id: __t.u64(),
33
+ keyHash: __t.string(),
34
+ fingerprint: __t.string(),
35
+ message: __t.string(),
36
+ path: __t.string(),
37
+ release: __t.string(),
38
+ count: __t.u64(),
39
+ status: __t.string(),
40
+ firstSeenAt: __t.timestamp(),
41
+ lastSeenAt: __t.u64(),
42
+ lastFlowId: __t.u64(),
43
+ });
44
+ export type ErrorGroup = __Infer<typeof ErrorGroup>;
45
+
30
46
  export const Flow = __t.object("Flow", {
31
47
  id: __t.u64(),
32
48
  keyHash: __t.string(),
@@ -34,6 +50,7 @@ export const Flow = __t.object("Flow", {
34
50
  name: __t.string(),
35
51
  path: __t.string(),
36
52
  status: __t.string(),
53
+ release: __t.string(),
37
54
  timeoutSeconds: __t.u64(),
38
55
  createdAt: __t.timestamp(),
39
56
  finishedAt: __t.u64(),
@@ -41,6 +58,12 @@ export const Flow = __t.object("Flow", {
41
58
  });
42
59
  export type Flow = __Infer<typeof Flow>;
43
60
 
61
+ export const GcJob = __t.object("GcJob", {
62
+ scheduledId: __t.u64(),
63
+ scheduledAt: __t.scheduleAt(),
64
+ });
65
+ export type GcJob = __Infer<typeof GcJob>;
66
+
44
67
  export const LogEntry = __t.object("LogEntry", {
45
68
  id: __t.u64(),
46
69
  flowId: __t.u64(),
@@ -0,0 +1,17 @@
1
+ // THIS FILE IS AUTOMATICALLY GENERATED BY SPACETIMEDB. EDITS TO THIS FILE
2
+ // WILL NOT BE SAVED. MODIFY TABLES IN YOUR MODULE SOURCE CODE INSTEAD.
3
+
4
+ /* eslint-disable */
5
+ /* tslint:disable */
6
+ import {
7
+ TypeBuilder as __TypeBuilder,
8
+ t as __t,
9
+ type AlgebraicTypeType as __AlgebraicTypeType,
10
+ type Infer as __Infer,
11
+ } from "spacetimedb";
12
+
13
+ export default {
14
+ keyHash: __t.string(),
15
+ errorGroupId: __t.u64(),
16
+ status: __t.string(),
17
+ };