@tailor-platform/sdk 2.0.0-next.6 → 2.0.0-next.7
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 +47 -0
- package/dist/{application-BJXRpQj5.mjs → application-GzW98_Xr.mjs} +199 -172
- package/dist/application-GzW98_Xr.mjs.map +1 -0
- package/dist/application-OM0taSPn.mjs +3 -0
- package/dist/cli/index.mjs +12 -9
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.mjs +2 -2
- package/dist/completion/zsh-worker.zsh +1 -1
- package/dist/configure/config/types.d.mts +5 -1
- package/dist/configure/index.mjs +16 -16
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/auth/types.d.mts +18 -23
- package/dist/configure/services/executor/operation.d.mts +1 -1
- package/dist/configure/services/workflow/job.d.mts +10 -10
- package/dist/configure/services/workflow/workflow.d.mts +6 -4
- package/dist/{globals-D-YbJKW-.mjs → globals-CBZ0egXT.mjs} +3 -6
- package/dist/globals-CBZ0egXT.mjs.map +1 -0
- package/dist/{register-ts-hook-DL31O2W9.mjs → register-ts-hook-DvEs6YsL.mjs} +35 -78
- package/dist/register-ts-hook-DvEs6YsL.mjs.map +1 -0
- package/dist/{registry-CC3CbQiF.mjs → registry-i7EdJ-D5.mjs} +10 -10
- package/dist/registry-i7EdJ-D5.mjs.map +1 -0
- package/dist/runtime/globals.d.mts +1 -4
- package/dist/runtime/index.mjs +1 -1
- package/dist/runtime/workflow.d.mts +1 -76
- package/dist/runtime/workflow.mjs +1 -1
- package/dist/service-BJAQ70e5.mjs +3 -0
- package/dist/{service-CnHz9rwz.mjs → service-Dmxa2I4i.mjs} +31 -17
- package/dist/service-Dmxa2I4i.mjs.map +1 -0
- package/dist/utils/test/index.mjs +11 -11
- package/dist/utils/test/index.mjs.map +1 -1
- package/dist/utils/test/mock.d.mts +6 -6
- package/dist/vitest/environment.mjs +1 -1
- package/dist/vitest/index.mjs +67 -99
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mocks/workflow.d.mts +21 -41
- package/dist/vitest/setup.mjs +1 -1
- package/dist/vitest/workflow-local.d.mts +2 -2
- package/dist/workflow-CsBvRE3g.mjs +34 -0
- package/dist/workflow-CsBvRE3g.mjs.map +1 -0
- package/docs/cli/application.md +2 -0
- package/docs/cli/function.md +2 -2
- package/docs/cli/tailordb.md +2 -2
- package/docs/cli-reference.md +1 -1
- package/docs/configuration.md +2 -0
- package/docs/github-actions.md +2 -2
- package/docs/migration/v2.md +128 -11
- package/docs/runtime.md +1 -1
- package/docs/services/auth.md +7 -7
- package/docs/services/workflow.md +43 -43
- package/docs/testing.md +9 -9
- package/package.json +2 -2
- package/dist/application-BJXRpQj5.mjs.map +0 -1
- package/dist/application-BV-AXawv.mjs +0 -3
- package/dist/globals-D-YbJKW-.mjs.map +0 -1
- package/dist/register-ts-hook-DL31O2W9.mjs.map +0 -1
- package/dist/registry-CC3CbQiF.mjs.map +0 -1
- package/dist/service-CnHz9rwz.mjs.map +0 -1
- package/dist/service-nU6ITOHL.mjs +0 -3
- package/dist/workflow-9kHGKxF2.mjs +0 -64
- package/dist/workflow-9kHGKxF2.mjs.map +0 -1
|
@@ -10,7 +10,7 @@ Workflows provide:
|
|
|
10
10
|
- Durable execution with automatic state management
|
|
11
11
|
- Resume capabilities from failure points
|
|
12
12
|
- Access to TailorDB via Kysely query builder
|
|
13
|
-
- Job
|
|
13
|
+
- Job starting to compose multi-step logic
|
|
14
14
|
|
|
15
15
|
For the official Tailor Platform documentation, see [Workflow Guide](https://docs.tailor.tech/guides/workflow).
|
|
16
16
|
|
|
@@ -26,12 +26,12 @@ All workflow components must follow these rules:
|
|
|
26
26
|
- **Job name uniqueness**: Job names must be unique across the entire project (not just within one file)
|
|
27
27
|
- **mainJob required**: Every workflow must specify a `mainJob`
|
|
28
28
|
|
|
29
|
-
| Rule | Description
|
|
30
|
-
| ---------------------------------------------- |
|
|
31
|
-
| `createWorkflow` result must be default export | Workflow files must export the workflow as default
|
|
32
|
-
| All jobs must be named exports | Includes `mainJob` and any job
|
|
33
|
-
| Job `name` values must be unique | Job names must be unique across the entire project
|
|
34
|
-
| `mainJob` is required | Every workflow must specify a `mainJob`
|
|
29
|
+
| Rule | Description |
|
|
30
|
+
| ---------------------------------------------- | ---------------------------------------------------------------------------------------------------- |
|
|
31
|
+
| `createWorkflow` result must be default export | Workflow files must export the workflow as default |
|
|
32
|
+
| All jobs must be named exports | Includes `mainJob` and any job started via `.start()` (even if referenced only within the same file) |
|
|
33
|
+
| Job `name` values must be unique | Job names must be unique across the entire project |
|
|
34
|
+
| `mainJob` is required | Every workflow must specify a `mainJob` |
|
|
35
35
|
|
|
36
36
|
## Creating a Workflow Job
|
|
37
37
|
|
|
@@ -92,11 +92,11 @@ export const nullJob = createWorkflowJob({
|
|
|
92
92
|
|
|
93
93
|
These constraints are enforced at compile time — you will get a type error if you use an unsupported type.
|
|
94
94
|
|
|
95
|
-
##
|
|
95
|
+
## Starting Jobs
|
|
96
96
|
|
|
97
|
-
Use `.
|
|
97
|
+
Use `.start()` to start other jobs from within a job.
|
|
98
98
|
|
|
99
|
-
Jobs are
|
|
99
|
+
Jobs are started by calling `.start()` on the other job object (no `deps` and no `jobs` object in the context).
|
|
100
100
|
|
|
101
101
|
```typescript
|
|
102
102
|
import { createWorkflowJob } from "@tailor-platform/sdk";
|
|
@@ -106,10 +106,10 @@ import { sendNotification } from "./jobs/send-notification";
|
|
|
106
106
|
export const mainJob = createWorkflowJob({
|
|
107
107
|
name: "main-job",
|
|
108
108
|
body: (input: { customerId: string }) => {
|
|
109
|
-
const customer = fetchCustomer.
|
|
109
|
+
const customer = fetchCustomer.start({
|
|
110
110
|
customerId: input.customerId,
|
|
111
111
|
});
|
|
112
|
-
const notification = sendNotification.
|
|
112
|
+
const notification = sendNotification.start({
|
|
113
113
|
message: "Order processed",
|
|
114
114
|
recipient: customer.email,
|
|
115
115
|
});
|
|
@@ -120,51 +120,51 @@ export const mainJob = createWorkflowJob({
|
|
|
120
120
|
|
|
121
121
|
### Deterministic Execution Requirement
|
|
122
122
|
|
|
123
|
-
Workflow jobs use a **suspend/resume execution model**. When a job calls `.
|
|
123
|
+
Workflow jobs use a **suspend/resume execution model**. When a job calls `.start()`, the runtime suspends the current job, executes the started job, and then **re-executes the calling job from the beginning** with cached results from previous starts.
|
|
124
124
|
|
|
125
|
-
This means that **job code must be deterministic** — every re-execution must produce the same sequence of `.
|
|
125
|
+
This means that **job code must be deterministic** — every re-execution must produce the same sequence of `.start()` calls with the same arguments in the same order.
|
|
126
126
|
|
|
127
|
-
Using `.
|
|
127
|
+
Using `.start()` inside a loop works correctly, as long as the loop is deterministic:
|
|
128
128
|
|
|
129
129
|
```typescript
|
|
130
130
|
// ✅ OK: deterministic loop — same calls in the same order on every execution
|
|
131
131
|
const regions = ["us", "eu", "ap"];
|
|
132
132
|
for (const region of regions) {
|
|
133
|
-
const result = fetchData.
|
|
133
|
+
const result = fetchData.start({ region });
|
|
134
134
|
results.push(result);
|
|
135
135
|
}
|
|
136
136
|
```
|
|
137
137
|
|
|
138
138
|
```typescript
|
|
139
139
|
// ❌ Bad: non-deterministic — argument changes between executions
|
|
140
|
-
processJob.
|
|
140
|
+
processJob.start({ timestamp: Date.now() });
|
|
141
141
|
|
|
142
|
-
// ✅ OK: call Date.now() in
|
|
143
|
-
const timestamp = timestampJob.
|
|
144
|
-
processJob.
|
|
142
|
+
// ✅ OK: call Date.now() in a separate job
|
|
143
|
+
const timestamp = timestampJob.start();
|
|
144
|
+
processJob.start({ timestamp });
|
|
145
145
|
```
|
|
146
146
|
|
|
147
147
|
```typescript
|
|
148
148
|
// ❌ Bad: non-deterministic — external data may change between executions
|
|
149
149
|
const items = await fetch("https://api.example.com/items").then((r) => r.json());
|
|
150
150
|
for (const item of items) {
|
|
151
|
-
processItem.
|
|
151
|
+
processItem.start({ id: item.id });
|
|
152
152
|
}
|
|
153
153
|
|
|
154
|
-
// ✅ OK: call fetch("https://api.example.com/items").then((r) => r.json()); in
|
|
155
|
-
const items = fetchItemsJob.
|
|
154
|
+
// ✅ OK: call fetch("https://api.example.com/items").then((r) => r.json()); in a separate job
|
|
155
|
+
const items = fetchItemsJob.start();
|
|
156
156
|
for (const item of items) {
|
|
157
|
-
processItem.
|
|
157
|
+
processItem.start({ id: item.id });
|
|
158
158
|
}
|
|
159
159
|
```
|
|
160
160
|
|
|
161
|
-
If the runtime detects that a `.
|
|
161
|
+
If the runtime detects that a `.start()` call at the same position has different arguments than the previous execution, it will throw an **argument hash mismatch error**.
|
|
162
162
|
|
|
163
163
|
**Guidelines:**
|
|
164
164
|
|
|
165
|
-
- Do not use non-deterministic values (random numbers, timestamps, external API responses) as `.
|
|
166
|
-
- Do not use conditions that may change between executions to decide whether to call `.
|
|
167
|
-
- Any data that varies between executions should be fetched **inside the
|
|
165
|
+
- Do not use non-deterministic values (random numbers, timestamps, external API responses) as `.start()` arguments.
|
|
166
|
+
- Do not use conditions that may change between executions to decide whether to call `.start()`.
|
|
167
|
+
- Any data that varies between executions should be fetched **inside the started job**, not passed as an argument from the calling job.
|
|
168
168
|
|
|
169
169
|
## Workflow Definition
|
|
170
170
|
|
|
@@ -181,12 +181,12 @@ export const processOrder = createWorkflowJob({
|
|
|
181
181
|
body: (input: { customerId: string }, { env, invoker }) => {
|
|
182
182
|
// `env` contains values from `tailor.config.ts` -> `env`.
|
|
183
183
|
// `invoker` is the principal running this job, or the machine user
|
|
184
|
-
// configured through the
|
|
185
|
-
//
|
|
186
|
-
const customer = fetchCustomer.
|
|
184
|
+
// configured through the start `invoker` option; `null` for anonymous calls.
|
|
185
|
+
// Start other jobs by calling .start() on the job object.
|
|
186
|
+
const customer = fetchCustomer.start({
|
|
187
187
|
customerId: input.customerId,
|
|
188
188
|
});
|
|
189
|
-
sendNotification.
|
|
189
|
+
sendNotification.start({
|
|
190
190
|
message: "Order processed",
|
|
191
191
|
recipient: customer.email,
|
|
192
192
|
});
|
|
@@ -353,7 +353,7 @@ export default createWorkflow({
|
|
|
353
353
|
|
|
354
354
|
## Execution Policies
|
|
355
355
|
|
|
356
|
-
Execution policies apply a per-key concurrency cap to workflow job function dispatches. Declare them at the workspace level and pass a matching key when
|
|
356
|
+
Execution policies apply a per-key concurrency cap to workflow job function dispatches. Declare them at the workspace level and pass a matching key when starting a job; the platform serializes dispatches that resolve to the same key and suspends any that would exceed the cap until slots free up.
|
|
357
357
|
|
|
358
358
|
### Declaring Policies
|
|
359
359
|
|
|
@@ -401,7 +401,7 @@ An exact-key policy applies to dispatches whose runtime key equals the policy ke
|
|
|
401
401
|
|
|
402
402
|
### Referencing a Policy from a Workflow
|
|
403
403
|
|
|
404
|
-
Pass the runtime key through the `executionPolicyKey` option on `job.
|
|
404
|
+
Pass the runtime key through the `executionPolicyKey` option on `job.start()` or `tailor.workflow.startJobFunction()`. For exact-key policies, use `<policy>.key` directly — it's typed so only a value that came from a declared policy can be passed. For wildcard policies (`matchType: "prefix"`), there is no `<policy>.key` — call `<policy>.keyFor(suffix)` to build the concrete key. `keyFor` joins the prefix and suffix with `.` by default; override it with `separator` — the second argument to `defineWorkflowExecutionPolicies` (applies to every policy in the group), or a `def` field on a single `defineWorkflowExecutionPolicy`.
|
|
405
405
|
|
|
406
406
|
```typescript
|
|
407
407
|
import { createWorkflowJob } from "@tailor-platform/sdk";
|
|
@@ -413,13 +413,13 @@ export const mainJob = createWorkflowJob({
|
|
|
413
413
|
name: "main-job",
|
|
414
414
|
body: async (input: { tenantId: string }) => {
|
|
415
415
|
// Exact key policy: pass .key directly.
|
|
416
|
-
await sendNotification.
|
|
416
|
+
await sendNotification.start(
|
|
417
417
|
{ message: "Order processed" },
|
|
418
418
|
{ executionPolicyKey: executionPolicies.premium.key },
|
|
419
419
|
);
|
|
420
420
|
|
|
421
421
|
// Wildcard policy: build the concrete key with keyFor().
|
|
422
|
-
await fetchTenant.
|
|
422
|
+
await fetchTenant.start(
|
|
423
423
|
{ tenantId: input.tenantId },
|
|
424
424
|
{ executionPolicyKey: executionPolicies.tenantApi.keyFor(input.tenantId) },
|
|
425
425
|
);
|
|
@@ -427,13 +427,13 @@ export const mainJob = createWorkflowJob({
|
|
|
427
427
|
});
|
|
428
428
|
```
|
|
429
429
|
|
|
430
|
-
The same `executionPolicyKey` option is available on `tailor.workflow.startJobFunction(name, args, options)`
|
|
430
|
+
The same `executionPolicyKey` option is available on `tailor.workflow.startJobFunction(name, args, options)` for jobs invoked by name.
|
|
431
431
|
|
|
432
|
-
##
|
|
432
|
+
## Starting a Workflow from a Resolver
|
|
433
433
|
|
|
434
|
-
You can start a workflow execution from a resolver using `workflow.
|
|
434
|
+
You can start a workflow execution from a resolver using `workflow.start()`.
|
|
435
435
|
|
|
436
|
-
- `workflow.
|
|
436
|
+
- `workflow.start(args, options?)` returns a workflow run ID (`Promise<string>`).
|
|
437
437
|
- To run with machine-user permissions, pass `{ invoker: "<machine-user>" }`. The name is type-narrowed to the machine users defined in your auth config.
|
|
438
438
|
|
|
439
439
|
```typescript
|
|
@@ -441,14 +441,14 @@ import { createResolver, t } from "@tailor-platform/sdk";
|
|
|
441
441
|
import orderProcessingWorkflow from "../workflows/order-processing";
|
|
442
442
|
|
|
443
443
|
export default createResolver({
|
|
444
|
-
name: "
|
|
444
|
+
name: "startOrderProcessing",
|
|
445
445
|
operation: "mutation",
|
|
446
446
|
input: {
|
|
447
447
|
orderId: t.string(),
|
|
448
448
|
customerId: t.string(),
|
|
449
449
|
},
|
|
450
450
|
body: async ({ input }) => {
|
|
451
|
-
const workflowRunId = await orderProcessingWorkflow.
|
|
451
|
+
const workflowRunId = await orderProcessingWorkflow.start(
|
|
452
452
|
{ orderId: input.orderId, customerId: input.customerId },
|
|
453
453
|
{ invoker: "manager-machine-user" },
|
|
454
454
|
);
|
|
@@ -461,7 +461,7 @@ export default createResolver({
|
|
|
461
461
|
});
|
|
462
462
|
```
|
|
463
463
|
|
|
464
|
-
See the full working example in the repository: [example/resolvers/
|
|
464
|
+
See the full working example in the repository: [example/resolvers/startWorkflow.ts](https://github.com/tailor-platform/sdk/blob/main/example/resolvers/startWorkflow.ts).
|
|
465
465
|
|
|
466
466
|
## File Organization
|
|
467
467
|
|
package/docs/testing.md
CHANGED
|
@@ -13,7 +13,7 @@ Unit-test entrypoints exposed by the SDK:
|
|
|
13
13
|
|
|
14
14
|
- `resolver.body({ input, caller, invoker, env })` — invoke a resolver
|
|
15
15
|
- `workflowJob.body(input, { env, invoker })` — invoke a workflow job body directly
|
|
16
|
-
- `workflowJob.
|
|
16
|
+
- `workflowJob.start(input)` — chain a workflow job through the workflow runtime
|
|
17
17
|
- `runWorkflowLocally(workflow, args)` — run a workflow chain locally with real job bodies
|
|
18
18
|
- `executor.operation.body({ ...args, invoker })` — invoke a function-kind executor
|
|
19
19
|
|
|
@@ -146,13 +146,13 @@ Pass `{ onUnhandled: "error" }` to make an unmatched query fail instead of retur
|
|
|
146
146
|
|
|
147
147
|
### Workflow Mock
|
|
148
148
|
|
|
149
|
-
Workflow job `.
|
|
149
|
+
Workflow job `.start()` calls use the platform workflow runtime. Acquire `mockWorkflow()` when you want to provide start responses with `setJobHandler` / `enqueueResult` or assert on `startedJobs`. If no response is configured, the mock throws so missing job mocks fail loudly. Use `job(definition)` or `workflow(definition)` to get a stable, fully typed Vitest mock for one definition:
|
|
150
150
|
|
|
151
151
|
```typescript
|
|
152
152
|
import { mockWorkflow } from "@tailor-platform/sdk/vitest";
|
|
153
153
|
import { processPayment, validateOrder } from "./jobs";
|
|
154
154
|
|
|
155
|
-
test("workflow
|
|
155
|
+
test("workflow starts jobs", async () => {
|
|
156
156
|
using wf = mockWorkflow();
|
|
157
157
|
const validate = wf.job(validateOrder);
|
|
158
158
|
const payment = wf.job(processPayment);
|
|
@@ -166,7 +166,7 @@ test("workflow triggers jobs", async () => {
|
|
|
166
166
|
});
|
|
167
167
|
```
|
|
168
168
|
|
|
169
|
-
Unconfigured definition mocks continue to run their real implementations. The lower-level `
|
|
169
|
+
Unconfigured definition mocks continue to run their real implementations. The lower-level `startJobFunction`, `startWorkflow`, `resumeWorkflowExecution`, `wait`, and `resolve` mocks and the existing `setJobHandler`, `enqueueResult`, `enqueueResults`, and call-record helpers remain available.
|
|
170
170
|
|
|
171
171
|
Use `waitPoint(definition)` for typed wait-point control:
|
|
172
172
|
|
|
@@ -374,7 +374,7 @@ export default defineConfig({
|
|
|
374
374
|
|
|
375
375
|
## Unit Tests
|
|
376
376
|
|
|
377
|
-
Unit tests call `.body()` (or `.
|
|
377
|
+
Unit tests call `.body()` (or `.start()`) directly on a resolver, workflow job, or executor and stub any platform-provided globals they touch.
|
|
378
378
|
|
|
379
379
|
### Testing Resolvers
|
|
380
380
|
|
|
@@ -669,7 +669,7 @@ To exercise the full chain (executor → helper → TailorDB), drop the spy and
|
|
|
669
669
|
|
|
670
670
|
### Testing Workflow Jobs
|
|
671
671
|
|
|
672
|
-
Workflow jobs expose the same `.body()` entrypoint as resolvers, plus `.
|
|
672
|
+
Workflow jobs expose the same `.body()` entrypoint as resolvers, plus `.start()` for calling them from another job or a test.
|
|
673
673
|
|
|
674
674
|
#### Simple job
|
|
675
675
|
|
|
@@ -696,7 +696,7 @@ describe("validateOrder", () => {
|
|
|
696
696
|
});
|
|
697
697
|
```
|
|
698
698
|
|
|
699
|
-
#### Jobs that
|
|
699
|
+
#### Jobs that start other jobs
|
|
700
700
|
|
|
701
701
|
Use `mockWorkflow().job(definition)` to replace dependent jobs with deterministic results:
|
|
702
702
|
|
|
@@ -781,7 +781,7 @@ The lower-level `setWaitHandler` and `waitCalls` APIs remain available when one
|
|
|
781
781
|
|
|
782
782
|
#### Running a full workflow locally
|
|
783
783
|
|
|
784
|
-
To exercise the full chain with real job bodies, call `runWorkflowLocally(workflow, args)`. Dependent jobs run their real `.body()` functions, and
|
|
784
|
+
To exercise the full chain with real job bodies, call `runWorkflowLocally(workflow, args)`. Dependent jobs run their real `.body()` functions, and start args/results cross the same JSON boundary as the platform, so a non-serializable payload fails the test exactly as it would in production:
|
|
785
785
|
|
|
786
786
|
```typescript
|
|
787
787
|
import { runWorkflowLocally } from "@tailor-platform/sdk/vitest";
|
|
@@ -803,7 +803,7 @@ Pass `{ env }` as the third argument when job bodies need configuration values d
|
|
|
803
803
|
|
|
804
804
|
If you already acquired `mockWorkflow()`, you can also call `wf.setEnv(...)` to reuse the same env across local workflow runs.
|
|
805
805
|
|
|
806
|
-
Like the platform runtime, the local runner re-runs the orchestrator body once per `.
|
|
806
|
+
Like the platform runtime, the local runner re-runs the orchestrator body once per `.start()` call (N starts means N+1 passes), so any side effects outside the start results fire on every pass. Keep the body deterministic and move repeatable side effects into the started jobs.
|
|
807
807
|
|
|
808
808
|
This helper is still a local runner. Use E2E tests when you need to verify deployed workflow scheduling, suspension, or replay behavior.
|
|
809
809
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tailor-platform/sdk",
|
|
3
|
-
"version": "2.0.0-next.
|
|
3
|
+
"version": "2.0.0-next.7",
|
|
4
4
|
"description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"repository": {
|
|
@@ -205,7 +205,7 @@
|
|
|
205
205
|
"oxlint": "1.73.0",
|
|
206
206
|
"oxlint-tsgolint": "0.24.0",
|
|
207
207
|
"sonda": "0.14.0",
|
|
208
|
-
"tsdown": "0.22.
|
|
208
|
+
"tsdown": "0.22.7",
|
|
209
209
|
"typescript": "6.0.3",
|
|
210
210
|
"vitest": "4.1.10",
|
|
211
211
|
"zinfer": "0.2.5",
|