@radishbot/sdk 0.2.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.
Files changed (2) hide show
  1. package/README.md +96 -57
  2. package/package.json +1 -1
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.2.0",
3
+ "version": "0.2.1",
4
4
  "type": "module",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",