@tailor-platform/sdk 0.16.3 → 0.17.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/CHANGELOG.md +17 -0
- package/README.md +75 -8
- package/dist/cli/api.d.mts +5 -1
- package/dist/cli/api.mjs +1 -1
- package/dist/cli/index.mjs +51 -75
- package/dist/cli/index.mjs.map +1 -1
- package/dist/{resume-kyHIaNvK.mjs → resume-B2ba5opn.mjs} +141 -103
- package/dist/{resume-kyHIaNvK.mjs.map → resume-B2ba5opn.mjs.map} +1 -1
- package/dist/utils/test/index.d.mts +8 -2
- package/dist/utils/test/index.mjs +8 -6
- package/dist/utils/test/index.mjs.map +1 -1
- package/docs/cli/application.md +136 -0
- package/docs/cli/auth.md +110 -0
- package/docs/cli/secret.md +125 -0
- package/docs/cli/user.md +183 -0
- package/docs/cli/workflow.md +144 -0
- package/docs/cli/workspace.md +122 -0
- package/docs/cli-reference.md +80 -801
- package/docs/configuration.md +36 -32
- package/docs/generator/builtin.md +194 -0
- package/docs/generator/custom.md +150 -0
- package/docs/generator/index.md +56 -0
- package/docs/quickstart.md +9 -4
- package/docs/services/auth.md +225 -0
- package/docs/services/executor.md +304 -0
- package/docs/services/idp.md +106 -0
- package/docs/services/resolver.md +213 -0
- package/docs/services/secret.md +116 -0
- package/docs/services/staticwebsite.md +132 -0
- package/docs/services/tailordb.md +305 -0
- package/docs/services/workflow.md +176 -0
- package/docs/testing.md +3 -1
- package/package.json +4 -3
- package/docs/core-concepts.md +0 -609
package/docs/core-concepts.md
DELETED
|
@@ -1,609 +0,0 @@
|
|
|
1
|
-
# Conceptual Guides
|
|
2
|
-
|
|
3
|
-
### TailorDB Concepts
|
|
4
|
-
|
|
5
|
-
Define TailorDB Types in files matching glob patterns specified in `tailor.config.ts`.
|
|
6
|
-
|
|
7
|
-
#### Field Types
|
|
8
|
-
|
|
9
|
-
Define TailorDB Fields using methods like `db.string()`, `db.int()`, etc. All TailorDB Field types are supported:
|
|
10
|
-
|
|
11
|
-
| Method | TailorDB | TypeScript |
|
|
12
|
-
| --------------- | -------- | -------------- |
|
|
13
|
-
| `db.string()` | String | string |
|
|
14
|
-
| `db.int()` | Integer | number |
|
|
15
|
-
| `db.float()` | Float | number |
|
|
16
|
-
| `db.bool()` | Boolean | boolean |
|
|
17
|
-
| `db.date()` | Date | string |
|
|
18
|
-
| `db.datetime()` | DateTime | string \| Date |
|
|
19
|
-
| `db.time()` | Time | string |
|
|
20
|
-
| `db.uuid()` | UUID | string |
|
|
21
|
-
| `db.enum()` | Enum | string |
|
|
22
|
-
| `db.object()` | Nested | object |
|
|
23
|
-
|
|
24
|
-
**Enum fields** - specify allowed values as arguments:
|
|
25
|
-
|
|
26
|
-
```typescript
|
|
27
|
-
db.enum("red", "green", "blue");
|
|
28
|
-
db.enum(
|
|
29
|
-
{ value: "active", description: "Active status" },
|
|
30
|
-
{ value: "inactive", description: "Inactive status" },
|
|
31
|
-
);
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
**Object fields** - specify field structure as an argument:
|
|
35
|
-
|
|
36
|
-
```typescript
|
|
37
|
-
db.object({
|
|
38
|
-
street: db.string(),
|
|
39
|
-
city: db.string(),
|
|
40
|
-
country: db.string(),
|
|
41
|
-
});
|
|
42
|
-
```
|
|
43
|
-
|
|
44
|
-
#### Optional and Array Fields
|
|
45
|
-
|
|
46
|
-
Make fields optional or arrays by specifying options:
|
|
47
|
-
|
|
48
|
-
```typescript
|
|
49
|
-
db.string({ optional: true });
|
|
50
|
-
db.string({ array: true });
|
|
51
|
-
db.string({ optional: true, array: true });
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
#### Field Modifiers
|
|
55
|
-
|
|
56
|
-
**Description** - Add a description to field:
|
|
57
|
-
|
|
58
|
-
```typescript
|
|
59
|
-
db.string().description("User's full name");
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
**Index / Unique** - Add an index to field:
|
|
63
|
-
|
|
64
|
-
```typescript
|
|
65
|
-
db.string().index();
|
|
66
|
-
db.string().unique();
|
|
67
|
-
```
|
|
68
|
-
|
|
69
|
-
**Relations** - Add a relation to field with automatic index and foreign key constraint:
|
|
70
|
-
|
|
71
|
-
```typescript
|
|
72
|
-
const role = db.type("Role", {
|
|
73
|
-
name: db.string(),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
const user = db.type("User", {
|
|
77
|
-
name: db.string(),
|
|
78
|
-
roleId: db.uuid().relation({
|
|
79
|
-
type: "n-1",
|
|
80
|
-
toward: { type: role },
|
|
81
|
-
}),
|
|
82
|
-
});
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
For one-to-one relations, use `type: "1-1"`:
|
|
86
|
-
|
|
87
|
-
```typescript
|
|
88
|
-
const userProfile = db.type("UserProfile", {
|
|
89
|
-
userId: db.uuid().relation({
|
|
90
|
-
type: "1-1",
|
|
91
|
-
toward: { type: user },
|
|
92
|
-
}),
|
|
93
|
-
bio: db.string(),
|
|
94
|
-
});
|
|
95
|
-
```
|
|
96
|
-
|
|
97
|
-
For foreign key constraint without creating a relation, use `type: "keyOnly"`:
|
|
98
|
-
|
|
99
|
-
```typescript
|
|
100
|
-
const user = db.type("User", {
|
|
101
|
-
roleId: db.uuid().relation({
|
|
102
|
-
type: "keyOnly",
|
|
103
|
-
toward: { type: role },
|
|
104
|
-
}),
|
|
105
|
-
});
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
Create relations against different fields using `toward.key`:
|
|
109
|
-
|
|
110
|
-
```typescript
|
|
111
|
-
const user = db.type("User", {
|
|
112
|
-
email: db.string().unique(),
|
|
113
|
-
});
|
|
114
|
-
|
|
115
|
-
const userProfile = db.type("UserProfile", {
|
|
116
|
-
userEmail: db.string().relation({
|
|
117
|
-
type: "1-1",
|
|
118
|
-
toward: { type: user, key: "email" },
|
|
119
|
-
}),
|
|
120
|
-
});
|
|
121
|
-
```
|
|
122
|
-
|
|
123
|
-
Customize relation names using `toward.as` / `backward` options:
|
|
124
|
-
|
|
125
|
-
```typescript
|
|
126
|
-
const userProfile = db.type("UserProfile", {
|
|
127
|
-
userId: db.uuid().relation({
|
|
128
|
-
type: "1-1",
|
|
129
|
-
toward: { type: user, as: "base" },
|
|
130
|
-
backward: "profile",
|
|
131
|
-
}),
|
|
132
|
-
});
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
**Hooks** - Add hooks to execute functions during data creation or update:
|
|
136
|
-
|
|
137
|
-
```typescript
|
|
138
|
-
db.datetime().hooks({
|
|
139
|
-
create: () => new Date(),
|
|
140
|
-
update: () => new Date(),
|
|
141
|
-
});
|
|
142
|
-
```
|
|
143
|
-
|
|
144
|
-
Function arguments include: `value` (field value), `data` (entire record value), `user` (user performing the operation).
|
|
145
|
-
|
|
146
|
-
**Validation** - Add validation functions to field:
|
|
147
|
-
|
|
148
|
-
```typescript
|
|
149
|
-
db.string().validate(
|
|
150
|
-
({ value }) => value.length > 5,
|
|
151
|
-
[
|
|
152
|
-
({ value }) => value.length < 10,
|
|
153
|
-
"Value must be shorter than 10 characters",
|
|
154
|
-
],
|
|
155
|
-
);
|
|
156
|
-
```
|
|
157
|
-
|
|
158
|
-
**Vector Search** - Enable field for vector search:
|
|
159
|
-
|
|
160
|
-
```typescript
|
|
161
|
-
db.string().vector();
|
|
162
|
-
```
|
|
163
|
-
|
|
164
|
-
**Serial / Auto-increment** - Enable field auto-increment:
|
|
165
|
-
|
|
166
|
-
```typescript
|
|
167
|
-
db.int().serial({
|
|
168
|
-
start: 0,
|
|
169
|
-
maxValue: 100,
|
|
170
|
-
});
|
|
171
|
-
|
|
172
|
-
db.string().serial({
|
|
173
|
-
start: 0,
|
|
174
|
-
format: "CUST_%d",
|
|
175
|
-
});
|
|
176
|
-
```
|
|
177
|
-
|
|
178
|
-
**Common Fields** - Add common fields using built-in helpers:
|
|
179
|
-
|
|
180
|
-
```typescript
|
|
181
|
-
export const user = db.type("User", {
|
|
182
|
-
name: db.string(),
|
|
183
|
-
...db.fields.timestamps(),
|
|
184
|
-
});
|
|
185
|
-
```
|
|
186
|
-
|
|
187
|
-
#### Type Definition
|
|
188
|
-
|
|
189
|
-
Define a TailorDB Type using `db.type()` method:
|
|
190
|
-
|
|
191
|
-
```typescript
|
|
192
|
-
db.type("User", {
|
|
193
|
-
name: db.string(),
|
|
194
|
-
});
|
|
195
|
-
```
|
|
196
|
-
|
|
197
|
-
Specify PluralForm by passing an array as first argument:
|
|
198
|
-
|
|
199
|
-
```typescript
|
|
200
|
-
db.type(["User", "UserList"], {
|
|
201
|
-
name: db.string(),
|
|
202
|
-
});
|
|
203
|
-
```
|
|
204
|
-
|
|
205
|
-
Pass a description as second argument:
|
|
206
|
-
|
|
207
|
-
```typescript
|
|
208
|
-
db.type("User", "User in the system", {
|
|
209
|
-
name: db.string(),
|
|
210
|
-
description: db.string().optional(),
|
|
211
|
-
});
|
|
212
|
-
```
|
|
213
|
-
|
|
214
|
-
**Composite Indexes** - Configure composite indexes:
|
|
215
|
-
|
|
216
|
-
```typescript
|
|
217
|
-
db.type("User", {
|
|
218
|
-
firstName: db.string(),
|
|
219
|
-
lastName: db.string(),
|
|
220
|
-
}).indexes({
|
|
221
|
-
fields: ["firstName", "lastName"],
|
|
222
|
-
unique: true,
|
|
223
|
-
name: "user_name_idx",
|
|
224
|
-
});
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
**File Fields** - Add file fields:
|
|
228
|
-
|
|
229
|
-
```typescript
|
|
230
|
-
db.type("User", {
|
|
231
|
-
name: db.string(),
|
|
232
|
-
}).files({
|
|
233
|
-
avatar: "profile image",
|
|
234
|
-
});
|
|
235
|
-
```
|
|
236
|
-
|
|
237
|
-
**Features** - Enable additional features:
|
|
238
|
-
|
|
239
|
-
```typescript
|
|
240
|
-
db.type("User", {
|
|
241
|
-
name: db.string(),
|
|
242
|
-
}).features({
|
|
243
|
-
aggregation: true,
|
|
244
|
-
bulkUpsert: true,
|
|
245
|
-
});
|
|
246
|
-
```
|
|
247
|
-
|
|
248
|
-
**Permissions** - Configure Permission and GQLPermission. For details, see the [TailorDB Permission documentation](https://docs.tailor.tech/guides/tailordb/permission).
|
|
249
|
-
|
|
250
|
-
```typescript
|
|
251
|
-
db.type("User", {
|
|
252
|
-
name: db.string(),
|
|
253
|
-
role: db.enum("admin", "user").index(),
|
|
254
|
-
})
|
|
255
|
-
.permission({
|
|
256
|
-
create: [[{ user: "role" }, "=", "admin"]],
|
|
257
|
-
read: [
|
|
258
|
-
[{ user: "role" }, "=", "admin"],
|
|
259
|
-
[{ record: "id" }, "=", { user: "id" }],
|
|
260
|
-
],
|
|
261
|
-
update: [[{ user: "role" }, "=", "admin"]],
|
|
262
|
-
delete: [[{ user: "role" }, "=", "admin"]],
|
|
263
|
-
})
|
|
264
|
-
.gqlPermission([
|
|
265
|
-
{ conditions: [[{ user: "role" }, "=", "admin"]], actions: "all" },
|
|
266
|
-
{ conditions: [[{ user: "role" }, "=", "user"]], actions: ["read"] },
|
|
267
|
-
]);
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
Following the secure-by-default principle, all operations are denied if permissions are not configured.
|
|
271
|
-
|
|
272
|
-
### Resolver Concepts
|
|
273
|
-
|
|
274
|
-
Define Resolvers in files matching glob patterns specified in `tailor.config.ts`.
|
|
275
|
-
|
|
276
|
-
#### Resolver Creation
|
|
277
|
-
|
|
278
|
-
Define Resolvers using `createResolver` method:
|
|
279
|
-
|
|
280
|
-
```typescript
|
|
281
|
-
createResolver({
|
|
282
|
-
name: "add",
|
|
283
|
-
operation: "query",
|
|
284
|
-
input: {
|
|
285
|
-
left: t.int(),
|
|
286
|
-
right: t.int(),
|
|
287
|
-
},
|
|
288
|
-
body: (context) => {
|
|
289
|
-
return {
|
|
290
|
-
result: context.input.left + context.input.right,
|
|
291
|
-
};
|
|
292
|
-
},
|
|
293
|
-
output: t.object({
|
|
294
|
-
result: t.int(),
|
|
295
|
-
}),
|
|
296
|
-
});
|
|
297
|
-
```
|
|
298
|
-
|
|
299
|
-
#### Input/Output Schemas
|
|
300
|
-
|
|
301
|
-
Define Input/Output schemas using methods of `t` object. Basic usage and supported field types are the same as TailorDB. TailorDB-specific options (e.g., index, relation) are not supported.
|
|
302
|
-
|
|
303
|
-
You can reuse fields defined with `db` object, but note that unsupported options will be ignored:
|
|
304
|
-
|
|
305
|
-
```typescript
|
|
306
|
-
const user = db.type("User", {
|
|
307
|
-
name: db.string().unique(),
|
|
308
|
-
age: db.int(),
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
createResolver({
|
|
312
|
-
input: {
|
|
313
|
-
name: user.fields.name,
|
|
314
|
-
},
|
|
315
|
-
});
|
|
316
|
-
```
|
|
317
|
-
|
|
318
|
-
#### Body Function
|
|
319
|
-
|
|
320
|
-
Define actual resolver logic in the `body` function. Function arguments include: `input` (input data), `user` (user performing the operation).
|
|
321
|
-
|
|
322
|
-
If you're generating Kysely types with a generator, you can use `getDB` to execute typed queries:
|
|
323
|
-
|
|
324
|
-
```typescript
|
|
325
|
-
import { getDB } from "../generated/tailordb";
|
|
326
|
-
|
|
327
|
-
createResolver({
|
|
328
|
-
name: "getUser",
|
|
329
|
-
operation: "query",
|
|
330
|
-
input: {
|
|
331
|
-
name: t.string(),
|
|
332
|
-
},
|
|
333
|
-
body: async (context) => {
|
|
334
|
-
const db = getDB("tailordb");
|
|
335
|
-
const query = db
|
|
336
|
-
.selectFrom("User")
|
|
337
|
-
.select("id")
|
|
338
|
-
.where("name", "=", context.input.name)
|
|
339
|
-
.limit(1)
|
|
340
|
-
.executeTakeFirstOrThrow();
|
|
341
|
-
return {
|
|
342
|
-
result: result.id,
|
|
343
|
-
};
|
|
344
|
-
},
|
|
345
|
-
output: t.object({
|
|
346
|
-
result: t.uuid(),
|
|
347
|
-
}),
|
|
348
|
-
});
|
|
349
|
-
```
|
|
350
|
-
|
|
351
|
-
### Executor Patterns
|
|
352
|
-
|
|
353
|
-
Define Executors in files matching glob patterns specified in `tailor.config.ts`.
|
|
354
|
-
|
|
355
|
-
```typescript
|
|
356
|
-
createExecutor({
|
|
357
|
-
name: "user-welcome",
|
|
358
|
-
description: "Send welcome email to new users",
|
|
359
|
-
trigger: recordCreatedTrigger({
|
|
360
|
-
type: user,
|
|
361
|
-
condition: ({ newRecord }) => !!newRecord.email && newRecord.isActive,
|
|
362
|
-
}),
|
|
363
|
-
operation: {
|
|
364
|
-
kind: "function",
|
|
365
|
-
body: async ({ newRecord }) => {
|
|
366
|
-
// Send welcome email logic here
|
|
367
|
-
},
|
|
368
|
-
},
|
|
369
|
-
});
|
|
370
|
-
```
|
|
371
|
-
|
|
372
|
-
#### Trigger Types
|
|
373
|
-
|
|
374
|
-
**Record Triggers** - Fire when records are created, updated, or deleted:
|
|
375
|
-
|
|
376
|
-
- `recordCreatedTrigger()`: Fires when a new record is created
|
|
377
|
-
- `recordUpdatedTrigger()`: Fires when a record is updated
|
|
378
|
-
- `recordDeletedTrigger()`: Fires when a record is deleted
|
|
379
|
-
|
|
380
|
-
Each trigger can include an optional filter function:
|
|
381
|
-
|
|
382
|
-
```typescript
|
|
383
|
-
recordUpdatedTrigger({
|
|
384
|
-
type: order,
|
|
385
|
-
condition: ({ newRecord, oldRecord }) =>
|
|
386
|
-
newRecord.status === "completed" && oldRecord.status !== "completed",
|
|
387
|
-
});
|
|
388
|
-
```
|
|
389
|
-
|
|
390
|
-
**Schedule Trigger** - Fires on a cron schedule:
|
|
391
|
-
|
|
392
|
-
```typescript
|
|
393
|
-
scheduleTrigger({ cron: "*/5 * * * *" });
|
|
394
|
-
scheduleTrigger({ cron: "0 9 * * 1" });
|
|
395
|
-
scheduleTrigger({ cron: "0 0 1 * *" });
|
|
396
|
-
scheduleTrigger({ cron: "0 * * * *", timezone: "Asia/Tokyo" });
|
|
397
|
-
```
|
|
398
|
-
|
|
399
|
-
**Incoming Webhook Trigger** - Fires when an external webhook is received:
|
|
400
|
-
|
|
401
|
-
```typescript
|
|
402
|
-
incomingWebhookTrigger<WebhookPayload>();
|
|
403
|
-
```
|
|
404
|
-
|
|
405
|
-
**Resolver Executed Trigger** - Fires when a resolver is executed:
|
|
406
|
-
|
|
407
|
-
```typescript
|
|
408
|
-
resolverExecutedTrigger({
|
|
409
|
-
resolver: createOrderResolver,
|
|
410
|
-
condition: ({ result, error }) => !error && result?.order?.id,
|
|
411
|
-
}
|
|
412
|
-
```
|
|
413
|
-
|
|
414
|
-
#### Execution Targets
|
|
415
|
-
|
|
416
|
-
**executeFunction / executeJobFunction** - Execute JavaScript/TypeScript functions:
|
|
417
|
-
|
|
418
|
-
```typescript
|
|
419
|
-
createExecutor({
|
|
420
|
-
operation: {
|
|
421
|
-
kind: "function",
|
|
422
|
-
body: async ({ newRecord }) => {
|
|
423
|
-
console.log("New record created:", newRecord);
|
|
424
|
-
},
|
|
425
|
-
},
|
|
426
|
-
});
|
|
427
|
-
```
|
|
428
|
-
|
|
429
|
-
**executeWebhook** - Call external webhooks with dynamic data:
|
|
430
|
-
|
|
431
|
-
```typescript
|
|
432
|
-
createExecutor({
|
|
433
|
-
operation: {
|
|
434
|
-
kind: "webhook",
|
|
435
|
-
url: ({ newRecord }) =>
|
|
436
|
-
`https://api.example.com/webhooks/${newRecord.type}`,
|
|
437
|
-
headers: {
|
|
438
|
-
"Content-Type": "application/json",
|
|
439
|
-
"X-API-Key": { vault: "api-keys", key: "external-api" },
|
|
440
|
-
},
|
|
441
|
-
requestBody: ({ newRecord }) => ({
|
|
442
|
-
id: newRecord.id,
|
|
443
|
-
timestamp: new Date(),
|
|
444
|
-
data: newRecord,
|
|
445
|
-
}),
|
|
446
|
-
},
|
|
447
|
-
});
|
|
448
|
-
```
|
|
449
|
-
|
|
450
|
-
**executeGql** - Execute GraphQL queries and mutations:
|
|
451
|
-
|
|
452
|
-
```typescript
|
|
453
|
-
createExecutor({
|
|
454
|
-
operation: {
|
|
455
|
-
kind: "graphql",
|
|
456
|
-
appName: "my-app",
|
|
457
|
-
query: gql`
|
|
458
|
-
mutation UpdateUserStatus($id: ID!, $status: String!) {
|
|
459
|
-
updateUser(id: $id, input: { status: $status }) {
|
|
460
|
-
id
|
|
461
|
-
status
|
|
462
|
-
updatedAt
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
`,
|
|
466
|
-
variables: ({ newRecord }) => ({
|
|
467
|
-
id: newRecord.userId,
|
|
468
|
-
status: "active",
|
|
469
|
-
}),
|
|
470
|
-
},
|
|
471
|
-
});
|
|
472
|
-
```
|
|
473
|
-
|
|
474
|
-
#### Execution Context
|
|
475
|
-
|
|
476
|
-
Context varies based on trigger type:
|
|
477
|
-
|
|
478
|
-
**Record trigger context** - For `recordCreatedTrigger`, `recordUpdatedTrigger`, and `recordDeletedTrigger`:
|
|
479
|
-
|
|
480
|
-
- `typeName`: Name of the TailorDB type
|
|
481
|
-
- `newRecord`: New record state (not available for delete triggers)
|
|
482
|
-
- `oldRecord`: Previous record state (not available for create triggers)
|
|
483
|
-
|
|
484
|
-
**Incoming webhook trigger context** - For `incomingWebhookTrigger`:
|
|
485
|
-
|
|
486
|
-
- `body`: Webhook request body
|
|
487
|
-
- `headers`: Webhook request headers
|
|
488
|
-
- `method`: HTTP method
|
|
489
|
-
- `rawBody`: Raw request body as string
|
|
490
|
-
|
|
491
|
-
**Resolver executed trigger context** - For `resolverExecutedTrigger`:
|
|
492
|
-
|
|
493
|
-
- `resolverName`: Name of the resolver
|
|
494
|
-
- `result`: Resolver's return value (when execution succeeds)
|
|
495
|
-
- `error`: Error object (when execution fails)
|
|
496
|
-
|
|
497
|
-
### Workflow Concepts
|
|
498
|
-
|
|
499
|
-
Define Workflows in files matching glob patterns specified in `tailor.config.ts`. Workflows orchestrate multiple jobs that can depend on each other.
|
|
500
|
-
|
|
501
|
-
#### Workflow Rules
|
|
502
|
-
|
|
503
|
-
**Important**: All workflow components must follow these rules:
|
|
504
|
-
|
|
505
|
-
| Rule | Description |
|
|
506
|
-
| ---------------------------------------------- | --------------------------------------------------- |
|
|
507
|
-
| `createWorkflow` result must be default export | Workflow files must export the workflow as default |
|
|
508
|
-
| All jobs must be named exports | Every job used in a workflow must be a named export |
|
|
509
|
-
| Job names must be unique | Job names must be unique across the entire project |
|
|
510
|
-
| `mainJob` is required | Every workflow must specify a `mainJob` |
|
|
511
|
-
| Jobs in `deps` must be job objects | Pass job objects, not strings |
|
|
512
|
-
|
|
513
|
-
#### Workflow Job Definition
|
|
514
|
-
|
|
515
|
-
Define workflow jobs using `createWorkflowJob`:
|
|
516
|
-
|
|
517
|
-
```typescript
|
|
518
|
-
import { createWorkflowJob } from "@tailor-platform/sdk";
|
|
519
|
-
import { getDB } from "../generated/tailordb";
|
|
520
|
-
|
|
521
|
-
// All jobs must be named exports
|
|
522
|
-
export const fetchCustomer = createWorkflowJob({
|
|
523
|
-
name: "fetch-customer",
|
|
524
|
-
body: async (input: { customerId: string }) => {
|
|
525
|
-
const db = getDB("tailordb");
|
|
526
|
-
const customer = await db
|
|
527
|
-
.selectFrom("Customer")
|
|
528
|
-
.selectAll()
|
|
529
|
-
.where("id", "=", input.customerId)
|
|
530
|
-
.executeTakeFirst();
|
|
531
|
-
return customer;
|
|
532
|
-
},
|
|
533
|
-
});
|
|
534
|
-
```
|
|
535
|
-
|
|
536
|
-
#### Job Dependencies
|
|
537
|
-
|
|
538
|
-
Jobs can depend on other jobs using the `deps` array. Dependent jobs are accessible via the second argument of `body` function with hyphens replaced by underscores:
|
|
539
|
-
|
|
540
|
-
```typescript
|
|
541
|
-
import { createWorkflowJob } from "@tailor-platform/sdk";
|
|
542
|
-
import { fetchCustomer } from "./jobs/fetch-customer";
|
|
543
|
-
import { sendNotification } from "./jobs/send-notification";
|
|
544
|
-
|
|
545
|
-
// All jobs must be named exports - including jobs with dependencies
|
|
546
|
-
export const processOrder = createWorkflowJob({
|
|
547
|
-
name: "process-order",
|
|
548
|
-
deps: [fetchCustomer, sendNotification],
|
|
549
|
-
body: async (input: { orderId: string; customerId: string }, { jobs }) => {
|
|
550
|
-
// Access dependent jobs with hyphens replaced by underscores
|
|
551
|
-
// "fetch-customer" -> jobs.fetch_customer()
|
|
552
|
-
// "send-notification" -> jobs.send_notification()
|
|
553
|
-
const customer = await jobs.fetch_customer({
|
|
554
|
-
customerId: input.customerId,
|
|
555
|
-
});
|
|
556
|
-
|
|
557
|
-
const notification = await jobs.send_notification({
|
|
558
|
-
message: `Order ${input.orderId} is being processed`,
|
|
559
|
-
recipient: customer.email,
|
|
560
|
-
});
|
|
561
|
-
|
|
562
|
-
return {
|
|
563
|
-
orderId: input.orderId,
|
|
564
|
-
customerEmail: customer.email,
|
|
565
|
-
notificationSent: notification.sent,
|
|
566
|
-
};
|
|
567
|
-
},
|
|
568
|
-
});
|
|
569
|
-
```
|
|
570
|
-
|
|
571
|
-
#### Workflow Definition
|
|
572
|
-
|
|
573
|
-
Define a workflow using `createWorkflow` and export it as default:
|
|
574
|
-
|
|
575
|
-
```typescript
|
|
576
|
-
import { createWorkflow, createWorkflowJob } from "@tailor-platform/sdk";
|
|
577
|
-
import { fetchCustomer } from "./jobs/fetch-customer";
|
|
578
|
-
import { sendNotification } from "./jobs/send-notification";
|
|
579
|
-
|
|
580
|
-
// Jobs must be named exports
|
|
581
|
-
export const processOrder = createWorkflowJob({
|
|
582
|
-
name: "process-order",
|
|
583
|
-
deps: [fetchCustomer, sendNotification],
|
|
584
|
-
body: async (input, { jobs }) => {
|
|
585
|
-
// ... job logic
|
|
586
|
-
},
|
|
587
|
-
});
|
|
588
|
-
|
|
589
|
-
// Workflow must be default export
|
|
590
|
-
export default createWorkflow({
|
|
591
|
-
name: "order-processing",
|
|
592
|
-
mainJob: processOrder,
|
|
593
|
-
});
|
|
594
|
-
```
|
|
595
|
-
|
|
596
|
-
#### File Organization
|
|
597
|
-
|
|
598
|
-
Recommended file structure for workflows:
|
|
599
|
-
|
|
600
|
-
```
|
|
601
|
-
workflows/
|
|
602
|
-
├── jobs/
|
|
603
|
-
│ ├── fetch-customer.ts # export const fetchCustomer = createWorkflowJob(...)
|
|
604
|
-
│ └── send-notification.ts # export const sendNotification = createWorkflowJob(...)
|
|
605
|
-
└── order-processing.ts # export const processOrder = createWorkflowJob(...)
|
|
606
|
-
# export default createWorkflow(...)
|
|
607
|
-
```
|
|
608
|
-
|
|
609
|
-
All jobs can be in a single file or split across multiple files, as long as they are named exports.
|