Skip to content

Commit 092ff2a

Browse files
authored
Type safe async jobs (#1400)
1 parent 0183d9c commit 092ff2a

File tree

89 files changed

+1434
-2196
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

89 files changed

+1434
-2196
lines changed

waspc/ChangeLog.md

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,50 @@
11
# Changelog
22

3+
## 0.11.4
4+
5+
### 🎉 [New Feature] Added Typescript support for Jobs
6+
7+
Now you can type your async jobs better and receive all the benefits of Typescript. When you define a job, Wasp will generate a generic type which you can use to type your job function:
8+
9+
```wasp
10+
job simplePrintJob {
11+
executor: PgBoss,
12+
perform: {
13+
fn: import { simplePrint } from "@server/jobs.js",
14+
},
15+
entities: [Task]
16+
}
17+
```
18+
19+
```typescript
20+
import { SimplePrintJob } from "@wasp/jobs/simplePrintJob";
21+
import { Task } from "@wasp/entities";
22+
23+
export const simplePrint: SimplePrintJob<
24+
{ name: string },
25+
{ tasks: Task[] }
26+
> = async (args, context) => {
27+
// 👆 args are typed e.g. { name: string }
28+
// 👆 context is typed e.g. { entitites: { Task: ... } }
29+
const tasks = await context.entities.Task.findMany({});
30+
return {
31+
tasks,
32+
};
33+
};
34+
```
35+
36+
When you use the job, you can pass the arguments and receive the result with the correct types:
37+
38+
```typescript
39+
import { simplePrintJob } from "@wasp/jobs/simplePrintJob.js";
40+
41+
...
42+
const job = await simplePrintJob.submit({ name: "John" })
43+
...
44+
const result = await result.pgBoss.details()
45+
// 👆 result is typed e.g. { tasks: Task[] }
46+
```
47+
348
## 0.11.3
449

550
### 🎉 [New Feature] Type-safe links
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
// Used for internal Wasp development only, not copied to generated app.
2+
module.exports = {
3+
trailingComma: 'es5',
4+
tabWidth: 2,
5+
semi: false,
6+
singleQuote: true,
7+
}

waspc/data/Generator/templates/server/src/_types/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ type EntityMap<Entities extends _Entity[]> = {
6666
[EntityName in Entities[number]["_entityName"]]: PrismaDelegate[EntityName]
6767
}
6868

69-
type PrismaDelegate = {
69+
export type PrismaDelegate = {
7070
{=# entities =}
7171
"{= name =}": typeof prisma.{= prismaIdentifier =},
7272
{=/ entities =}
Lines changed: 25 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,43 @@
1-
export type Payload = void | SuperJSONValue;
1+
export type Payload = void | SuperJSONValue
22

33
// The part below was copied from SuperJSON and slightly modified:
44
// https://github.com/blitz-js/superjson/blob/ae7dbcefe5d3ece5b04be0c6afe6b40f3a44a22a/src/types.ts
55
//
66
// We couldn't use SuperJSON's types directly because:
77
// 1. They aren't exported publicly.
8-
// 2. They have a werid quirk that turns `SuperJSONValue` into `any`.
9-
// See why here:
8+
// 2. They have a werid quirk that turns `SuperJSONValue` into `any`.
9+
// See why here:
1010
// https://github.com/blitz-js/superjson/pull/36#issuecomment-669239876
1111
//
1212
// We changed the code as little as possible to make future comparisons easier.
13+
export type JSONValue = PrimitiveJSONValue | JSONArray | JSONObject
1314

14-
type PrimitiveJSONValue = string | number | boolean | undefined | null;
15-
16-
type JSONValue = PrimitiveJSONValue | JSONArray | JSONObject;
17-
18-
interface JSONArray extends Array<JSONValue> {
15+
export interface JSONObject {
16+
[key: string]: JSONValue
1917
}
2018

21-
interface JSONObject {
22-
[key: string]: JSONValue;
23-
}
19+
type PrimitiveJSONValue = string | number | boolean | undefined | null
2420

25-
type SerializableJSONValue = Symbol | Set<SuperJSONValue> | Map<SuperJSONValue, SuperJSONValue> | undefined | bigint | Date | RegExp;
21+
interface JSONArray extends Array<JSONValue> {}
2622

27-
// Here's where we excluded `ClassInstance` (which was `any`) from the union.
28-
type SuperJSONValue = JSONValue | SerializableJSONValue | SuperJSONArray | SuperJSONObject;
23+
type SerializableJSONValue =
24+
| Symbol
25+
| Set<SuperJSONValue>
26+
| Map<SuperJSONValue, SuperJSONValue>
27+
| undefined
28+
| bigint
29+
| Date
30+
| RegExp
2931

30-
interface SuperJSONArray extends Array<SuperJSONValue> {
31-
}
32+
// Here's where we excluded `ClassInstance` (which was `any`) from the union.
33+
type SuperJSONValue =
34+
| JSONValue
35+
| SerializableJSONValue
36+
| SuperJSONArray
37+
| SuperJSONObject
38+
39+
interface SuperJSONArray extends Array<SuperJSONValue> {}
3240

3341
interface SuperJSONObject {
34-
[key: string]: SuperJSONValue;
42+
[key: string]: SuperJSONValue
3543
}

waspc/data/Generator/templates/server/src/jobs/_job.js

Lines changed: 0 additions & 16 deletions
This file was deleted.
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
{{={= =}=}}
2+
import prisma from '../dbClient.js'
3+
import type { JSONValue, JSONObject } from '../_types/serialization.js'
4+
import { createJob, type JobFn } from './{= jobExecutorRelativePath =}'
5+
{=& jobPerformFnImportStatement =}
6+
7+
const entities = {
8+
{=# entities =}
9+
{= name =}: prisma.{= prismaIdentifier =},
10+
{=/ entities =}
11+
};
12+
13+
export type {= typeName =}<Input extends JSONObject, Output extends JSONValue | void> = JobFn<Input, Output, typeof entities>
14+
15+
export const {= jobName =} = createJob({
16+
jobName: "{= jobName =}",
17+
jobFn: {= jobPerformFnName =},
18+
defaultJobOptions: {=& jobPerformOptions =},
19+
jobSchedule: {=& jobSchedule =},
20+
entities,
21+
})

waspc/data/Generator/templates/server/src/jobs/core/Job.js

Lines changed: 0 additions & 36 deletions
This file was deleted.

waspc/data/Generator/templates/server/src/jobs/core/SubmittedJob.js

Lines changed: 0 additions & 29 deletions
This file was deleted.
File renamed without changes.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/**
2+
* This is a definition of a job (think draft or invocable computation), not the running instance itself.
3+
* This can be submitted one or more times to be executed by some job executor via the same instance.
4+
* Once submitted, you get a SubmittedJob to track it later.
5+
*/
6+
export class Job {
7+
public readonly jobName: string
8+
public readonly executorName: string | symbol
9+
10+
constructor(jobName: string, executorName: string | symbol) {
11+
this.jobName = jobName
12+
this.executorName = executorName
13+
}
14+
}
15+
16+
/**
17+
* This is the result of submitting a Job to some executor.
18+
* It can be used by callers to track things, or call executor-specific subclass functionality.
19+
*/
20+
export class SubmittedJob {
21+
public readonly job: Job
22+
public readonly jobId: string
23+
24+
constructor(job: Job, jobId: string) {
25+
this.job = job
26+
this.jobId = jobId
27+
}
28+
}

0 commit comments

Comments
 (0)