@tailor-platform/sdk 2.0.0-next.5 → 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 +85 -0
- package/dist/{application-Bf8wxOi6.mjs → application-GzW98_Xr.mjs} +377 -1418
- package/dist/application-GzW98_Xr.mjs.map +1 -0
- package/dist/application-OM0taSPn.mjs +3 -0
- package/dist/cli/commands/deploy/deploy.d.mts +8 -3
- package/dist/cli/commands/generate/options.d.mts +0 -1
- package/dist/cli/commands/generate/service.d.mts +1 -1
- package/dist/cli/commands/staticwebsite/deploy.d.mts +19 -0
- package/dist/cli/commands/workspace/create.d.mts +3 -0
- package/dist/cli/commands/workspace/list.d.mts +1 -0
- package/dist/cli/commands/workspace/transform.d.mts +2 -0
- package/dist/cli/index.mjs +781 -1553
- package/dist/cli/index.mjs.map +1 -1
- package/dist/cli/lib.d.mts +7 -3
- package/dist/cli/lib.mjs +49 -3
- package/dist/cli/lib.mjs.map +1 -1
- package/dist/cli/shared/config-loader.d.mts +1 -1
- package/dist/cli/shared/readonly-guard.d.mts +25 -0
- package/dist/cli/shared/tailordb-namespaces.d.mts +37 -0
- package/dist/cli/shared/workspace-name.d.mts +1 -0
- package/dist/completion/zsh-worker.zsh +15 -303
- package/dist/configure/config/types.d.mts +5 -1
- package/dist/configure/index.mjs +20 -19
- package/dist/configure/index.mjs.map +1 -1
- package/dist/configure/services/auth/types.d.mts +18 -23
- package/dist/configure/services/executor/executor.d.mts +2 -13
- package/dist/configure/services/executor/operation.d.mts +8 -3
- package/dist/configure/services/tailordb/schema.d.mts +1 -7
- package/dist/configure/services/tailordb/types.d.mts +1 -1
- package/dist/configure/services/workflow/execution-policy.d.mts +2 -2
- package/dist/configure/services/workflow/job.d.mts +10 -10
- package/dist/configure/services/workflow/workflow.d.mts +6 -4
- package/dist/configure/types/type.d.mts +1 -9
- package/dist/{crashreport-D3RjuSYb.mjs → crashreport-0ioNRZ4K.mjs} +1 -1
- package/dist/{crashreport-Cg-pqRJV.mjs → crashreport-CxHne11E.mjs} +4 -3
- package/dist/{crashreport-Cg-pqRJV.mjs.map → crashreport-CxHne11E.mjs.map} +1 -1
- package/dist/errors-DfnF4ibq.mjs +100 -0
- package/dist/errors-DfnF4ibq.mjs.map +1 -0
- package/dist/{globals-BjhJJ2dN.mjs → globals-CBZ0egXT.mjs} +13 -10
- package/dist/globals-CBZ0egXT.mjs.map +1 -0
- package/dist/{logger-BEiZZ3qT.mjs → logger-BwS4ppwO.mjs} +3 -3
- package/dist/logger-BwS4ppwO.mjs.map +1 -0
- package/dist/parser/service/tailordb/type-source.d.mts +10 -0
- package/dist/{register-ts-hook-BhzVkej0.mjs → register-ts-hook-DvEs6YsL.mjs} +2571 -1636
- package/dist/register-ts-hook-DvEs6YsL.mjs.map +1 -0
- package/dist/{registry-BSbq6SPo.mjs → registry-i7EdJ-D5.mjs} +10 -10
- package/dist/registry-i7EdJ-D5.mjs.map +1 -0
- package/dist/runtime/field-parse.d.mts +10 -0
- package/dist/runtime/globals.d.mts +3 -3
- package/dist/runtime/index.mjs +1 -1
- package/dist/runtime/workflow.d.mts +18 -33
- package/dist/runtime/workflow.mjs +1 -1
- package/dist/{schema-iCd1ugGp.mjs → schema--xYWRGfe.mjs} +42 -24
- package/dist/schema--xYWRGfe.mjs.map +1 -0
- package/dist/{secret-file-aoNXJmKa.mjs → secret-file-C3J4Gy8r.mjs} +2 -27
- package/dist/secret-file-C3J4Gy8r.mjs.map +1 -0
- package/dist/{secretmanager-Bd45j7an.mjs → secretmanager-IY4UvinW.mjs} +42 -9
- package/dist/secretmanager-IY4UvinW.mjs.map +1 -0
- package/dist/service-BJAQ70e5.mjs +3 -0
- package/dist/{service-DeZeYa06.mjs → service-DaeJlBoH.mjs} +3 -3
- package/dist/{service-DeZeYa06.mjs.map → service-DaeJlBoH.mjs.map} +1 -1
- package/dist/{service-CzZEVrp9.mjs → service-Dmxa2I4i.mjs} +64 -28
- package/dist/service-Dmxa2I4i.mjs.map +1 -0
- package/dist/service_pb-BoLM0X4K.mjs +167 -0
- package/dist/service_pb-BoLM0X4K.mjs.map +1 -0
- package/dist/{service_pb-DZkfjB_6.mjs → service_pb-r1T7kpJc.mjs} +1 -1
- package/dist/types/executor.generated.d.mts +60 -42
- package/dist/user-agent-Bgsszb5I.mjs +28 -0
- package/dist/user-agent-Bgsszb5I.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.d.mts +8 -8
- package/dist/vitest/index.mjs +613 -227
- package/dist/vitest/index.mjs.map +1 -1
- package/dist/vitest/mock.d.mts +8 -8
- package/dist/vitest/mocks/aigateway.d.mts +14 -4
- package/dist/vitest/mocks/authconnection.d.mts +14 -4
- package/dist/vitest/mocks/file.d.mts +23 -6
- package/dist/vitest/mocks/iconv.d.mts +28 -4
- package/dist/vitest/mocks/idp.d.mts +25 -9
- package/dist/vitest/mocks/secretmanager.d.mts +13 -4
- package/dist/vitest/mocks/tailordb.d.mts +48 -7
- package/dist/vitest/mocks/workflow.d.mts +59 -33
- package/dist/vitest/setup.mjs +2 -2
- 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/dist/{service_pb-Bk7dL-yT.mjs → workspace_resource_pb-Db3fv68L.mjs} +47 -165
- package/dist/workspace_resource_pb-Db3fv68L.mjs.map +1 -0
- package/docs/cli/application.md +63 -15
- package/docs/cli/function.md +2 -2
- package/docs/cli/tailordb.md +8 -140
- package/docs/cli/workspace.md +6 -4
- package/docs/cli-reference.md +11 -8
- package/docs/configuration.md +2 -0
- package/docs/github-actions.md +8 -0
- package/docs/migration/v2.md +169 -11
- package/docs/runtime.md +5 -6
- package/docs/services/auth.md +7 -7
- package/docs/services/executor.md +5 -0
- package/docs/services/tailordb.md +44 -0
- package/docs/services/workflow.md +43 -43
- package/docs/testing.md +112 -96
- package/package.json +2 -5
- package/dist/application-Bf8wxOi6.mjs.map +0 -1
- package/dist/application-CUkjzfij.mjs +0 -3
- package/dist/cli/erd-viewer-assets/app.js +0 -1424
- package/dist/cli/erd-viewer-assets/index.html +0 -77
- package/dist/cli/erd-viewer-assets/serve.json +0 -13
- package/dist/cli/erd-viewer-assets/styles.css +0 -1036
- package/dist/configure/types/field-runtime.d.mts +0 -16
- package/dist/errors-118wUDRr.mjs +0 -42
- package/dist/errors-118wUDRr.mjs.map +0 -1
- package/dist/globals-BjhJJ2dN.mjs.map +0 -1
- package/dist/logger-BEiZZ3qT.mjs.map +0 -1
- package/dist/register-ts-hook-BhzVkej0.mjs.map +0 -1
- package/dist/registry-BSbq6SPo.mjs.map +0 -1
- package/dist/resource_pb-BRv7AMXK.mjs +0 -49
- package/dist/resource_pb-BRv7AMXK.mjs.map +0 -1
- package/dist/schema-iCd1ugGp.mjs.map +0 -1
- package/dist/secret-file-aoNXJmKa.mjs.map +0 -1
- package/dist/secretmanager-Bd45j7an.mjs.map +0 -1
- package/dist/service-BT0GPd01.mjs +0 -3
- package/dist/service-CzZEVrp9.mjs.map +0 -1
- package/dist/service_pb-Bk7dL-yT.mjs.map +0 -1
- package/dist/workflow-GNuSnsTx.mjs +0 -22
- package/dist/workflow-GNuSnsTx.mjs.map +0 -1
package/docs/runtime.md
CHANGED
|
@@ -31,7 +31,7 @@ const token = await authconnection.getConnectionToken("google");
|
|
|
31
31
|
const client = new idp.Client({ namespace: "my-namespace" });
|
|
32
32
|
const { users } = await client.users({ first: 10 });
|
|
33
33
|
|
|
34
|
-
const executionId = await workflow.
|
|
34
|
+
const executionId = await workflow.startWorkflow("approval", { reportId });
|
|
35
35
|
|
|
36
36
|
const invoker = context.getInvoker();
|
|
37
37
|
|
|
@@ -81,7 +81,7 @@ The runtime entry re-exports the following namespaces. Detailed signatures, para
|
|
|
81
81
|
- `secretmanager` — secret-vault access (`getSecret`, `getSecrets`)
|
|
82
82
|
- `authconnection` — OAuth-style connection tokens (`getConnectionToken`)
|
|
83
83
|
- `idp` — IdP user management (`new Client({ namespace })`)
|
|
84
|
-
- `workflow` — workflow & job control (`
|
|
84
|
+
- `workflow` — workflow & job control (`startWorkflow`, `resumeWorkflowExecution`, `startJobFunction`, `wait`, `resolve`)
|
|
85
85
|
- `context` — execution context (`getInvoker`)
|
|
86
86
|
- `file` — `tailordb.file` BLOB API (`upload`, `download`, `downloadAsBase64`, `delete`, `getMetadata`, `downloadStream`, `uploadStream`)
|
|
87
87
|
- `aigateway` — AI Gateway URL resolution (`get`)
|
|
@@ -97,17 +97,16 @@ import { expect, test } from "vitest";
|
|
|
97
97
|
|
|
98
98
|
test("encodes via iconv", () => {
|
|
99
99
|
using iconvM = mockIconv();
|
|
100
|
-
iconvM.
|
|
100
|
+
iconvM.convert.mockReturnValue(new Uint8Array([0x82, 0xa0]));
|
|
101
101
|
|
|
102
102
|
const out = iconv.convert("あ", "UTF-8", "Shift_JIS");
|
|
103
103
|
|
|
104
104
|
expect(out).toEqual(new Uint8Array([0x82, 0xa0]));
|
|
105
|
-
expect(iconvM.
|
|
105
|
+
expect(iconvM.convert).toHaveBeenCalledWith("あ", "UTF-8", "Shift_JIS");
|
|
106
106
|
}); // iconvM disposed here — the iconv mock is removed (previous state restored)
|
|
107
107
|
|
|
108
108
|
test("reads from a vault", async () => {
|
|
109
|
-
using sm = mockSecretmanager();
|
|
110
|
-
sm.setSecrets({ "my-vault": { API_KEY: "sk-123" } });
|
|
109
|
+
using sm = mockSecretmanager({ secrets: { "my-vault": { API_KEY: "sk-123" } } });
|
|
111
110
|
|
|
112
111
|
await expect(secretmanager.getSecret("my-vault", "API_KEY")).resolves.toBe("sk-123");
|
|
113
112
|
});
|
package/docs/services/auth.md
CHANGED
|
@@ -107,7 +107,7 @@ userProfile: {
|
|
|
107
107
|
|
|
108
108
|
**usernameField**: The field in the TailorDB type used as the username. This field must have a unique constraint (`.unique()`) since it is used to uniquely identify users.
|
|
109
109
|
|
|
110
|
-
**attributes**: Specifies which fields from the TailorDB type are used as user attributes. Set to `true` to enable a field.
|
|
110
|
+
**attributes**: Specifies which fields from the TailorDB type are used as user attributes. Set to `true` to enable a field. Machine user definitions must assign values to attributes derived from required fields; attributes derived from optional fields may be omitted (see [Machine Users](#machine-users)). Only fields with ValueOperand types (string, boolean, string[], boolean[]) can be used as attributes. The `id` field and datetime/date/time types are excluded.
|
|
111
111
|
|
|
112
112
|
## Attribute List
|
|
113
113
|
|
|
@@ -200,7 +200,7 @@ machineUsers: {
|
|
|
200
200
|
},
|
|
201
201
|
```
|
|
202
202
|
|
|
203
|
-
**attributes**: Values for attributes enabled in `userProfile.attributes` (or
|
|
203
|
+
**attributes**: Values for attributes enabled in `userProfile.attributes` (or fields defined in `machineUserAttributes` when `userProfile` is omitted). Attribute keys mirror the field's optionality: attributes derived from required fields must be set, while attributes derived from optional fields may be omitted. Setting an attribute to `null` or `undefined` is equivalent to omitting it — the attribute is deployed as unset. These values are accessible via the runtime principal's `attributes`:
|
|
204
204
|
|
|
205
205
|
```typescript
|
|
206
206
|
// In a resolver
|
|
@@ -269,7 +269,7 @@ tailor machineuser token <name>
|
|
|
269
269
|
|
|
270
270
|
### Specifying a machine user invoker
|
|
271
271
|
|
|
272
|
-
Resolvers, executors, and `workflow.
|
|
272
|
+
Resolvers, executors, and `workflow.start()` accept an `invoker` option that chooses which machine user runs the operation. Pass the machine user name as a plain string — it is type-narrowed to the names you registered in `machineUsers`.
|
|
273
273
|
|
|
274
274
|
```typescript
|
|
275
275
|
// tailor.config.ts
|
|
@@ -284,19 +284,19 @@ export const auth = defineAuth("my-auth", {
|
|
|
284
284
|
```
|
|
285
285
|
|
|
286
286
|
```typescript
|
|
287
|
-
// resolvers/
|
|
287
|
+
// resolvers/start-workflow.ts
|
|
288
288
|
import { createResolver, t } from "@tailor-platform/sdk";
|
|
289
289
|
import myWorkflow from "../workflows/my-workflow";
|
|
290
290
|
|
|
291
291
|
export default createResolver({
|
|
292
|
-
name: "
|
|
292
|
+
name: "startMyWorkflow",
|
|
293
293
|
operation: "mutation",
|
|
294
294
|
input: {
|
|
295
295
|
id: t.string(),
|
|
296
296
|
},
|
|
297
297
|
body: async ({ input }) => {
|
|
298
|
-
//
|
|
299
|
-
const workflowRunId = await myWorkflow.
|
|
298
|
+
// Start workflow with machine user permissions
|
|
299
|
+
const workflowRunId = await myWorkflow.start(
|
|
300
300
|
{ id: input.id },
|
|
301
301
|
{ invoker: "admin-machine-user" },
|
|
302
302
|
);
|
|
@@ -329,6 +329,11 @@ createExecutor({
|
|
|
329
329
|
});
|
|
330
330
|
```
|
|
331
331
|
|
|
332
|
+
`args` must match the workflow's main job input. It is required when that input is required
|
|
333
|
+
and can be omitted when the workflow has no input. Static arguments can be JSON-compatible
|
|
334
|
+
primitives, arrays, or plain objects; top-level `null` is not supported. An argument callback
|
|
335
|
+
must return the same input type.
|
|
336
|
+
|
|
332
337
|
### Authentication for Operations
|
|
333
338
|
|
|
334
339
|
GraphQL and Workflow operations can specify an `invoker` to execute with machine user credentials. Pass the machine user name as a plain string — it is type-narrowed to the names defined in your auth config:
|
|
@@ -555,6 +555,48 @@ db.table("User", {
|
|
|
555
555
|
});
|
|
556
556
|
```
|
|
557
557
|
|
|
558
|
+
#### GraphQL Operations
|
|
559
|
+
|
|
560
|
+
Control which GraphQL operations (`create`, `update`, `delete`, `read`) are exposed for a type. All operations are enabled by default.
|
|
561
|
+
|
|
562
|
+
```typescript
|
|
563
|
+
db.type("Order", {
|
|
564
|
+
status: db.string(),
|
|
565
|
+
}).features({
|
|
566
|
+
gqlOperations: {
|
|
567
|
+
delete: false, // Disable the delete mutation
|
|
568
|
+
},
|
|
569
|
+
});
|
|
570
|
+
```
|
|
571
|
+
|
|
572
|
+
Use the `"query"` alias to disable all mutations at once (read-only type: `create`/`update`/`delete` false, `read` true):
|
|
573
|
+
|
|
574
|
+
```typescript
|
|
575
|
+
db.type("AuditLog", {
|
|
576
|
+
action: db.string(),
|
|
577
|
+
}).features({
|
|
578
|
+
gqlOperations: "query",
|
|
579
|
+
});
|
|
580
|
+
```
|
|
581
|
+
|
|
582
|
+
**Namespace-level default**
|
|
583
|
+
|
|
584
|
+
Set a default for every type in a TailorDB namespace in `tailor.config.ts`. A type's own `.features({ gqlOperations })` always takes precedence over this default.
|
|
585
|
+
|
|
586
|
+
```typescript
|
|
587
|
+
// tailor.config.ts
|
|
588
|
+
export default defineConfig({
|
|
589
|
+
db: {
|
|
590
|
+
tailordb: {
|
|
591
|
+
files: ["./tailordb/*.ts"],
|
|
592
|
+
gqlOperations: { delete: false }, // Default for every type in this namespace
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
});
|
|
596
|
+
```
|
|
597
|
+
|
|
598
|
+
This default is re-evaluated on every `tailor-sdk deploy`, so changing it also updates types that already exist on the platform, not only newly created ones.
|
|
599
|
+
|
|
558
600
|
### Field Extraction (`pickFields` / `omitFields`)
|
|
559
601
|
|
|
560
602
|
Extract subsets of fields from a `TailorDBType` for reuse in resolvers, executors, seed schemas, etc.
|
|
@@ -635,6 +677,8 @@ Configure Permission and GQLPermission. For details, see the [TailorDB Permissio
|
|
|
635
677
|
|
|
636
678
|
**Important**: Following the secure-by-default principle, all operations are denied if permissions are not configured. You must explicitly grant permissions for each operation (create, read, update, delete).
|
|
637
679
|
|
|
680
|
+
`generate`/`deploy` reject a type that has no `.permission()`, or no `.gqlPermission()` while GraphQL operations are enabled for it (see [GraphQL Operations](#graphql-operations) above). Disable GraphQL exposure entirely with `.features({ gqlOperations: { create: false, update: false, delete: false, read: false } })` if a type only needs record-level permission.
|
|
681
|
+
|
|
638
682
|
```typescript
|
|
639
683
|
db.table("User", {
|
|
640
684
|
name: db.string(),
|
|
@@ -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.
|
|
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
|
|