Skip to content

Commit ea66aae

Browse files
committed
Add io.random
1 parent 67b5365 commit ea66aae

7 files changed

Lines changed: 189 additions & 1 deletion

File tree

docs/_snippets/random-example.mdx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
```typescript Random example
2+
client.defineJob({
3+
id: "random-job",
4+
name: "Random Job",
5+
version: "0.0.1",
6+
trigger: eventTrigger({
7+
name: "example.event",
8+
}),
9+
run: async (payload, io, ctx) => {
10+
// generate random numbers
11+
const small = await io.random("random-small");
12+
const large = await io.random("random-large", {
13+
min: 10,
14+
max: 200,
15+
round: true
16+
});
17+
18+
await io.logger.info(`${small} is smaller than ${large}`);
19+
},
20+
});
21+
```

docs/mint.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -334,6 +334,7 @@
334334
"sdk/io/logger",
335335
"sdk/io/sendevent",
336336
"sdk/io/backgroundfetch",
337+
"sdk/io/random",
337338
"sdk/io/try",
338339
"sdk/io/registerinterval",
339340
"sdk/io/unregisterinterval",

docs/sdk/io/overview.mdx

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ If you want to send an event from outside a run (e.g. just from your backend) yo
4040

4141
`io.backgroundFetch()` allows you to fetch data from a URL that can take longer that the serverless timeout. The actual `fetch` request is performed on the Trigger.dev platform, and the response is sent back to you. An example use case is fetching data from a slow API, like some AI endpoints.
4242

43+
### [random()](/sdk/io/random)
44+
45+
`io.random()` is identical to `Math.random()` when called without options but ensures your random numbers are not regenerated on resume or retry. It will return a pseudo-random floating-point number between optional `min` (default: 0, inclusive) and `max` (default: 1, exclusive). Can optionally `round` to the nearest integer.
46+
4347
### [try()](/sdk/io/try)
4448

4549
`io.try()` allows you to run Tasks and catch any errors that are thrown, it's similar to a normal `try/catch` block but works with [io.runTask()](/sdk/io/runtask).

docs/sdk/io/random.mdx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
---
2+
title: "io.random()"
3+
sidebarTitle: "random()"
4+
description: "`io.random()` is identical to `Math.random()` when called without options but ensures your random numbers are not regenerated on resume or retry. It will return a pseudo-random floating-point number between optional `min` (default: 0, inclusive) and `max` (default: 1, exclusive). Can optionally `round` to the nearest integer."
5+
---
6+
7+
## Parameters
8+
9+
<Snippet file="stable-key-param.mdx" />
10+
<ResponseField name="min" type="number" default="0" required>
11+
Sets the lower bound (inclusive). Can't be higher than `max`.
12+
</ResponseField>
13+
<ResponseField name="max" type="number" default="1" required>
14+
Sets the upper bound (exclusive). Can't be lower than `min`.
15+
</ResponseField>
16+
<ResponseField name="round" type="boolean" default="false" required>
17+
Controls rounding to the nearest integer. Any `max` integer will become inclusive when enabled. Rounding with floating-point bounds may cause unexpected skew and boundary inclusivity.
18+
</ResponseField>
19+
20+
## Returns
21+
22+
A `Promise` that resolves with a pseudo-random number. Always resolves to an integer when rounding is enabled.
23+
24+
<RequestExample>
25+
<Snippet file="random-example.mdx" />
26+
</RequestExample>

packages/trigger-sdk/src/io.ts

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,6 +212,90 @@ export class IO {
212212
});
213213
}
214214

215+
/** `io.random()` is identical to `Math.random()` when called without options but ensures your random numbers are not regenerated on resume or retry. It will return a pseudo-random floating-point number between optional `min` (default: 0, inclusive) and `max` (default: 1, exclusive). Can optionally `round` to the nearest integer.
216+
* @param cacheKey Should be a stable and unique key inside the `run()`. See [resumability](https://trigger.dev/docs/documentation/concepts/resumability) for more information.
217+
* @param min Sets the lower bound (inclusive). Can't be higher than `max`.
218+
* @param max Sets the upper bound (exclusive). Can't be lower than `min`.
219+
* @param round Controls rounding to the nearest integer. Any `max` integer will become inclusive when enabled. Rounding with floating-point bounds may cause unexpected skew and boundary inclusivity.
220+
*/
221+
async random(
222+
cacheKey: string | any[],
223+
{
224+
min = 0,
225+
max = 1,
226+
round = false,
227+
}: {
228+
min?: number;
229+
max?: number;
230+
round?: boolean;
231+
} = {}
232+
) {
233+
return await this.runTask(
234+
cacheKey,
235+
async (task) => {
236+
if (min > max) {
237+
throw new Error(
238+
`Lower bound can't be higher than upper bound - min: ${min}, max: ${max}`
239+
);
240+
}
241+
242+
if (min === max) {
243+
await this.logger.warn(
244+
`Lower and upper bounds are identical. The return value is not random and will always be: ${min}`
245+
);
246+
}
247+
248+
const withinBounds = (max - min) * Math.random() + min;
249+
250+
if (!round) {
251+
return withinBounds;
252+
}
253+
254+
if (!Number.isInteger(min) || !Number.isInteger(max)) {
255+
await this.logger.warn(
256+
"Rounding enabled with floating-point bounds. This may cause unexpected skew and boundary inclusivity."
257+
);
258+
}
259+
260+
const rounded = Math.round(withinBounds);
261+
262+
return rounded;
263+
},
264+
{
265+
name: "random",
266+
icon: "dice-5-filled",
267+
params: { min, max, round },
268+
properties: [
269+
...(min === 0
270+
? []
271+
: [
272+
{
273+
label: "min",
274+
text: String(min),
275+
},
276+
]),
277+
...(max === 1
278+
? []
279+
: [
280+
{
281+
label: "max",
282+
text: String(max),
283+
},
284+
]),
285+
...(round === false
286+
? []
287+
: [
288+
{
289+
label: "round",
290+
text: String(round),
291+
},
292+
]),
293+
],
294+
style: { style: "minimal" },
295+
}
296+
);
297+
}
298+
215299
/** `io.wait()` waits for the specified amount of time before continuing the Job. Delays work even if you're on a serverless platform with timeouts, or if your server goes down. They utilize [resumability](https://trigger.dev/docs/documentation/concepts/resumability) to ensure that the Run can be resumed after the delay.
216300
* @param cacheKey Should be a stable and unique key inside the `run()`. See [resumability](https://trigger.dev/docs/documentation/concepts/resumability) for more information.
217301
* @param seconds The number of seconds to wait. This can be very long, serverless timeouts are not an issue.
@@ -877,7 +961,7 @@ export class IO {
877961
*/
878962
brb = this.yield.bind(this);
879963

880-
/** `io.try()` allows you to run Tasks and catch any errors that are thrown, it's similar to a normal `try/catch` block but works with [io.runTask()](/sdk/io/runtask).
964+
/** `io.try()` allows you to run Tasks and catch any errors that are thrown, it's similar to a normal `try/catch` block but works with [io.runTask()](https://trigger.dev/docs/sdk/io/runtask).
881965
* A regular `try/catch` block on its own won't work as expected with Tasks. Internally `runTask()` throws some special errors to control flow execution. This is necessary to deal with resumability, serverless timeouts, and retrying Tasks.
882966
* @param tryCallback The code you wish to run
883967
* @param catchCallback Thhis will be called if the Task fails. The callback receives the error

references/job-catalog/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
"misconfigured": "nodemon --watch src/misconfigured.ts -r tsconfig-paths/register -r dotenv/config src/misconfigured.ts",
3030
"auto-yield": "nodemon --watch src/auto-yield.ts -r tsconfig-paths/register -r dotenv/config src/auto-yield.ts",
3131
"cli-example": "nodemon --watch src/cli-example.ts -r tsconfig-paths/register -r dotenv/config src/cli-example.ts",
32+
"random": "nodemon --watch src/random.ts -r tsconfig-paths/register -r dotenv/config src/random.ts",
3233
"dev:trigger": "trigger-cli dev --port 8080"
3334
},
3435
"dependencies": {
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import { createExpressServer } from "@trigger.dev/express";
2+
import { TriggerClient, eventTrigger } from "@trigger.dev/sdk";
3+
4+
export const client = new TriggerClient({
5+
id: "job-catalog",
6+
apiKey: process.env["TRIGGER_API_KEY"],
7+
apiUrl: process.env["TRIGGER_API_URL"],
8+
verbose: false,
9+
ioLogLocalEnabled: true,
10+
});
11+
12+
client.defineJob({
13+
id: "random-example",
14+
name: "Random Example",
15+
version: "1.0.0",
16+
enabled: true,
17+
trigger: eventTrigger({
18+
name: "random.example",
19+
}),
20+
run: async (payload, io, ctx) => {
21+
// just like Math.random() but wrapped in a Task
22+
await io.random("random-native");
23+
24+
// set lower and upper bounds - defaults to 0, 1 respectively
25+
await io.random("random-min-max", { min: 10, max: 20 });
26+
27+
// set lower bound only (inclusive)
28+
await io.random("random-min", { min: 0.5 });
29+
30+
// set upper bound only (exclusive)
31+
await io.random("random-max", { max: 100 });
32+
33+
// round to the nearest integer
34+
await io.random("random-round", { min: 100, max: 1000, round: true });
35+
36+
// rounding with floating-point bounds results in a warning
37+
// this example will unexpectedly (but correctly!) output 1 or 2, skewing towards 2
38+
await io.random("random-round-float", { min: 0.9, max: 2.5, round: true });
39+
40+
// negative values work just fine
41+
await io.random("random-negative", { min: -100, max: -50 });
42+
43+
// identical lower and upper bounds result in a warning
44+
await io.random("random-warn-bounds", { min: 10, max: 10 });
45+
46+
// invalid ranges will fail
47+
await io.random("random-error", { min: 10, max: 5 });
48+
},
49+
});
50+
51+
createExpressServer(client);

0 commit comments

Comments
 (0)