From 45e50ecc96ed6bd7c4bbc0e2ec1de79eb9116137 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Fri, 24 Jul 2026 11:56:33 +0900 Subject: [PATCH 1/4] docs(sdk): update hand-written v1 samples for SDK v2 breaking changes Update code samples in getting-started/guides/tutorials/administration/ reference to match the SDK v2 API ahead of tailor-platform/sdk#1808 (v2 release line merge into main). docs/sdk/ and docs/app-shell/ are auto-synced and untouched. - db.type() -> db.table() - CLI binary tailor-sdk -> tailor - tailor.workflow.triggerWorkflow/triggerJobFunction -> startWorkflow/startJobFunction - tailor.workflow.resumeWorkflow -> resumeWorkflowExecution - resolver context.user -> context.caller; executor context.user -> context.invoker (TailorUser/TailorActor/TailorInvoker unified into TailorPrincipal) - TailorDB hooks/validators: {value,data,user} -> {input,invoker,now,oldValue}; type-level hooks take a single object instead of a per-field map; validators return string | void instead of boolean/tuple - Wait/Resolve guide rewritten around createWaitPoint/createWaitPoints - Removed references to the deleted openDownloadStream API - Noted the new @tailor-platform/sdk/runtime/globals opt-in import --- docs/administration/workspace.md | 12 +- docs/getting-started/console/overview.md | 6 +- .../core-concepts/platform-architecture.md | 2 +- .../core-concepts/workspace-application.md | 4 +- docs/getting-started/graphql.md | 2 +- docs/getting-started/index.md | 2 +- docs/getting-started/quickstart.md | 6 +- docs/guides/auth/app-login.md | 10 +- docs/guides/auth/authconnection.md | 42 ++-- docs/guides/auth/overview.md | 8 +- docs/guides/function/accessing-tailordb.md | 15 +- docs/guides/function/builtin-interfaces.md | 25 +-- docs/guides/function/debugging.md | 6 +- docs/guides/function/managing-idp-users.md | 2 + docs/guides/function/overview.md | 22 +- docs/guides/integration/claude.md | 2 +- docs/guides/integration/loopreturns.md | 4 +- docs/guides/integration/quickbooks.md | 4 +- docs/guides/integration/shipstation.md | 4 +- docs/guides/integration/shopify.md | 10 +- docs/guides/resolver.md | 14 +- docs/guides/secretmanager.md | 10 +- docs/guides/static-website-hosting.md | 12 +- .../advanced-settings/vector-search.md | 2 +- docs/guides/tailordb/fields.md | 4 +- docs/guides/tailordb/file-type.md | 2 +- docs/guides/tailordb/hooks.md | 148 +++++++------ docs/guides/tailordb/indexes.md | 2 +- docs/guides/tailordb/overview.md | 10 +- docs/guides/tailordb/permission.md | 8 +- docs/guides/tailordb/relationships.md | 8 +- docs/guides/tailordb/validations.md | 59 ++--- docs/guides/tailordb/versioning.md | 4 +- docs/guides/workflow/creating-workflows.md | 8 +- docs/guides/workflow/index.md | 18 +- docs/guides/workflow/monitoring-executions.md | 38 ++-- docs/guides/workflow/triggering-workflow.md | 18 +- docs/guides/workflow/wait-resolve.md | 204 ++++++++++-------- docs/reference/api/api-references.md | 6 +- .../tutorials/develop-from-scratch/step-01.md | 10 +- .../manage-data-schema/create-data-schema.md | 2 +- .../manage-data-schema/data-schema-basics.md | 2 +- .../manage-data-schema/modify-data-schema.md | 2 +- .../manage-data-schema/validate-field-data.md | 26 +-- docs/tutorials/resolver.md | 6 +- .../setup-auth/login/create-oauth2-client.md | 4 +- .../tutorials/setup-auth/login/create-user.md | 2 +- .../setup-auth/register-identity-provider.md | 6 +- .../setup-auth/setup-auth-connections.md | 22 +- .../incoming-webhook-trigger.md | 2 +- 50 files changed, 442 insertions(+), 405 deletions(-) diff --git a/docs/administration/workspace.md b/docs/administration/workspace.md index 2d9fbce..983381a 100644 --- a/docs/administration/workspace.md +++ b/docs/administration/workspace.md @@ -5,10 +5,10 @@ containing all applications, including each Tailor Platform service. Since changing workspace settings affects all the applications within the workspace, only [Platform users](#what-is-the-platform-user) have permission to manage workspaces. -You can use the interactive mode of `tailor-sdk` to view all the options available for workspace management by running the following command. +You can use the interactive mode of `tailor` to view all the options available for workspace management by running the following command. ```bash -tailor-sdk workspace +tailor workspace Commands: workspace app Manage workspace applications workspace create Create a new Tailor Platform workspace. @@ -28,14 +28,14 @@ By default, a maximum of 10 workspaces can be created per organization. The Platform user is the user who can log in to Tailor Platform using the following command: ```bash -tailor-sdk login +tailor login ``` When you sign up for an account, we create a Platform user with an admin role for you to manage your workspace. Please note that the [users you add to your application](/tutorials/setup-auth/login/create-user) are not Platform users and therefore cannot manage your workspace.\ To add a new Platform user, you can invite anyone with a Tailor Platform account to your workspace using the following command: ```bash -tailor-sdk workspace user invite --email $userEmailAddress --role $(admin|editor|viewer) +tailor workspace user invite --email $userEmailAddress --role $(admin|editor|viewer) ``` Depending on the role assigned, the Platform user will have different permissions to manage workspaces. @@ -81,13 +81,13 @@ The `viewer` role can only view all application and workspace settings. Platform users with an `admin` role can destroy a workspace using the following command: ```bash -tailor-sdk workspace delete -w {WORKSPACE_ID} +tailor workspace delete -w {WORKSPACE_ID} ``` If you need to restore a destroyed workspace, you can do so within 2 weeks of deletion using the following command: ```bash -tailor-sdk workspace restore -w {WORKSPACE_ID} +tailor workspace restore -w {WORKSPACE_ID} ``` After 2 weeks, all data associated with the workspace will be permanently removed and cannot be recovered. diff --git a/docs/getting-started/console/overview.md b/docs/getting-started/console/overview.md index 682fd31..2a8ae11 100644 --- a/docs/getting-started/console/overview.md +++ b/docs/getting-started/console/overview.md @@ -54,10 +54,10 @@ You can confirm that the input data aligns with the validation rules defined in #### 1. Set Up Your Workspace -Configure your workspace to match your deployed application. You can view and manage workspaces using the `tailor-sdk` CLI. +Configure your workspace to match your deployed application. You can view and manage workspaces using the `tailor` CLI. ```bash -tailor-sdk workspace +tailor workspace ``` #### 2. Update the configuration file @@ -67,7 +67,7 @@ For example, to modify the `User` type with a new field, update your TailorDB ty ```typescript import { db } from "@tailor-platform/sdk"; -export const user = db.type("User", "User of the system", { +export const user = db.table("User", "User of the system", { name: db.string(), email: db.string().unique(), // Add new field diff --git a/docs/getting-started/core-concepts/platform-architecture.md b/docs/getting-started/core-concepts/platform-architecture.md index 3e24479..92d8dc7 100644 --- a/docs/getting-started/core-concepts/platform-architecture.md +++ b/docs/getting-started/core-concepts/platform-architecture.md @@ -18,7 +18,7 @@ A [Workspace](/administration/workspace) is the top-level namespace in the Tailo With the SDK, you can create a workspace using the CLI: ```bash -npx tailor-sdk workspace create --name my-workspace --region us-west +npx tailor workspace create --name my-workspace --region us-west ``` ### Application diff --git a/docs/getting-started/core-concepts/workspace-application.md b/docs/getting-started/core-concepts/workspace-application.md index 29b5c3e..6576ec6 100644 --- a/docs/getting-started/core-concepts/workspace-application.md +++ b/docs/getting-started/core-concepts/workspace-application.md @@ -12,10 +12,10 @@ Using the SDK CLI: ```bash # Create a new workspace -npx tailor-sdk workspace create --name my-workspace --region us-west +npx tailor workspace create --name my-workspace --region us-west # List your workspaces -npx tailor-sdk workspace list +npx tailor workspace list ``` Or create one through the [Console](https://console.tailor.tech). diff --git a/docs/getting-started/graphql.md b/docs/getting-started/graphql.md index 3ea84ad..4432a14 100644 --- a/docs/getting-started/graphql.md +++ b/docs/getting-started/graphql.md @@ -355,7 +355,7 @@ Using `groupBy` with aggregation queries in GraphQL allows you to perform calcul To use the aggregation query, you'll need to enable advanced APIs. For example, to enable aggregation on the `Order` type ```typescript -db.type("Order", { +db.table("Order", { name: db.string(), // ... other fields }).features({ diff --git a/docs/getting-started/index.md b/docs/getting-started/index.md index 624c7b0..b3b8e5e 100644 --- a/docs/getting-started/index.md +++ b/docs/getting-started/index.md @@ -15,7 +15,7 @@ Here is a quick look at the console. ## Tailor Platform SDK -The `tailor-sdk` is the command-line interface for building, deploying, and managing applications on the Tailor Platform. It provides a comprehensive set of tools for working with TailorDB, Functions, Workflows and Auth. +The `tailor` is the command-line interface for building, deploying, and managing applications on the Tailor Platform. It provides a comprehensive set of tools for working with TailorDB, Functions, Workflows and Auth. ### Installation diff --git a/docs/getting-started/quickstart.md b/docs/getting-started/quickstart.md index a4df6ce..91cd3c0 100644 --- a/docs/getting-started/quickstart.md +++ b/docs/getting-started/quickstart.md @@ -25,9 +25,9 @@ npm create @tailor-platform/sdk example-app --template hello-world Before deploying your app, you need to create a workspace: ```bash -npx tailor-sdk login -npx tailor-sdk workspace create --name --region -npx tailor-sdk workspace list +npx tailor login +npx tailor workspace create --name --region +npx tailor workspace list # OR # Create a new workspace using Tailor Platform Console diff --git a/docs/guides/auth/app-login.md b/docs/guides/auth/app-login.md index 83fe838..94ffb13 100644 --- a/docs/guides/auth/app-login.md +++ b/docs/guides/auth/app-login.md @@ -14,7 +14,7 @@ After integrating your Identity Provider (IdP) with the Auth service, a user mus 1. **User Profile Management**: How Tailor Platform connects authentication with TailorDB user profiles 2. **OAuth2 Client Configuration**: How Tailor Platform's auth service acts as a standalone authentication service -3. **Login Process**: Using OAuth2 flows with `tailor-sdk` +3. **Login Process**: Using OAuth2 flows with `tailor` ## Authentication Architecture @@ -173,17 +173,17 @@ Browser clients provide enhanced security for SPAs through multiple mechanisms: ## Part 3: Login Process -### Using `tailor-sdk` for Testing +### Using `tailor` for Testing -The `tailor-sdk` command provides a convenient way to test your OAuth2 configuration: +The `tailor` command provides a convenient way to test your OAuth2 configuration: ```bash -tailor-sdk login +tailor login ``` **What happens during this flow:** -1. **OAuth2 Authorization**: `tailor-sdk` initiates the OAuth2 authorization code flow +1. **OAuth2 Authorization**: `tailor` initiates the OAuth2 authorization code flow 2. **IdP Redirect**: Your browser opens to Tailor Platform's auth service, which redirects to your configured IdP 3. **User Authentication**: You authenticate with your IdP (Auth0, Okta, etc.) 4. **Profile Lookup**: Tailor Platform looks up your user profile in TailorDB using the email from your IdP diff --git a/docs/guides/auth/authconnection.md b/docs/guides/auth/authconnection.md index a0c0605..cde587f 100644 --- a/docs/guides/auth/authconnection.md +++ b/docs/guides/auth/authconnection.md @@ -72,7 +72,7 @@ Store the client ID and secret in your `.env` file or CI secrets — never commi Deploy to register the connection with the platform: ```bash -tailor-sdk deploy +tailor deploy ``` This creates (or updates) the connection record. A newly created connection exists but is not yet authorized — it has no tokens yet. @@ -94,7 +94,7 @@ This creates (or updates) the connection record. A newly created connection exis #### 2. Authorize the Connection ```bash -tailor-sdk authconnection authorize --name google-connection \ +tailor authconnection authorize --name google-connection \ --scopes "openid,profile,email" \ --port 8080 ``` @@ -114,16 +114,16 @@ This command: 4. Exchanges the authorization code for tokens using the client secret from your config 5. Stores tokens securely on the server -Alternatively, run `tailor-sdk authconnection open` to authorize the connection from the Console instead of the local CLI flow — useful on a machine without browser access: +Alternatively, run `tailor authconnection open` to authorize the connection from the Console instead of the local CLI flow — useful on a machine without browser access: ```bash -tailor-sdk authconnection open +tailor authconnection open ``` Verify the connection is authorized: ```bash -tailor-sdk authconnection list +tailor authconnection list ``` ### Option B: Manage Entirely via the Console @@ -131,7 +131,7 @@ tailor-sdk authconnection list No `tailor.config.ts` changes are needed — create, authorize, and manage the connection directly in the Tailor Platform Console: ```bash -tailor-sdk authconnection open +tailor authconnection open ``` This opens the workspace's connections settings page, where you can: @@ -146,8 +146,8 @@ This is a good fit for connections you don't want tracked in git — for example A connection name is owned by exactly one side at a time — the same connection cannot be managed by both the Console and your SDK config simultaneously: -- A connection created via the Console (Option B) carries no SDK ownership label. `tailor-sdk deploy` leaves it completely untouched as long as it isn't also declared in `connections`. -- If you later add a connection with the **same name** to `defineAuth()`'s `connections` and run `deploy`, the SDK finds it already exists without an ownership label and pauses to ask whether it should take it over ("Allow tailor-sdk to manage these resources?"). Confirming adopts it into config management — from that point on, every `deploy` overwrites its fields to match your config, and removing it from `connections` deletes the connection. (`--yes` confirms automatically, which is useful for CI but means this adoption happens without a prompt.) +- A connection created via the Console (Option B) carries no SDK ownership label. `tailor deploy` leaves it completely untouched as long as it isn't also declared in `connections`. +- If you later add a connection with the **same name** to `defineAuth()`'s `connections` and run `deploy`, the SDK finds it already exists without an ownership label and pauses to ask whether it should take it over ("Allow tailor to manage these resources?"). Confirming adopts it into config management — from that point on, every `deploy` overwrites its fields to match your config, and removing it from `connections` deletes the connection. (`--yes` confirms automatically, which is useful for CI but means this adoption happens without a prompt.) - Until you confirm that handover, `deploy` refuses to proceed, so a Console-managed connection is never silently overwritten by config just because a connection with the same name appears in `tailor.config.ts`. In short: use the Console (Option B) for connections you intend to manage by hand, and SDK config (Option A) for connections you want defined, reviewed, and deployed alongside the rest of your app. Don't mix the two for the same connection name. @@ -176,10 +176,10 @@ connections: { ```bash # 1. Deploy the connection -tailor-sdk deploy +tailor deploy # 2. Authorize and get tokens -tailor-sdk authconnection authorize --name google-oauth \ +tailor authconnection authorize --name google-oauth \ --scopes "https://www.googleapis.com/auth/admin.directory.user.readonly" ``` @@ -212,10 +212,10 @@ connections: { ```bash # 1. Deploy the connection -tailor-sdk deploy +tailor deploy # 2. Authorize and get tokens -tailor-sdk authconnection authorize --name ms365-oauth \ +tailor authconnection authorize --name ms365-oauth \ --scopes "https://graph.microsoft.com/.default" ``` @@ -249,7 +249,7 @@ connections: { ``` ```bash -tailor-sdk deploy +tailor deploy ``` **Common QuickBooks OAuth2 URLs:** @@ -301,7 +301,7 @@ export default async () => { // authconnection.getConnectionToken("unknown-connection"); // ❌ TypeScript error ``` -Type narrowing is provided by the generated `tailor.d.ts` (the `ConnectionNameRegistry` interface). Run `tailor-sdk generate` (or `deploy`) after defining new connections to refresh it. Before the first generate, or when `connections` is not defined in `defineAuth()`, `getConnectionToken()` accepts any string. +Type narrowing is provided by the generated `tailor.d.ts` (the `ConnectionNameRegistry` interface). Run `tailor generate` (or `deploy`) after defining new connections to refresh it. Before the first generate, or when `connections` is not defined in `defineAuth()`, `getConnectionToken()` accepts any string. ### Advanced Usage with Error Handling @@ -355,19 +355,19 @@ These commands work on any connection regardless of which option created it: ```bash # Open the connections page in the Console -tailor-sdk authconnection open +tailor authconnection open # Authorize (opens browser for OAuth2 flow) -tailor-sdk authconnection authorize --name +tailor authconnection authorize --name # List all connections -tailor-sdk authconnection list +tailor authconnection list # Revoke a connection's tokens (keeps the connection; re-authorize later) -tailor-sdk authconnection revoke --name +tailor authconnection revoke --name # Delete a connection entirely -tailor-sdk authconnection delete --name +tailor authconnection delete --name ``` To delete a connection managed via SDK config (Option A), remove it from `connections` and redeploy instead of using `authconnection delete` directly — manage each connection through one method only, as described above. @@ -408,7 +408,7 @@ connections: { ``` ```bash -tailor-sdk deploy -w --env-file .env.production +tailor deploy -w --env-file .env.production ``` ## Troubleshooting @@ -436,7 +436,7 @@ tailor-sdk deploy -w --env-file .env.production **Re-authorization Required After Deploy** -- If `tailor-sdk deploy` warns that a connection needs re-authorization, run `tailor-sdk authconnection authorize --name ` again +- If `tailor deploy` warns that a connection needs re-authorization, run `tailor authconnection authorize --name ` again - This happens when identity-changing fields (`providerUrl`, `issuerUrl`, `clientId`, `type`) are modified **Function Runtime Errors** diff --git a/docs/guides/auth/overview.md b/docs/guides/auth/overview.md index caefe0a..ff39ca5 100644 --- a/docs/guides/auth/overview.md +++ b/docs/guides/auth/overview.md @@ -52,7 +52,7 @@ Maps authenticated identities to a TailorDB type: // tailordb/user.ts import { db } from "@tailor-platform/sdk"; -export const user = db.type("User", { +export const user = db.table("User", { email: db.string().unique(), // usernameField must have unique constraint role: db.enum(["admin", "user"]), ...db.fields.timestamps(), @@ -78,7 +78,7 @@ machineUsers: { Get a machine user token using the CLI: ```bash -tailor-sdk machineuser token +tailor machineuser token ``` ### OAuth 2.0 Clients @@ -364,7 +364,7 @@ const auth = defineAuth("my-auth", { After adding the Machine user, run the following command to get the access token. ```bash -tailor-sdk machineuser token {MACHINE_USER_NAME} +tailor machineuser token {MACHINE_USER_NAME} ``` Once you get an access token, you can use it in the playground to run queries. @@ -376,7 +376,7 @@ You can use the machine user's credentials in the client application to authenti Run the following command to view the machine user credentials. ```bash -tailor-sdk machineuser list +tailor machineuser list ``` #### Request an Access Token diff --git a/docs/guides/function/accessing-tailordb.md b/docs/guides/function/accessing-tailordb.md index 93918fa..234c53b 100644 --- a/docs/guides/function/accessing-tailordb.md +++ b/docs/guides/function/accessing-tailordb.md @@ -8,6 +8,8 @@ In order to access TailorDB from Function service, you need to instantiate the ` `tailordb` exists in the Function service environment by default, so you can use it without any additional installation. Please specify the `namespace` of the TailorDB you want to connect to when creating the `Client` object. +Importing from `@tailor-platform/sdk` no longer activates the ambient `tailordb.*` global automatically. Add `import "@tailor-platform/sdk/runtime/globals"` as a side-effect import in your function file (or use the typed wrapper from `@tailor-platform/sdk/runtime` instead) — see [Runtime](/sdk/runtime) for details. + Here is the `Client` interface: ```ts @@ -177,7 +179,7 @@ To execute the function, you need to set up the Product table in TailorDB. ```typescript {{ title: 'product.ts' }} import { db, auth } from "@tailor-platform/sdk"; -export const product = db.type("Product", { +export const product = db.table("Product", { title: db.string().description("Title of the product"), ...db.fields.timestamps(), }); @@ -750,10 +752,6 @@ const { metadata } = await tailordb.file.uploadStream( ); ``` -::: warning Deprecated: openDownloadStream -`openDownloadStream()` is deprecated. Use `downloadStream()` instead, which returns a standard Web `ReadableStream` rather than a custom async iterator. -::: - ### Delete Files Delete a file attached to a record. This removes the file but does not delete the record itself. @@ -846,13 +844,6 @@ tailordb.file.uploadStream( options?: { contentType?: string; fileSize?: number } ): Promise<{ metadata: UploadMetadata }> -tailordb.file.openDownloadStream( // deprecated — use downloadStream() - namespace: string, - typeName: string, - fieldName: string, - recordId: string -): Promise> - tailordb.file.delete( namespace: string, typeName: string, diff --git a/docs/guides/function/builtin-interfaces.md b/docs/guides/function/builtin-interfaces.md index 0451d24..184dc00 100644 --- a/docs/guides/function/builtin-interfaces.md +++ b/docs/guides/function/builtin-interfaces.md @@ -9,6 +9,8 @@ These provide direct access to platform services without external HTTP calls or All interfaces are typed via [`@tailor-platform/function-types`](https://github.com/tailor-platform/function/tree/main/packages/types) — install the package to get full TypeScript support. +Importing from `@tailor-platform/sdk` no longer activates these ambient `tailor.*` / `tailordb.*` globals automatically. Add `import "@tailor-platform/sdk/runtime/globals"` as a side-effect import in your function file (or use the typed wrappers from `@tailor-platform/sdk/runtime` instead) — see [Runtime](/sdk/runtime) for details. + ## Overview | Service | Description | @@ -17,7 +19,7 @@ All interfaces are typed via [`@tailor-platform/function-types`](https://github. | [Secret Manager](#secret-manager) | Retrieve secrets from vaults | | [Auth Connection](#auth-connection) | Get access tokens for external auth connections | | [Character Encoding](#character-encoding) | Convert between character encodings (iconv) | -| [Workflow](#workflow) | Trigger workflows and job functions | +| [Workflow](#workflow) | Start workflows and job functions | | [TailorDB Client](#tailordb-client) | Execute SQL queries against TailorDB | | [TailorDB File](#tailordb-file) | Upload, download, and manage files in TailorDB | @@ -142,16 +144,16 @@ const result = converter.convert(inputBuffer); **Interface**: `tailor.workflow` -Trigger workflows and job functions from within a function. +Start workflows and job functions from within a function. ```typescript -// Trigger a workflow -const executionId = await tailor.workflow.triggerWorkflow("processOrder", { +// Start a workflow +const executionId = await tailor.workflow.startWorkflow("processOrder", { orderId: "order-123", }); -// Trigger with a specific machine user -const executionId = await tailor.workflow.triggerWorkflow( +// Start with a specific machine user +const executionId = await tailor.workflow.startWorkflow( "processOrder", { orderId: "order-123" }, { @@ -162,14 +164,14 @@ const executionId = await tailor.workflow.triggerWorkflow( }, ); -// Trigger a job function -const result = await tailor.workflow.triggerJobFunction("calculateTax", { +// Start a job function +const result = await tailor.workflow.startJobFunction("calculateTax", { amount: 1000, }); // Route the dispatch through a workspace-registered execution policy for // per-key concurrency control (see the SDK Workflow guide for policy setup). -const scoped = await tailor.workflow.triggerJobFunction( +const scoped = await tailor.workflow.startJobFunction( "syncTenant", { tenantId: "acme" }, { executionPolicyKey: `tenant-api.acme` }, @@ -178,8 +180,8 @@ const scoped = await tailor.workflow.triggerJobFunction( | Function | Returns | Description | | --- | --- | --- | -| `triggerWorkflow(name, args?, options?)` | `Promise` | Trigger a workflow. Returns the execution ID | -| `triggerJobFunction(name, args?, options?)` | `Promise` | Trigger a job function and return its result. `options.executionPolicyKey` routes the dispatch through a matching execution policy for per-key concurrency control | +| `startWorkflow(name, args?, options?)` | `Promise` | Start a workflow. Returns the execution ID | +| `startJobFunction(name, args?, options?)` | `Promise` | Start a job function and return its result. `options.executionPolicyKey` routes the dispatch through a matching execution policy for per-key concurrency control | For details on declaring execution policies and the key grammar, see [Execution Policies](/sdk/services/workflow#execution-policies) in the SDK Workflow reference. @@ -280,4 +282,3 @@ await tailordb.file.delete("my-namespace", "Document", "attachment", recordId); | `getMetadata(...)` | `Promise` | Get file metadata without downloading | | `downloadStream(...)` | `Promise` | Download a file as a `ReadableStream` | | `uploadStream(..., readableStream, options?)` | `Promise` | Upload a file using a `ReadableStream` | -| `openDownloadStream(...)` | `Promise` | **Deprecated.** Use `downloadStream()` instead | diff --git a/docs/guides/function/debugging.md b/docs/guides/function/debugging.md index 061fd78..8a18342 100644 --- a/docs/guides/function/debugging.md +++ b/docs/guides/function/debugging.md @@ -39,14 +39,14 @@ After deploying the function with [these steps](/guides/function/sending-request ## Viewing Function Logs -Use the `tailor-sdk function logs` command to view function execution logs: +Use the `tailor function logs` command to view function execution logs: ```bash {{ title: 'List all function logs' }} -tailor-sdk function logs +tailor function logs ``` ```bash {{ title: 'View specific execution logs' }} -tailor-sdk function logs +tailor function logs ``` Example output: diff --git a/docs/guides/function/managing-idp-users.md b/docs/guides/function/managing-idp-users.md index d605ad2..80d5df4 100644 --- a/docs/guides/function/managing-idp-users.md +++ b/docs/guides/function/managing-idp-users.md @@ -14,6 +14,8 @@ The `tailor.idp` namespace provides a `Client` class that you can use to interac This feature requires a configured Built-in IdP service. For information on setting up Built-in IdP, see [Built-in IdP](/guides/auth/integration/built-in-idp). ::: +Importing from `@tailor-platform/sdk` no longer activates the ambient `tailor.*` global automatically. Add `import "@tailor-platform/sdk/runtime/globals"` as a side-effect import in your function file (or use the typed wrapper from `@tailor-platform/sdk/runtime` instead) — see [Runtime](/sdk/runtime) for details. + ## Client Interface The `tailor.idp.Client` class provides the following interface: diff --git a/docs/guides/function/overview.md b/docs/guides/function/overview.md index dcc5da8..8c0f5db 100644 --- a/docs/guides/function/overview.md +++ b/docs/guides/function/overview.md @@ -133,21 +133,21 @@ query { Function operations are subject to a recursive call depth limit of 10 levels when calling other platform services. See [Platform Limits](/reference/platform/platform-limits#recursive-call-detection) for more details. -## Using the "user" variable +## Using the principal (`caller` / `invoker`) -When using the Function service with Pipeline resolvers or Executors, you can access the current user's information using the `user` variable. This allows you to incorporate user-specific data into your functions. +When using the Function service with Pipeline resolvers or Executors, you can access the current principal — a `TailorPrincipal` — from the body function. Resolvers expose it as `caller`; `function`/`jobFunction` executor operations expose it as `invoker`. Both are `null` for anonymous calls. -The `user` variable has the following properties: +`TailorPrincipal` has the following properties: -- `id`: The ID of the current user -- `type`: The type of the user -- `workspace_id`: The ID of the workspace the user belongs to -- `attributes`: The attributes of the user -- `tenant_id`: The ID of the tenant the user belongs to +- `id`: The ID of the principal +- `type`: `"user"` or `"machine_user"` +- `workspaceId`: The ID of the workspace the principal belongs to +- `attributes`: A map of the principal's attributes +- `attributeList`: A list of the principal's attribute IDs ### Resolver Example -In SDK, you can access the current user's information via `context.user` in the body function: +In SDK, you can access the current caller's information via `context.caller` in the body function: ```typescript {{ title: 'function-sample-hello.ts' }} import { createResolver, t } from "@tailor-platform/sdk"; @@ -161,7 +161,7 @@ export default createResolver({ }, body: (context) => { return { - message: context.user.id, + message: context.caller?.id, }; }, output: t.object({ @@ -183,7 +183,7 @@ export default createExecutor({ kind: "function", body: async (context) => { return { - message: "hi " + context.user.id, + message: "hi " + context.invoker?.id, }; }, }, diff --git a/docs/guides/integration/claude.md b/docs/guides/integration/claude.md index 1026618..bb163a7 100644 --- a/docs/guides/integration/claude.md +++ b/docs/guides/integration/claude.md @@ -64,7 +64,7 @@ npm run deploy -- --workspace-id After deployment, look up the issued `CLIENT_ID` with the SDK CLI: ```bash -tailor-sdk oauth2client list +tailor oauth2client list ``` ```bash diff --git a/docs/guides/integration/loopreturns.md b/docs/guides/integration/loopreturns.md index 3387e1f..5489b4d 100644 --- a/docs/guides/integration/loopreturns.md +++ b/docs/guides/integration/loopreturns.md @@ -62,8 +62,8 @@ Store your Loop API key as a secret in the Tailor PF using one of the following Run the following commands to create a vault named loop-vault and to store the secret key. ```bash -tailor-sdk secret vault create loop-vault -tailor-sdk secret create --vault-name loop-vault --name loop-key --value {$api_key} +tailor secret vault create loop-vault +tailor secret create --vault-name loop-vault --name loop-key --value {$api_key} ``` #### Through the Console diff --git a/docs/guides/integration/quickbooks.md b/docs/guides/integration/quickbooks.md index 1e3b47a..4f0598a 100644 --- a/docs/guides/integration/quickbooks.md +++ b/docs/guides/integration/quickbooks.md @@ -78,8 +78,8 @@ Store your QuickBooks access token as a secret in the Tailor PF using one of the Run the following commands to create a vault named quickbooks-vault and to store the secret key. ```bash -tailor-sdk secret vault create quickbooks-vault -tailor-sdk secret create --vault-name quickbooks-vault --name quickbooks-key --value {$access_token} +tailor secret vault create quickbooks-vault +tailor secret create --vault-name quickbooks-vault --name quickbooks-key --value {$access_token} ``` #### Through the Console diff --git a/docs/guides/integration/shipstation.md b/docs/guides/integration/shipstation.md index 4f69472..cefc0df 100644 --- a/docs/guides/integration/shipstation.md +++ b/docs/guides/integration/shipstation.md @@ -51,8 +51,8 @@ Store your ShipStation API key as a secret in the Tailor PF using one of the fol Run the following commands to create a vault named shipstation-vault and to store the secret key. ```bash -tailor-sdk secret vault create shipstation-vault -tailor-sdk secret create --vault-name shipstation-vault --name shipstation-key --value {$api_key} +tailor secret vault create shipstation-vault +tailor secret create --vault-name shipstation-vault --name shipstation-key --value {$api_key} ``` #### Through the Console diff --git a/docs/guides/integration/shopify.md b/docs/guides/integration/shopify.md index e3c5332..d6085a2 100644 --- a/docs/guides/integration/shopify.md +++ b/docs/guides/integration/shopify.md @@ -78,8 +78,8 @@ export const product = db Run the following commands to create a vault named `shopify-vault` and to store the secret key. ```bash -tailor-sdk secret vault create shopify-vault -tailor-sdk secret create --vault-name shopify-vault --name shopify-key --value {$your_API_key} +tailor secret vault create shopify-vault +tailor secret create --vault-name shopify-vault --name shopify-key --value {$your_API_key} ``` 3. Add the trigger @@ -193,7 +193,7 @@ npx tailor deploy After deployment, get your webhook URL: ```bash -tailor-sdk executor list +tailor executor list ``` You will see the webhook URL in the output which you'll use to configure Shopify. @@ -203,7 +203,7 @@ You will see the webhook URL in the output which you'll use to configure Shopify Run the command below to get the endpoint for configuring Shopify. ```bash -tailor-sdk executor webhook list +tailor executor webhook list ``` You will receive the following URL in response. @@ -233,7 +233,7 @@ You can open the GraphQL playground to verify the product created in Shopify is ```bash # Open the GraphQL playground in the browser -tailor-sdk open +tailor open ``` Run the following query to view all the products. diff --git a/docs/guides/resolver.md b/docs/guides/resolver.md index 623325d..84a179a 100644 --- a/docs/guides/resolver.md +++ b/docs/guides/resolver.md @@ -134,8 +134,8 @@ The `body` function receives a `context` object with: | Property | Description | | --------------- | ----------------------------------------------- | -| `context.input` | The validated input arguments | -| `context.user` | Current user information (id, attributes, etc.) | +| `context.input` | The validated input arguments | +| `context.caller` | The user or machine user who called the resolver, or `null` for anonymous calls | **Example** @@ -373,7 +373,7 @@ body: async (context) => { In the SDK, the `context` object is passed to the `body` function and provides access to: - `context.input` - The validated input arguments -- `context.user` - Current user information (id, attributes, workspace_id, etc.) +- `context.caller` - The user or machine user who called the resolver (id, attributes, workspaceId, etc.), or `null` for anonymous calls Data from previous operations is simply stored in TypeScript variables: @@ -417,13 +417,13 @@ export default createResolver({ }); ``` -Access user context for authorization: +Access caller context for authorization: ```typescript body: async (context) => { - // Access current user information - const currentUserId = context.user.id; - const userAttributes = context.user.attributes; + // Access current caller information (null for anonymous calls) + const currentUserId = context.caller?.id; + const userAttributes = context.caller?.attributes; // Use in your logic return { userId: currentUserId }; diff --git a/docs/guides/secretmanager.md b/docs/guides/secretmanager.md index 1b54e04..7ffddc4 100644 --- a/docs/guides/secretmanager.md +++ b/docs/guides/secretmanager.md @@ -56,18 +56,18 @@ resource "tailor_secretmanager_secret" "my-secret" { - **value_wo**:(String) The actual secret value (write-only for security) - **value_wo_version**:(Number) Version number for the secret value -You can also manage secrets using the tailor-sdk command-line interface: +You can also manage secrets using the tailor command-line interface: ```bash # Create a vault -tailor-sdk secret vault create default +tailor secret vault create default # Create a secret in the vault -tailor-sdk secret create --vault-name default --name my-secret --value my-secret-value +tailor secret create --vault-name default --name my-secret --value my-secret-value ``` -- `tailor-sdk secret vault create`: Creates a new vault in the current workspace (vault name as positional argument) -- `tailor-sdk secret create`: Adds a new secret to an existing vault +- `tailor secret vault create`: Creates a new vault in the current workspace (vault name as positional argument) +- `tailor secret create`: Adds a new secret to an existing vault - Use `--vault-name` to specify the target vault name - Use `--name` to set the secret identifier - Use `--value` to provide the secret value diff --git a/docs/guides/static-website-hosting.md b/docs/guides/static-website-hosting.md index b70908c..d87b287 100644 --- a/docs/guides/static-website-hosting.md +++ b/docs/guides/static-website-hosting.md @@ -31,7 +31,7 @@ Deploy pre-built static files using TailorCLI. The service is designed for singl ```bash {{ title: "Deploy Pre-built Files" }} # Deploy your build output directory -tailor-sdk staticwebsite deploy \ +tailor staticwebsite deploy \ --name my-spa \ --dir ./dist \ --workspace_id @@ -76,7 +76,7 @@ There are two ways to add custom domains to a static website. #### SDK Configuration -Add the `customDomains` field to your `defineStaticWebSite()` definition. Domains are provisioned automatically when you run `tailor-sdk deploy`. +Add the `customDomains` field to your `defineStaticWebSite()` definition. Domains are provisioned automatically when you run `tailor deploy`. ```typescript {{ title: "Static Website with Custom Domains" }} import { defineStaticWebSite } from "@tailor-platform/sdk"; @@ -92,7 +92,7 @@ export const mySpa = defineStaticWebSite("my-spa", { Use the API command to add a custom domain directly. ```bash {{ title: "Add a Custom Domain" }} -tailor-sdk api AddCustomDomain --body '{ +tailor api AddCustomDomain --body '{ "workspace_id": "", "static_website_name": "my-spa", "domain": "app.example.com" @@ -103,15 +103,15 @@ You can list and inspect custom domains with dedicated subcommands: ```bash {{ title: "List and Get Custom Domains" }} # List all custom domains for a static website -tailor-sdk staticwebsite domain list my-spa +tailor staticwebsite domain list my-spa # Get details of a specific custom domain -tailor-sdk staticwebsite domain get app.example.com +tailor staticwebsite domain get app.example.com ``` ### DNS Configuration -After adding a custom domain, configure CNAME records with your DNS provider. The required targets are returned in the response and can also be retrieved with `tailor-sdk staticwebsite domain get`. +After adding a custom domain, configure CNAME records with your DNS provider. The required targets are returned in the response and can also be retrieved with `tailor staticwebsite domain get`. | Record Type | Host | Target | Purpose | | ----------- | ---- | ------ | ------- | diff --git a/docs/guides/tailordb/advanced-settings/vector-search.md b/docs/guides/tailordb/advanced-settings/vector-search.md index befea55..0fbea63 100644 --- a/docs/guides/tailordb/advanced-settings/vector-search.md +++ b/docs/guides/tailordb/advanced-settings/vector-search.md @@ -11,7 +11,7 @@ Currently, Vector Search can only be enabled for `#TypeString` and cannot be use ```typescript import { db } from "@tailor-platform/sdk"; -export const product = db.type("Product", "Product model", { +export const product = db.table("Product", "Product model", { // ... description: db.string().description("Product description").vector(), ...db.fields.timestamps(), diff --git a/docs/guides/tailordb/fields.md b/docs/guides/tailordb/fields.md index 9b0be6e..f24d7c7 100644 --- a/docs/guides/tailordb/fields.md +++ b/docs/guides/tailordb/fields.md @@ -21,7 +21,7 @@ With just this, the GraphQL API is automatically generated. ```typescript {{title: "tailordb/product.ts"}} import { db } from "@tailor-platform/sdk"; -export const product = db.type("Product", "Product data schema", { +export const product = db.table("Product", "Product data schema", { title: db.string().description("Title of the product").index(), description: db.string().description("Description of the product"), ...db.fields.timestamps(), @@ -338,7 +338,7 @@ serial: db.string() ```typescript {{title: "tailordb/episode.ts"}} import { db } from "@tailor-platform/sdk"; -export const episode = db.type("Episode", "Episode data schema", { +export const episode = db.table("Episode", "Episode data schema", { title: db.string().description("Title of the episode"), serial: db .string() diff --git a/docs/guides/tailordb/file-type.md b/docs/guides/tailordb/file-type.md index ed8230d..c54f5af 100644 --- a/docs/guides/tailordb/file-type.md +++ b/docs/guides/tailordb/file-type.md @@ -25,7 +25,7 @@ TailorDB File Type enables developers to attach files to records in their data m Files are defined in the **Files** section (not Fields) of your type definition. Each file type field in a record has a reserved URL endpoint that serves as both the upload and download location for that specific field. This URL is automatically generated when the record is created and remains constant throughout the record's lifecycle. ```typescript {{ title: "Schema Definition" }} -db.type("Starship", { +db.table("Starship", { name: db.string().description("Name of the starship"), blueprint: db.file().description("Technical blueprint file"), thumbnail: db.file().description("Thumbnail image"), diff --git a/docs/guides/tailordb/hooks.md b/docs/guides/tailordb/hooks.md index 3453ea0..0f3af1c 100644 --- a/docs/guides/tailordb/hooks.md +++ b/docs/guides/tailordb/hooks.md @@ -15,8 +15,8 @@ You can perform calculations using other fields within the same record. For exam 2. Adding user context: -Hooks allow the addition of current user context to the record. For instance, a field like `createdById` can be populated with the `user.id`.\ -Additionally, you can utilize user attributes, an array of UUIDs configured in the `AttributesFields` in the Auth service. A typical use case for `user.attributes` involves validation. Refer to this [example](/guides/tailordb/validations#exampledemonstratinghowtouseuserattributes). +Hooks allow the addition of the current invoker's context to the record. For instance, a field like `createdById` can be populated with the `invoker.id`.\ +Additionally, you can utilize invoker attributes, an array of UUIDs configured in the `AttributesFields` in the Auth service. A typical use case for `invoker.attributes` involves validation. Refer to this [example](/guides/tailordb/validations#exampledemonstratinghowtouseuserattributes). Furthermore, the field updates itself whenever a new record is created or an existing one is updated.\ This ensures data consistency without manual recalculations, similar to calculated fields in a database, and helps you avoid writing complex logic in [Pipeline](/guides/resolver). @@ -55,19 +55,22 @@ hooks = { // if shopifyID is null, set status to 'awaiting_payment', else set s ```typescript {{ title: "order.ts"}} import { db } from "@tailor-platform/sdk"; -export const order = db.type("Order", { - price: db.int().description("Unit price of a certain product"), - quantity: db.int().description("Order quantity of a certain product"), - totalPrice: db - .int() - .description("Total price of a certain product") - .hooks({ - create: ({ data }) => data.price * data.quantity, - update: ({ data }) => data.price * data.quantity, +export const order = db + .table("Order", { + price: db.int().description("Unit price of a certain product"), + quantity: db.int().description("Order quantity of a certain product"), + totalPrice: db.int().description("Total price of a certain product"), + }) + .hooks({ + create: ({ input }) => ({ totalPrice: input.price * input.quantity }), + update: ({ input, oldRecord }) => ({ + totalPrice: (input.price ?? oldRecord.price) * (input.quantity ?? oldRecord.quantity), }), -}); + }); ``` +Field-level hooks can't read other fields, so computing `totalPrice` from `price` and `quantity` requires a type-level hook (`db.table().hooks(...)`) instead. + ```sh {{ title: "order.tf"}} price = { type = "integer" @@ -94,12 +97,12 @@ With such hooks in place, the totalPrice field will be computed and stored whene ```typescript {{ title: "supplier.ts"}} import { db } from "@tailor-platform/sdk"; -export const supplier = db.type("Supplier", { +export const supplier = db.table("Supplier", { createdById: db .uuid() .description("User ID of the logged in user") .hooks({ - create: ({ user }) => user.id, + create: ({ invoker }) => invoker?.id ?? "", }), }); ``` @@ -119,16 +122,18 @@ export const supplier = db.type("Supplier", { ```typescript {{ title: "order.ts"}} import { db } from "@tailor-platform/sdk"; -export const order = db.type("Order", { +export const order = db.table("Order", { quantity: db .int() .description("Order quantity of a certain product") .hooks({ - create: ({ value }) => value ?? 2, + create: ({ input }) => input ?? 2, }), }); ``` +For a plain, unconditional default like this one, prefer `.default(2)` on the field instead — it also makes the field optional in the create input. Use a hook when the default depends on other fields or the invoker (see [Conditional default values](#conditional-default-values) below). + ```sh {{ title: "order.tf"}} quantity: { type = "integer" @@ -150,17 +155,18 @@ Hooks run after input evaluation, having the consequence that any passed value m ```typescript {{ title: "order.ts"}} import { db } from "@tailor-platform/sdk"; -export const order = db.type("Order", { - price: db.int().description("Unit price of a certain product"), - quantity: db.int().description("Order quantity of a certain product"), - totalPrice: db - .int() - .description("Total price of a certain product") - .hooks({ - create: ({ data }) => data.price * data.quantity, - update: ({ data }) => data.price * data.quantity, +export const order = db + .table("Order", { + price: db.int().description("Unit price of a certain product"), + quantity: db.int().description("Order quantity of a certain product"), + totalPrice: db.int().description("Total price of a certain product"), + }) + .hooks({ + create: ({ input }) => ({ totalPrice: input.price * input.quantity }), + update: ({ input, oldRecord }) => ({ + totalPrice: (input.price ?? oldRecord.price) * (input.quantity ?? oldRecord.quantity), }), -}); + }); ``` ```sh {{ title: "order.tf"}} @@ -194,18 +200,21 @@ If the total exceeds 100, validation fails and the record is not created. ```typescript {{ title: "order.ts"}} import { db } from "@tailor-platform/sdk"; -export const order = db.type("Order", { - price: db.int().description("Unit price of a certain product"), - quantity: db.int().description("Order quantity of a certain product"), - totalPrice: db - .int() - .description("Total price of a certain product") - .hooks({ - create: ({ data }) => data.price * data.quantity, - update: ({ data }) => data.price * data.quantity, - }) - .validate([({ value }) => value >= 100, "totalPrice value must be less than 100"]), -}); +export const order = db + .table("Order", { + price: db.int().description("Unit price of a certain product"), + quantity: db.int().description("Order quantity of a certain product"), + totalPrice: db + .int() + .description("Total price of a certain product") + .validate(({ value }) => (value >= 100 ? "totalPrice value must be less than 100" : undefined)), + }) + .hooks({ + create: ({ input }) => ({ totalPrice: input.price * input.quantity }), + update: ({ input, oldRecord }) => ({ + totalPrice: (input.price ?? oldRecord.price) * (input.quantity ?? oldRecord.quantity), + }), + }); ``` ```sh {{ title: "order.tf"}} @@ -242,20 +251,16 @@ The datetime each record was created and updated can be stored as follows ```typescript {{ title: "order.ts"}} import { db } from "@tailor-platform/sdk"; -export const order = db.type("Order", { - createdAt: db - .datetime() - .description("The time when this record is created") - .hooks({ - create: () => new Date(), - }), - updatedAt: db - .datetime() - .description("The time when this record is updated") - .hooks({ - update: () => new Date(), - }), -}); +export const order = db + .table("Order", { + createdAt: db.datetime().description("The time when this record is created"), + updatedAt: db.datetime().description("The time when this record is updated"), + }) + .hooks({ + // `now` is the same Date instance across every hook in the same operation. + create: ({ now }) => ({ createdAt: now, updatedAt: now }), + update: ({ now }) => ({ updatedAt: now }), + }); ``` Or use the built-in timestamps helper: @@ -263,11 +268,13 @@ Or use the built-in timestamps helper: ```typescript {{ title: "order.ts"}} import { db } from "@tailor-platform/sdk"; -export const order = db.type("Order", { +export const order = db.table("Order", { ...db.fields.timestamps(), }); ``` +`db.fields.timestamps()` adds non-null `createdAt` and `updatedAt` fields with this same behavior built in — both are populated on create, and `updatedAt` is refreshed on every update. Provided values are preserved (useful for seeding historical timestamps). + ```sh {{ title: "order.tf"}} createdAt = { type = "datetime" @@ -285,11 +292,11 @@ export const order = db.type("Order", { } ``` -In this example, the `createdAt` field is evaluated only on create events, and the `updatedAt` field is evaluated only on update events. +In this example, the `createdAt` field is set only on create, while `updatedAt` is set on both create and update. -When the record is created, the current datetime value is stored in the `createdAt` field, but not in the `updatedAt` field. +When the record is created, the current datetime value is stored in both the `createdAt` and `updatedAt` fields. -When the record is updated, the value in the `createdAt` field will remain unchanged, while the `updatedAt` field will be updated with the current datetime. +When the record is updated, the value in the `createdAt` field remains unchanged, while the `updatedAt` field is updated with the current datetime. ### Conditional default values @@ -300,22 +307,23 @@ In this example, the default value of the field `price` is determined by the val ```typescript {{ title: "order.ts"}} import { db } from "@tailor-platform/sdk"; -export const order = db.type("Order", { - type: db - .enum([ - { value: "ITEMA", description: "Item A" }, - { value: "ITEMB", description: "Item B" }, - ]) - .description("Item category"), - price: db - .int() - .description("Unit price of a certain product") - .hooks({ - create: ({ data }) => (data.type === "ITEMA" ? 100 : null), - }), -}); +export const order = db + .table("Order", { + type: db + .enum([ + { value: "ITEMA", description: "Item A" }, + { value: "ITEMB", description: "Item B" }, + ]) + .description("Item category"), + price: db.int().description("Unit price of a certain product"), + }) + .hooks({ + create: ({ input }) => ({ price: input.type === "ITEMA" ? 100 : null }), + }); ``` +This needs a type-level hook because it reads the `type` field to decide `price` — field-level hooks can't see other fields. + ```sh {{ title: "order.tf"}} type = { type = "enum" diff --git a/docs/guides/tailordb/indexes.md b/docs/guides/tailordb/indexes.md index 944776b..03c96c9 100644 --- a/docs/guides/tailordb/indexes.md +++ b/docs/guides/tailordb/indexes.md @@ -11,7 +11,7 @@ This functionality is especially useful for applications that require efficient For instance, consider the definition of the `titleAndSku` index as follows: ```typescript {{ title: 'tailordb/product.ts' }} -db.type("Product", { +db.table("Product", { title: db.string(), sku: db.string(), }).indexes({ diff --git a/docs/guides/tailordb/overview.md b/docs/guides/tailordb/overview.md index b17b8e9..2cf1733 100644 --- a/docs/guides/tailordb/overview.md +++ b/docs/guides/tailordb/overview.md @@ -37,7 +37,7 @@ Here's how to define a `Product` type using the SDK: ```typescript import { db } from "@tailor-platform/sdk"; -export const product = db.type("Product", "Product data schema", { +export const product = db.table("Product", "Product data schema", { title: db.string().description("Title of the product").index(), description: db.string().description("Description of the product"), price: db.float(), @@ -123,7 +123,7 @@ A TailorDB type definition consists of: - **Fields** (Required): Field definitions with types and modifiers ```typescript -export const task = db.type("Task", "Task management entity", { +export const task = db.table("Task", "Task management entity", { title: db.string().description("Task title"), completed: db.bool(), dueDate: db.datetime({ optional: true }), @@ -139,7 +139,7 @@ See [Fields](fields) for detailed field configuration options. TailorDB follows a **secure-by-default** principle. All operations are denied unless explicitly granted. ```typescript -db.type("Task", { +db.table("Task", { title: db.string(), ownerId: db.uuid(), ...db.fields.timestamps(), @@ -163,7 +163,7 @@ See [Permission](permission) for more information. Enable additional GraphQL operations with the `.features()` modifier: ```typescript -db.type("Product", { +db.table("Product", { name: db.string(), price: db.float(), ...db.fields.timestamps(), @@ -180,7 +180,7 @@ See [Advanced Settings](advanced-settings/overview) for more information. Create multi-field indexes for query optimization: ```typescript -db.type("Order", { +db.table("Order", { customerId: db.uuid(), orderDate: db.datetime(), ...db.fields.timestamps(), diff --git a/docs/guides/tailordb/permission.md b/docs/guides/tailordb/permission.md index 5b0a16a..dfbf274 100644 --- a/docs/guides/tailordb/permission.md +++ b/docs/guides/tailordb/permission.md @@ -18,7 +18,7 @@ The new permission system is recommended for all new applications. It addresses ### Basic Structure ```typescript -db.type("Example", { +db.table("Example", { // field definitions }).permission({ create: [ @@ -45,7 +45,7 @@ Each permission type has different semantics: Read Permission act as automatic filters. Only records that match at least one policy can be retrieved. ```typescript -db.type("Example", { +db.table("Example", { userId: db.uuid(), }).permission({ read: [[{ record: "userId" }, "=", { user: "id" }]], @@ -57,7 +57,7 @@ db.type("Example", { These Permissions act as validation rules. If a record doesn't match any policy, the operation is prohibited and returns a permission denied error. ```typescript -db.type("Example", { +db.table("Example", { // fields }).permission({ create: [[{ user: "role" }, "=", "ADMIN"]], @@ -177,7 +177,7 @@ const TaskStatus = db.enum("TaskStatus", [ { value: "DONE", description: "Task has been completed" }, ]); -db.type("Task", { +db.table("Task", { title: db.string({ required: true, description: "Task title" }), status: TaskStatus({ description: "Task status", diff --git a/docs/guides/tailordb/relationships.md b/docs/guides/tailordb/relationships.md index 2a46f4b..649a763 100644 --- a/docs/guides/tailordb/relationships.md +++ b/docs/guides/tailordb/relationships.md @@ -55,13 +55,13 @@ In a 1:1 relationship, each record in Type A can be associated with at most one ```typescript import { db } from "@tailor-platform/sdk"; -export const species = db.type("Species", "Species data schema", { +export const species = db.table("Species", "Species data schema", { name: db.string().description("Name of the species").required(), ...db.fields.timestamps(), }); export type species = typeof species; -export const character = db.type("Character", "Character data schema", { +export const character = db.table("Character", "Character data schema", { name: db.string().description("Name of the character").required(), speciesId: db .uuid() @@ -131,13 +131,13 @@ In a 1:N relationship, each record in Type A can be associated with multiple rec ```typescript import { db } from "@tailor-platform/sdk"; -export const species = db.type("Species", "Species data schema", { +export const species = db.table("Species", "Species data schema", { name: db.string().description("Name of the species").required(), ...db.fields.timestamps(), }); export type species = typeof species; -export const character = db.type("Character", "Character data schema", { +export const character = db.table("Character", "Character data schema", { name: db.string().description("Name of the character").required(), speciesId: db .uuid() diff --git a/docs/guides/tailordb/validations.md b/docs/guides/tailordb/validations.md index 24be921..6db4490 100644 --- a/docs/guides/tailordb/validations.md +++ b/docs/guides/tailordb/validations.md @@ -15,16 +15,12 @@ Use the `.validate()` method to add validation rules to a field. The method acce ```typescript db.string().validate( - ({ value }) => value.includes("@"), // validation function - [({ value }) => value.length > 5, "Must be longer than 5 characters"], // with error message + ({ value }) => (value.includes("@") ? undefined : "Must contain @"), + ({ value }) => (value.length > 5 ? undefined : "Must be longer than 5 characters"), ); ``` -The validation function receives an object with: - -- `value`: The current field value -- `data`: The entire record data -- `user`: The user context performing the operation +The validation function receives `{ value }` — the field value after hooks have run — and returns an error message string to fail, or `void`/`undefined` to pass. Field-level validators can't access other fields; use a type-level validator (`db.table().validate()`) for cross-field checks. **Notes:** @@ -65,28 +61,37 @@ The `Action` will be evaluated when the `Expr` returns true. ```typescript // reportNumber value must be less than 100 or over 103 -reportNumber: db.int().validate([ - ({ value }) => value < 100 || value > 103, - "reportNumber value must be less than 100 or over 103", -]); +reportNumber: db + .int() + .validate(({ value }) => + value < 100 || value > 103 ? undefined : "reportNumber value must be less than 100 or over 103", + ); ``` ```typescript // Description length should be less than 40 characters -description: db.string() +description: db + .string() .description("Description of the product.") - .validate([ - ({ value }) => value.length < 40, - "Description length should be less than 40 characters.", - ]); + .validate(({ value }) => + value.length < 40 ? undefined : "Description length should be less than 40 characters.", + ); ``` +Field-level validators only see `{ value }`, so a check like "the invoker must be logged in" — which depends on the invoker, not the field's own value — needs a type-level validator instead: + ```typescript // User must be logged in to create an item -itemStatus: db.string().validate([ - ({ user }) => Object.keys(user).length > 0, - "You must be logged in to create an item", -]); +export const item = db + .table("Item", { + itemStatus: db.string(), + // ...other fields + }) + .validate(({ invoker }, issues) => { + if (!invoker) { + issues("itemStatus", "You must be logged in to create an item"); + } + }); ``` ```javascript @@ -184,10 +189,16 @@ itemStatus: { ### Example demonstrating how to use user attributes ```typescript -itemCode: db.string().validate([ - ({ user }) => user.attributes.includes("{ADMIN_ID}"), - "To create an item, you must be the Admin", -]); +export const item = db + .table("Item", { + itemCode: db.string(), + // ...other fields + }) + .validate(({ invoker }, issues) => { + if (!invoker?.attributeList.includes("{ADMIN_ID}")) { + issues("itemCode", "To create an item, you must be the Admin"); + } + }); ``` ```javascript diff --git a/docs/guides/tailordb/versioning.md b/docs/guides/tailordb/versioning.md index 5672bf4..8977f5f 100644 --- a/docs/guides/tailordb/versioning.md +++ b/docs/guides/tailordb/versioning.md @@ -21,7 +21,7 @@ This approach preserves historical data and enables change monitoring and analys Let's create a history table for the `StockSummary` type to log its data changes. ```typescript -db.type("StockSummaryHistory", { +db.table("StockSummaryHistory", { variantID: db.uuid({ required: true, description: "Variant ID" }), variant: db.link("ProductVariant", "variantID"), onHoldQuantity: db.float({ required: true, description: "onHoldQuantity" }), @@ -56,7 +56,7 @@ db.type("StockSummaryHistory", { By enabling `publishEvents` in the `StockSummary` features, you can create an event-based trigger that executes on every `StockSummary` record update. ```typescript -db.type("StockSummary", { +db.table("StockSummary", { // fields... }).features({ publishEvents: true, diff --git a/docs/guides/workflow/creating-workflows.md b/docs/guides/workflow/creating-workflows.md index ab1a3db..92fa173 100644 --- a/docs/guides/workflow/creating-workflows.md +++ b/docs/guides/workflow/creating-workflows.md @@ -168,21 +168,21 @@ export function main(args) { console.log("Starting workflow with orderId:", args.orderId); // Step 1: Fetch order data - const order = tailor.workflow.triggerJobFunction("fetchOrder", { + const order = tailor.workflow.startJobFunction("fetchOrder", { orderId: args.orderId, }); // Step 2: Validate order - const validated = tailor.workflow.triggerJobFunction("validateOrder", order); + const validated = tailor.workflow.startJobFunction("validateOrder", order); // Step 3: Process payment - const payment = tailor.workflow.triggerJobFunction("processPayment", { + const payment = tailor.workflow.startJobFunction("processPayment", { orderId: validated.id, amount: validated.total, }); // Step 4: Send confirmation - tailor.workflow.triggerJobFunction("sendConfirmation", { + tailor.workflow.startJobFunction("sendConfirmation", { orderId: validated.id, email: validated.customerEmail, paymentId: payment.id, diff --git a/docs/guides/workflow/index.md b/docs/guides/workflow/index.md index 574e0c0..be3258e 100644 --- a/docs/guides/workflow/index.md +++ b/docs/guides/workflow/index.md @@ -16,7 +16,7 @@ With Workflow service, you can: - Automatically preserve execution state at each step - Resume failed workflows from the point of failure - Access TailorDB and other platform services with proper authentication -- Monitor execution progress through Tailor Console and tailor-sdk CLI +- Monitor execution progress through Tailor Console and tailor CLI ### Workflow vs Function @@ -79,8 +79,8 @@ Workflows support nested function calls, similar to regular programming: ```javascript export function main(args) { // Call functions sequentially - const data = tailor.workflow.triggerJobFunction("fetchData", {}); - const processed = tailor.workflow.triggerJobFunction("processData", data); + const data = tailor.workflow.startJobFunction("fetchData", {}); + const processed = tailor.workflow.startJobFunction("processData", data); return processed; } ``` @@ -93,7 +93,7 @@ The execution stack: Each function's result is cached and passed to the next function in the chain. -Job functions within a single workflow execute **sequentially**, not in parallel. Each function completes before the next one starts. For concurrent execution, you can start multiple workflows asynchronously using `tailor.workflow.triggerWorkflow()`. +Job functions within a single workflow execute **sequentially**, not in parallel. Each function completes before the next one starts. For concurrent execution, you can start multiple workflows asynchronously using `tailor.workflow.startWorkflow()`. ## Concurrency Control @@ -105,7 +105,7 @@ Set `concurrencyPolicy.maxConcurrentExecutions` on the workflow definition to ca - Enforced by the **scheduler** when it picks up `PENDING` executions. Executions that would exceed the cap stay `PENDING` and are re-evaluated on the next scheduler tick. - Scoped per workflow definition. Other workflows in the same workspace are unaffected. -- Applies to every entry point (`triggerWorkflow`, executor triggers, CLI `workflow start`) equally. +- Applies to every entry point (`startWorkflow`, executor triggers, CLI `workflow start`) equally. ```typescript {{ title: 'workflows/import-orders.ts' }} import { createWorkflow } from "@tailor-platform/sdk"; @@ -127,7 +127,7 @@ See [Concurrency Policy](/sdk/services/workflow#concurrency-policy) in the SDK W ### Job Function Execution Policies -Declare workspace-scoped execution policies with a per-key `maxConcurrentExecutions` cap, then route job function dispatches through them by passing `executionPolicyKey` on `job.trigger()` / `tailor.workflow.triggerJobFunction()`. +Declare workspace-scoped execution policies with a per-key `maxConcurrentExecutions` cap, then route job function dispatches through them by passing `executionPolicyKey` on `job.start()` / `tailor.workflow.startJobFunction()`. - Enforced by the **runner** at dispatch time — a separate mechanism from the scheduler-level workflow cap above. The two stack: a workflow that is allowed to start can still have its job function dispatches suspended by an execution policy. - Dispatches that would exceed the cap are suspended and resume automatically as slots free up. @@ -176,14 +176,14 @@ export const syncTenant = createWorkflowJob({ name: "sync-tenant", body: async (input: { tenantId: string }) => { // Exact-key policy: pass `.key` directly (typed). - await pushMetrics.trigger( + await pushMetrics.start( { tenantId: input.tenantId }, { executionPolicyKey: executionPolicies.premium.key }, ); // Wildcard policy: build the concrete key with `.keyFor(suffix)`. // Resolves to e.g. "tenant-api.acme"; each tenant gets its own pool of 3. - await syncOrders.trigger( + await syncOrders.start( { tenantId: input.tenantId }, { executionPolicyKey: executionPolicies.tenantApi.keyFor(input.tenantId) }, ); @@ -191,7 +191,7 @@ export const syncTenant = createWorkflowJob({ }); ``` -The same `executionPolicyKey` option is available on `tailor.workflow.triggerJobFunction(name, args, options)` when dispatching by name from a Function-service script. +The same `executionPolicyKey` option is available on `tailor.workflow.startJobFunction(name, args, options)` when dispatching by name from a Function-service script. **Matching modes:** diff --git a/docs/guides/workflow/monitoring-executions.md b/docs/guides/workflow/monitoring-executions.md index a0ad561..fd2dc94 100644 --- a/docs/guides/workflow/monitoring-executions.md +++ b/docs/guides/workflow/monitoring-executions.md @@ -6,22 +6,22 @@ doc_type: guide ## Check Execution Status -Use the `tailor-sdk workflow executions` command to check the status of a workflow execution: +Use the `tailor workflow executions` command to check the status of a workflow execution: ```bash -tailor-sdk workflow executions +tailor workflow executions ``` **With logs:** ```bash -tailor-sdk workflow executions --logs +tailor workflow executions --logs ``` **Usage:** ```bash -tailor-sdk workflow executions [options] [executionId] +tailor workflow executions [options] [executionId] ``` **Arguments:** @@ -73,25 +73,25 @@ Result: { List all workflow executions in your workspace: ```bash -tailor-sdk workflow executions +tailor workflow executions ``` **Filter by workflow name:** ```bash -tailor-sdk workflow executions --workflow-name my-workflow +tailor workflow executions --workflow-name my-workflow ``` **Filter by status:** ```bash -tailor-sdk workflow executions --status RUNNING +tailor workflow executions --status RUNNING ``` **Combine filters:** ```bash -tailor-sdk workflow executions --workflow-name my-workflow --status FAILED +tailor workflow executions --workflow-name my-workflow --status FAILED ``` ## Execution Status @@ -110,7 +110,7 @@ A workflow execution can have the following statuses: You can follow an execution in real-time using the `--wait` flag: ```bash -tailor-sdk workflow executions --wait +tailor workflow executions --wait ``` This will poll for updates until the execution completes. @@ -118,13 +118,13 @@ This will poll for updates until the execution completes. **Customize polling interval:** ```bash -tailor-sdk workflow executions --wait --interval 5s +tailor workflow executions --wait --interval 5s ``` **Wait and show logs:** ```bash -tailor-sdk workflow executions --wait --logs +tailor workflow executions --wait --logs ``` The `start` command returns an execution ID that you can use with `workflow executions` to monitor progress. @@ -147,13 +147,13 @@ The console provides: If a workflow execution fails, you can resume it from the point of failure using the `resume` command: ```bash -tailor-sdk workflow resume +tailor workflow resume ``` **Usage:** ```bash -tailor-sdk workflow resume [options] +tailor workflow resume [options] ``` **Arguments:** @@ -170,13 +170,13 @@ tailor-sdk workflow resume [options] **Resume and wait for completion:** ```bash -tailor-sdk workflow resume --wait +tailor workflow resume --wait ``` **Resume and show logs:** ```bash -tailor-sdk workflow resume --wait --logs +tailor workflow resume --wait --logs ``` **What happens during resume:** @@ -206,13 +206,13 @@ saveToDb → Executed again ### Resuming from Code -You can also resume a failed or pending-retry execution from your own code (a workflow job function, an executor, or a pipeline resolver) using `tailor.workflow.resumeWorkflow()`. This lets you build self-healing flows that recover from transient failures automatically, without an operator running `tailor-sdk workflow resume` or using the Tailor Console. +You can also resume a failed or pending-retry execution from your own code (a workflow job function, an executor, or a pipeline resolver) using `tailor.workflow.resumeWorkflowExecution()`. This lets you build self-healing flows that recover from transient failures automatically, without an operator running `tailor workflow resume` or using the Tailor Console. **Example:** ```javascript export async function main(args) { - const resumedId = await tailor.workflow.resumeWorkflow(args.executionId); + const resumedId = await tailor.workflow.resumeWorkflowExecution(args.executionId); console.log("Resumed execution:", resumedId); return { resumedExecutionId: resumedId }; } @@ -226,7 +226,7 @@ export async function main(args) { - Execution ID of the resumed execution (string) -`resumeWorkflow()` behaves like the `resume` command described above. The workflow restarts from the main function and reuses the cached results of successful job functions, so only failed or not-yet-executed jobs run again. It rejects with an error whose message is prefixed with `resumeWorkflow failed:` when the execution cannot be resumed. Let that error propagate to fail the calling execution, and add a `try`/`catch` only when you need to control the error or its message. +`resumeWorkflowExecution()` behaves like the `resume` command described above. The workflow restarts from the main function and reuses the cached results of successful job functions, so only failed or not-yet-executed jobs run again. It rejects with an error describing why the execution cannot be resumed when it isn't in a resumable state. Let that error propagate to fail the calling execution, and add a `try`/`catch` only when you need to control the error or its message. ### When to Use Resume @@ -295,5 +295,5 @@ export const myWorkflow = createWorkflow({ After defining your workflow, deploy it using: ```bash -tailor-sdk apply +tailor apply ``` diff --git a/docs/guides/workflow/triggering-workflow.md b/docs/guides/workflow/triggering-workflow.md index cab816c..173e76f 100644 --- a/docs/guides/workflow/triggering-workflow.md +++ b/docs/guides/workflow/triggering-workflow.md @@ -11,14 +11,14 @@ Workflows can be triggered in production environments using two primary methods: ## Job Functions (Programmatic) -You can start another workflow from within a job function using `tailor.workflow.triggerWorkflow()`. This is useful for workflow composition, parallel execution, and implementing multi-stage processes. +You can start another workflow from within a job function using `tailor.workflow.startWorkflow()`. This is useful for workflow composition, parallel execution, and implementing multi-stage processes. **Example:** ```javascript export async function main(args) { // Start another workflow asynchronously - const executionId = await tailor.workflow.triggerWorkflow( + const executionId = await tailor.workflow.startWorkflow( "notification-workflow", { orderId: args.orderId, email: args.customerEmail }, { @@ -56,7 +56,7 @@ export async function main(args) { - Starting parallel sub-workflows for different tasks - Implementing workflow composition patterns -`triggerWorkflow()` starts a workflow asynchronously and returns immediately with an execution ID. It does not wait for the workflow to complete. Use this for fire-and-forget or parallel execution patterns. +`startWorkflow()` starts a workflow asynchronously and returns immediately with an execution ID. It does not wait for the workflow to complete. Use this for fire-and-forget or parallel execution patterns. ## Executor (Event-Driven Automation) @@ -286,7 +286,7 @@ Both triggering methods support authentication through Machine Users: **In Job Functions:** ```javascript -const executionId = await tailor.workflow.triggerWorkflow( +const executionId = await tailor.workflow.startWorkflow( "my-workflow", { data: "value" }, { @@ -318,10 +318,10 @@ Note: In the SDK, authentication is handled through the `authInvoker` configurat ## Starting Workflows -Start a workflow execution using the `tailor-sdk workflow start` command: +Start a workflow execution using the `tailor workflow start` command: ```bash -tailor-sdk workflow start my-workflow \ +tailor workflow start my-workflow \ --machineuser admin-machine-user \ --arg '{"orderId": "12345"}' ``` @@ -331,7 +331,7 @@ The workflow name is specified as a positional argument (the first argument afte **Usage:** ```bash -tailor-sdk workflow start [options] +tailor workflow start [options] ``` **Arguments:** @@ -358,7 +358,7 @@ Status: pending Pass input data to your workflow using the `--arg` option: ```bash -tailor-sdk workflow start my-workflow \ +tailor workflow start my-workflow \ --machineuser admin-machine-user \ --arg '{"orderId": "12345", "priority": "high"}' ``` @@ -380,7 +380,7 @@ Workflows execute with machine user authentication context. **Machine User Authentication:** ```bash -tailor-sdk workflow start my-workflow \ +tailor workflow start my-workflow \ --machineuser my-machine-user \ --arg '{"data": "value"}' ``` diff --git a/docs/guides/workflow/wait-resolve.md b/docs/guides/workflow/wait-resolve.md index be10596..7d15233 100644 --- a/docs/guides/workflow/wait-resolve.md +++ b/docs/guides/workflow/wait-resolve.md @@ -18,9 +18,9 @@ This is useful for scenarios where a workflow needs to wait for: The Wait / Resolve flow consists of three phases: -1. **Trigger** — A workflow is triggered and starts executing -2. **Wait** — The workflow calls `wait()`, which suspends execution and parks it in the database -3. **Resolve** — Another script calls `resolve()` with the execution ID and key, which resumes the workflow +1. **Start** — A workflow is started and begins executing +2. **Wait** — The workflow job calls a wait point's `.wait()` method, which suspends execution and parks it in the database +3. **Resolve** — Another script calls the same wait point's `.resolve()` method with the execution ID, which resumes the workflow ```mermaid sequenceDiagram @@ -29,66 +29,78 @@ sequenceDiagram participant DB as TailorDB participant Resolver as Resolver (Pipeline Function) - Trigger->>Workflow: triggerWorkflow() + Trigger->>Workflow: startWorkflow() Trigger->>DB: Save executionId - Workflow->>Workflow: wait("approval", payload) + Workflow->>Workflow: approval.wait(payload) Note over Workflow: Execution paused (status: waiting) Resolver->>DB: Read executionId - Resolver->>Workflow: resolve(executionId, "approval", callback) + Resolver->>Workflow: approval.resolve(executionId, callback) Note over Workflow: Execution resumed (status: running) Workflow->>Workflow: Continues with resolve result ``` -While waiting, the workflow runner exits and no resources are consumed. The execution is parked in the database until `resolve()` is called. +While waiting, the workflow runner exits and no resources are consumed. The execution is parked in the database until `.resolve()` is called. ## API Reference -### `tailor.workflow.wait(key, payload)` +Wait points are declared with `createWaitPoint` (single) or `createWaitPoints` (multiple, via a builder callback) from `@tailor-platform/sdk`. Each returns an object with `.wait()` and `.resolve()` methods, typed by a `Payload` (sent to `.wait()`) and a `Result` (returned from `.wait()`, produced by the `.resolve()` callback) — both must be JSON-serializable values. -Suspends the current workflow execution and waits for an external signal. +```typescript {{ title: 'workflows/approval.ts' }} +import { createWaitPoint } from "@tailor-platform/sdk"; + +export const approval = createWaitPoint< + { orderId: string; amount: number; requestedBy: string }, + { approved: boolean } +>("approval"); +``` + +### `wait(payload)` + +Suspends the current workflow job and waits for an external signal. **Arguments:** -- **`key`** (string, required) — A unique identifier for this wait point within the workflow. Must match the pattern `^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$` (lowercase alphanumeric with hyphens, 3–63 characters). -- **`payload`** (object, optional) — JSON-serializable data to persist while waiting. This data is available to the `resolve()` callback. Defaults to `{}` if omitted. +- **`payload`** (optional) — JSON-serializable data to persist while waiting. This data is available to the `.resolve()` callback. Omit if the wait point's `Payload` type is `undefined`. **Return value:** -- The result returned by the `resolve()` callback when the workflow resumes. +- The result returned by the `.resolve()` callback when the workflow resumes. **Example:** -```javascript -export async function main(args) { - const order = tailor.workflow.triggerJobFunction("createOrder", args); +```typescript {{ title: 'workflows/jobs/process-order.ts' }} +import { createWorkflowJob } from "@tailor-platform/sdk"; +import { approval } from "../approval"; - // Pause and wait for approval - const approval = await tailor.workflow.wait("approval", { - orderId: order.id, - amount: order.totalAmount, - requestedBy: args.userId, - }); +export const processOrder = createWorkflowJob({ + name: "process-order", + body: async (input: { orderId: string; totalAmount: number; userId: string }) => { + const order = { id: input.orderId }; - if (approval.approved) { - tailor.workflow.triggerJobFunction("fulfillOrder", { + // Pause and wait for approval + const result = await approval.wait({ orderId: order.id, + amount: input.totalAmount, + requestedBy: input.userId, }); - } - return { orderId: order.id, approved: approval.approved }; -} + if (!result.approved) { + return { orderId: order.id, status: "rejected" as const }; + } + return { orderId: order.id, status: "approved" as const }; + }, +}); ``` -### `tailor.workflow.resolve(executionId, key, callback)` +### `resolve(executionId, callback)` Resolves a waiting workflow, causing it to resume execution. **Arguments:** - **`executionId`** (string, required) — The execution ID of the waiting workflow. -- **`key`** (string, required) — The wait key to resolve. Must match the key passed to `wait()`. -- **`callback`** (function, required) — A function that receives the `waitPayload` (the data passed to `wait()`) and returns a JSON-serializable result. The returned value is passed back to the `wait()` caller when the workflow resumes. +- **`callback`** (function, required) — A function that receives the payload passed to `.wait()` and returns the `Result`. The returned value is passed back to the `.wait()` caller when the workflow resumes. **Return value:** @@ -96,38 +108,45 @@ Resolves a waiting workflow, causing it to resume execution. **Example:** -```javascript -export async function main(args) { - await tailor.workflow.resolve( - args.executionId, - "approval", - (waitPayload) => { - // waitPayload contains the data passed to wait() +```typescript {{ title: 'resolvers/resolveApproval.ts' }} +import { createResolver, t } from "@tailor-platform/sdk"; +import { approval } from "../workflows/approval"; + +export default createResolver({ + name: "resolveApproval", + operation: "mutation", + input: { + executionId: t.string(), + approved: t.bool(), + }, + body: async ({ input }) => { + await approval.resolve(input.executionId, (payload) => { + // payload contains the data passed to wait() // e.g., { orderId: "...", amount: 1000, requestedBy: "..." } - return { - approved: args.approved, - approvedBy: args.approverId, - approvedAt: new Date().toISOString(), - }; - }, - ); -} + return { approved: input.approved }; + }); + return { resolved: true }; + }, + output: t.object({ resolved: t.bool() }), +}); ``` +Wait points can be imported and used in any file (workflow jobs, resolvers, executors). + ## Typical Pattern A common implementation pattern involves three components: -1. **A workflow** that triggers processing and pauses with `wait()` +1. **A workflow** that starts processing and pauses on a wait point's `.wait()` 2. **A TailorDB record** that stores the execution ID for later retrieval -3. **A pipeline resolver function** that calls `resolve()` when the human decision is made +3. **A pipeline resolver function** that calls the wait point's `.resolve()` when the human decision is made -### Step 1: Trigger the workflow and save the execution ID +### Step 1: Start the workflow and save the execution ID ```javascript // Pipeline resolver or executor function export async function main(args) { - const executionId = await tailor.workflow.triggerWorkflow( + const executionId = await tailor.workflow.startWorkflow( "order-approval-workflow", { orderId: args.orderId }, ); @@ -147,56 +166,59 @@ export async function main(args) { } ``` -### Step 2: Workflow pauses with `wait()` +### Step 2: Workflow pauses on `approval.wait()` -```javascript -// Workflow job function -export async function main(args) { - const order = tailor.workflow.triggerJobFunction("prepareOrder", args); - - // Pause and wait for human approval - const decision = await tailor.workflow.wait("approval", { - orderId: order.id, - items: order.items, - total: order.total, - }); +```typescript {{ title: 'workflows/jobs/prepare-order.ts' }} +import { createWorkflowJob } from "@tailor-platform/sdk"; +import { approval } from "../approval"; - // Resume after approval - if (decision.approved) { - tailor.workflow.triggerJobFunction("processOrder", { - orderId: order.id, +export const prepareOrder = createWorkflowJob({ + name: "prepare-order", + body: async (input: { orderId: string; items: unknown[]; total: number }) => { + // Pause and wait for human approval + const decision = await approval.wait({ + orderId: input.orderId, + items: input.items, + total: input.total, }); - } - return { status: decision.approved ? "completed" : "rejected" }; -} + return { status: decision.approved ? "completed" : "rejected" }; + }, +}); ``` ### Step 3: Resolve from a pipeline resolver function -```javascript -// Pipeline resolver function called when an approver submits their decision -export async function main(args) { - // Retrieve the execution ID from TailorDB - const order = await gql.query({ - order: { - __args: { id: args.orderId }, - workflowExecutionId: true, - }, - }); +```typescript {{ title: 'resolvers/resolveApproval.ts' }} +import { createResolver, t } from "@tailor-platform/sdk"; +import { getDB } from "../generated/tailordb"; +import { approval } from "../workflows/approval"; + +export default createResolver({ + name: "resolveApproval", + operation: "mutation", + input: { + orderId: t.uuid(), + approved: t.bool(), + }, + body: async ({ input }) => { + // Retrieve the execution ID from TailorDB + const db = getDB("tailordb"); + const order = await db + .selectFrom("Order") + .select("workflowExecutionId") + .where("id", "=", input.orderId) + .executeTakeFirstOrThrow(); + + // Resolve the waiting workflow + await approval.resolve(order.workflowExecutionId, (payload) => { + return { approved: input.approved }; + }); - // Resolve the waiting workflow - await tailor.workflow.resolve( - order.workflowExecutionId, - "approval", - (waitPayload) => { - return { - approved: args.approved, - approvedBy: args.approverId, - }; - }, - ); -} + return { resolved: true }; + }, + output: t.object({ resolved: t.bool() }), +}); ``` ## Execution Status @@ -224,13 +246,13 @@ stateDiagram-v2 You can check the waiting status using the CLI: ```bash -tailor-sdk workflow executions --status WAITING +tailor workflow executions --status WAITING ``` ## Key Behaviors - **No resource consumption while waiting** — The workflow runner exits when `wait()` is called. The execution is parked in the database. - **Durable state** — The wait payload and all previous job function results are preserved across the wait/resume cycle. -- **Key matching** — The `key` in `resolve()` must exactly match the `key` in `wait()`. A mismatched key results in an error. +- **Key matching** — Each wait point has its own key (the string passed to `createWaitPoint`, or the property name under `createWaitPoints`). Resolving requires importing and calling `.resolve()` on that same wait point value — you can't resolve a suspended execution against a different wait point. - **Single resolve** — Each wait point can only be resolved once. Concurrent resolve attempts for the same execution and key are safely rejected. - **Cache-aware** — Wait results are integrated into the durable execution cache. If a resumed workflow is later retried, the cached wait result is reused without requiring another `resolve()`. diff --git a/docs/reference/api/api-references.md b/docs/reference/api/api-references.md index e67a638..e286c1c 100644 --- a/docs/reference/api/api-references.md +++ b/docs/reference/api/api-references.md @@ -70,14 +70,14 @@ All platform services are exposed through the `OperatorService`, a unified gRPC ## Authentication -All API calls require authentication using Bearer tokens. Use tailor-sdk to authenticate: +All API calls require authentication using Bearer tokens. Use tailor to authenticate: ```bash # Login to Tailor Platform -tailor-sdk login +tailor login # Create a personal access token for API authentication -tailor-sdk user pat create +tailor user pat create ``` See the [Auth Guide](/guides/auth/overview) for detailed authentication setup. diff --git a/docs/tutorials/develop-from-scratch/step-01.md b/docs/tutorials/develop-from-scratch/step-01.md index a71238b..01edb3c 100644 --- a/docs/tutorials/develop-from-scratch/step-01.md +++ b/docs/tutorials/develop-from-scratch/step-01.md @@ -12,8 +12,8 @@ This step establishes the foundational database schema for your project manageme "private": true, "type": "module", "scripts": { - "deploy": "tailor-sdk apply", - "generate": "tailor-sdk generate" + "deploy": "tailor apply", + "generate": "tailor generate" }, "dependencies": { "@tailor-platform/function-kysely-tailordb": "0.1.3", @@ -61,7 +61,7 @@ export default defineConfig({ ```typescript {{title: 'src/db/user.ts'}} import { db } from "@tailor-platform/sdk"; -export const user = db.type("User", { +export const user = db.table("User", { name: db.string().description("Name of the user"), email: db.string().unique().description("Email address of the user"), role: db.enum(["MANAGER", "STAFF"]), @@ -72,7 +72,7 @@ export const user = db.type("User", { ```typescript {{title: 'src/db/project.ts'}} import { db } from "@tailor-platform/sdk"; -export const project = db.type("Project", { +export const project = db.table("Project", { name: db.string().description("Name of the Project"), description: db.string({ optional: true }).description("Description of the project"), status: db.enum([ @@ -94,7 +94,7 @@ import { db } from "@tailor-platform/sdk"; import { project } from "./project"; import { user } from "./user"; -export const task = db.type("Task", { +export const task = db.table("Task", { name: db.string().description("Name of the Task"), description: db.string({ optional: true }).description("Description of the task"), projectId: db diff --git a/docs/tutorials/manage-data-schema/create-data-schema.md b/docs/tutorials/manage-data-schema/create-data-schema.md index 8a3aad8..c17c521 100644 --- a/docs/tutorials/manage-data-schema/create-data-schema.md +++ b/docs/tutorials/manage-data-schema/create-data-schema.md @@ -23,7 +23,7 @@ Create a new file `db/task.ts` in your project and define the Task type: import { db } from "@tailor-platform/sdk"; import { project } from "./project"; -export const task = db.type("Task", { +export const task = db.table("Task", { title: db.string().description("Task title"), description: db.string().optional().description("Task description"), status: db diff --git a/docs/tutorials/manage-data-schema/data-schema-basics.md b/docs/tutorials/manage-data-schema/data-schema-basics.md index 7dff21f..1b59910 100644 --- a/docs/tutorials/manage-data-schema/data-schema-basics.md +++ b/docs/tutorials/manage-data-schema/data-schema-basics.md @@ -21,7 +21,7 @@ Create a `Project` type in your application. In your project's `db/` directory, ```typescript import { db } from "@tailor-platform/sdk"; -export const project = db.type("Project", { +export const project = db.table("Project", { name: db.string().description("Project name"), description: db.string().optional().description("Project description"), status: db diff --git a/docs/tutorials/manage-data-schema/modify-data-schema.md b/docs/tutorials/manage-data-schema/modify-data-schema.md index 1a0d11c..0e5bdc0 100644 --- a/docs/tutorials/manage-data-schema/modify-data-schema.md +++ b/docs/tutorials/manage-data-schema/modify-data-schema.md @@ -26,7 +26,7 @@ Open your `db/project.ts` file and add the new field: ```typescript import { db } from "@tailor-platform/sdk"; -export const project = db.type("Project", { +export const project = db.table("Project", { name: db.string().description("Project name"), description: db.string().optional().description("Project description"), status: db diff --git a/docs/tutorials/manage-data-schema/validate-field-data.md b/docs/tutorials/manage-data-schema/validate-field-data.md index 58de100..65a472d 100644 --- a/docs/tutorials/manage-data-schema/validate-field-data.md +++ b/docs/tutorials/manage-data-schema/validate-field-data.md @@ -22,13 +22,13 @@ Open your `db/project.ts` file and add a validation rule to the `name` field. We ```typescript import { db } from "@tailor-platform/sdk"; -export const project = db.type("Project", { +export const project = db.table("Project", { name: db .string() .description("Project name") .validate( - ({ value }) => value.length >= 3, - [({ value }) => value.length <= 50, "Project name must be 50 characters or less"], + ({ value }) => (value.length >= 3 ? undefined : "Project name must be at least 3 characters"), + ({ value }) => (value.length <= 50 ? undefined : "Project name must be 50 characters or less"), ), description: db.string().optional().description("Project description"), status: db @@ -39,18 +39,20 @@ export const project = db.type("Project", { completionPercentage: db .int() .optional() - .validate( - ({ value }) => value === undefined || (value >= 0 && value <= 100), - "Completion percentage must be between 0 and 100", + .validate(({ value }) => + value === undefined || (value >= 0 && value <= 100) + ? undefined + : "Completion percentage must be between 0 and 100", ) .description("Project completion percentage"), priority: db.enum(["low", "medium", "high", "critical"]).description("Project priority level"), teamSize: db .int() .optional() - .validate( - ({ value }) => value === undefined || (value > 0 && value <= 100), - "Team size must be between 1 and 100", + .validate(({ value }) => + value === undefined || (value > 0 && value <= 100) + ? undefined + : "Team size must be between 1 and 100", ) .description("Number of team members"), ...db.fields.timestamps(), @@ -66,9 +68,9 @@ export type project = typeof project; The `validate()` method accepts: -- A validation function that receives `{ value }` and returns `true` if valid -- An optional error message (string or tuple with function and message) -- Multiple validations can be chained +- One or more validation functions, each receiving `{ value }` (the field value after hooks) +- Each function returns an error message string to fail, or `void`/`undefined` to pass +- Multiple validations can be chained by passing multiple functions For more information, see [Input Validation](../../sdk/services/resolver#input-validation) documentation. diff --git a/docs/tutorials/resolver.md b/docs/tutorials/resolver.md index 190b464..9528e14 100644 --- a/docs/tutorials/resolver.md +++ b/docs/tutorials/resolver.md @@ -21,7 +21,7 @@ First, create the necessary types in TailorDB. Create two files in your `db/` di ```typescript import { db } from "@tailor-platform/sdk"; -export const project = db.type("Project", { +export const project = db.table("Project", { name: db.string().description("Project name"), description: db.string().optional().description("Project description"), status: db.enum(["active", "completed", "archived"]).description("Project status"), @@ -36,7 +36,7 @@ export type project = typeof project; import { db } from "@tailor-platform/sdk"; import { project } from "./project"; -export const teamMember = db.type("TeamMember", { +export const teamMember = db.table("TeamMember", { name: db.string().description("Team member name"), email: db.string().description("Team member email"), role: db.string().optional().description("Team member role"), @@ -44,7 +44,7 @@ export const teamMember = db.type("TeamMember", { }); export type teamMember = typeof teamMember; -export const task = db.type("Task", { +export const task = db.table("Task", { title: db.string().description("Task title"), description: db.string().optional().description("Task description"), status: db.enum(["todo", "in_progress", "completed"]).description("Task status"), diff --git a/docs/tutorials/setup-auth/login/create-oauth2-client.md b/docs/tutorials/setup-auth/login/create-oauth2-client.md index 5ed3ae6..741031c 100644 --- a/docs/tutorials/setup-auth/login/create-oauth2-client.md +++ b/docs/tutorials/setup-auth/login/create-oauth2-client.md @@ -121,7 +121,7 @@ npm run deploy -- --workspace-id Run the following command to access your application: ```bash -tailor-sdk login --oauth2-client spa-client +tailor login --oauth2-client spa-client ``` You will be redirected to your IdP (e.g., Auth0) to authenticate. Log in with your credentials. @@ -147,7 +147,7 @@ In this section, you'll learn how to integrate OAuth 2.0 authentication into a s Run the following command to get the client_id and client_secret. ```bash -tailor-sdk oauth2client list +tailor oauth2client list ``` You will get the following response with the client credentials which is used in the Next.js app for OAuth2 client configuration. diff --git a/docs/tutorials/setup-auth/login/create-user.md b/docs/tutorials/setup-auth/login/create-user.md index 8d9abf0..8212304 100644 --- a/docs/tutorials/setup-auth/login/create-user.md +++ b/docs/tutorials/setup-auth/login/create-user.md @@ -44,7 +44,7 @@ Get an access token to use it in the GraphQL playground to run queries. Run the following command to get an access token: ```bash -tailor-sdk machineuser token admin-machine-user +tailor machineuser token admin-machine-user ``` Set the token in the Headers section of the playground as follows: diff --git a/docs/tutorials/setup-auth/register-identity-provider.md b/docs/tutorials/setup-auth/register-identity-provider.md index 354afaf..13399be 100644 --- a/docs/tutorials/setup-auth/register-identity-provider.md +++ b/docs/tutorials/setup-auth/register-identity-provider.md @@ -22,7 +22,7 @@ First, ensure you have a User type defined in your database schema (e.g., `db/us ```typescript import { db } from "@tailor-platform/sdk"; -export const user = db.type("User", { +export const user = db.table("User", { email: db.string().unique(), // usernameField must be unique name: db.string(), roles: db.array(db.string()).optional(), @@ -83,10 +83,10 @@ Before deploying, you need to store your client secret in the secret manager. Ru ```bash # Create a vault -tailor-sdk secret vault create my-vault +tailor secret vault create my-vault # Store the client secret -tailor-sdk secret create \ +tailor secret create \ --vault-name my-vault \ --name oidc-client-secret \ --value YOUR_CLIENT_SECRET diff --git a/docs/tutorials/setup-auth/setup-auth-connections.md b/docs/tutorials/setup-auth/setup-auth-connections.md index 1926d00..3cda16b 100644 --- a/docs/tutorials/setup-auth/setup-auth-connections.md +++ b/docs/tutorials/setup-auth/setup-auth-connections.md @@ -4,7 +4,7 @@ Auth connections enable your application to authenticate with external OAuth2 pr - To follow along, first complete the [SDK Quickstart](../../sdk/quickstart) and [Setting up Auth](overview). -This tutorial manages the connection through SDK config (`defineAuth()`), which is one of two supported approaches. You can instead create, authorize, and manage a connection entirely from the Console with `tailor-sdk authconnection open` — see [Setup Flow](/guides/auth/authconnection#setup-flow) in the AuthConnection guide for both options and why a given connection should only be managed by one of them. +This tutorial manages the connection through SDK config (`defineAuth()`), which is one of two supported approaches. You can instead create, authorize, and manage a connection entirely from the Console with `tailor authconnection open` — see [Setup Flow](/guides/auth/authconnection#setup-flow) in the AuthConnection guide for both options and why a given connection should only be managed by one of them. ## What you'll build @@ -60,10 +60,10 @@ Store `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` in your `.env` file or CI se ### 2. Deploy the Connection -Run `tailor-sdk deploy` to register the connection with the platform: +Run `tailor deploy` to register the connection with the platform: ```bash -tailor-sdk deploy +tailor deploy ``` This creates the connection record. The connection exists but is not yet authorized (it has no tokens yet). @@ -77,22 +77,22 @@ Redeploying updates an existing connection **in-place**, preserving the OAuth to Run the authorize command to complete the OAuth2 flow: ```bash -tailor-sdk authconnection authorize --name google-connection \ +tailor authconnection authorize --name google-connection \ --scopes "openid,profile,email" ``` This opens a browser tab for the OAuth2 consent screen. After you approve, the platform exchanges the authorization code for tokens and stores them securely. Your app code never handles the tokens directly. -You can also run `tailor-sdk authconnection open` to authorize from the Console instead of the local CLI flow: +You can also run `tailor authconnection open` to authorize from the Console instead of the local CLI flow: ```bash -tailor-sdk authconnection open +tailor authconnection open ``` Verify the connection is authorized: ```bash -tailor-sdk authconnection list +tailor authconnection list ``` ### 4. Use the Connection Token at Runtime @@ -133,20 +133,20 @@ The connection name is **type-checked** against the connections defined in `defi // authconnection.getConnectionToken("unknown-connection"); // ❌ TypeScript error ``` -Type narrowing comes from the generated `tailor.d.ts`. Run `tailor-sdk generate` (or `deploy`) after adding or renaming connections to refresh it. +Type narrowing comes from the generated `tailor.d.ts`. Run `tailor generate` (or `deploy`) after adding or renaming connections to refresh it. ### 5. Manage Connections via CLI ```bash # List all connections and their status -tailor-sdk authconnection list +tailor authconnection list # Re-authorize a connection (e.g. token expired or scopes changed) -tailor-sdk authconnection authorize --name google-connection \ +tailor authconnection authorize --name google-connection \ --scopes "openid,profile,email,https://www.googleapis.com/auth/calendar" # Revoke a connection -tailor-sdk authconnection revoke --name google-connection +tailor authconnection revoke --name google-connection ``` ## Complete Example: Calling an External API from an Executor diff --git a/docs/tutorials/setup-executor/incoming-webhook-trigger.md b/docs/tutorials/setup-executor/incoming-webhook-trigger.md index afff709..a2a1e61 100644 --- a/docs/tutorials/setup-executor/incoming-webhook-trigger.md +++ b/docs/tutorials/setup-executor/incoming-webhook-trigger.md @@ -151,7 +151,7 @@ https://api.erp.dev/v1/executor/workspaces/{WORKSPACE_ID}/executors/webhook-upda Alternatively, use the Tailor CLI: ```bash -npx tailor-sdk executor webhook list +npx tailor executor webhook list ``` **Step 2: Create a test project** From 250b8413eec8b48bad1169be808db9030bedfa0e Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Fri, 31 Jul 2026 22:01:03 +0900 Subject: [PATCH 2/4] style: auto-fix formatting (oxfmt) --- docs/guides/function/builtin-interfaces.md | 6 +++--- docs/guides/resolver.md | 6 +++--- docs/guides/tailordb/hooks.md | 4 +++- docs/guides/tailordb/validations.md | 11 ++++------- docs/guides/workflow/wait-resolve.md | 7 +++---- .../manage-data-schema/validate-field-data.md | 3 ++- 6 files changed, 18 insertions(+), 19 deletions(-) diff --git a/docs/guides/function/builtin-interfaces.md b/docs/guides/function/builtin-interfaces.md index 342317d..3f18809 100644 --- a/docs/guides/function/builtin-interfaces.md +++ b/docs/guides/function/builtin-interfaces.md @@ -180,8 +180,8 @@ const scoped = await tailor.workflow.execJobFunction( ``` | Function | Returns | Description | -| ----------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -| `startWorkflow(name, args?, options?)` | `Promise` | Start a workflow. Returns the execution ID | +| ---------------------------------------- | ----------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| `startWorkflow(name, args?, options?)` | `Promise` | Start a workflow. Returns the execution ID | | `execJobFunction(name, args?, options?)` | `Promise` | Execute a job function and return its result. `options.executionPolicyKey` routes the dispatch through a matching execution policy for per-key concurrency control | For details on declaring execution policies and the key grammar, see [Execution Policies](/sdk/services/workflow#execution-policies) in the SDK Workflow reference. @@ -270,7 +270,7 @@ await tailordb.file.delete("my-namespace", "Document", "attachment", recordId); **Note**: All methods take `(namespace, typeName, fieldName, recordId)` as the first four arguments. | Method | Returns | Description | -| --------------------------------------------- | ---------------------------------------- | ------------------------------------------------ | +| --------------------------------------------- | --------------------------------------- | ------------------------------------------------ | | `upload(..., data, options?)` | `Promise` | Upload a file | | `download(...)` | `Promise` | Download a file as `Uint8Array`. Throws if >10MB | | `downloadAsBase64(...)` | `Promise` | Download as Base64 string. Throws if >10MB | diff --git a/docs/guides/resolver.md b/docs/guides/resolver.md index 84a179a..a6aca5c 100644 --- a/docs/guides/resolver.md +++ b/docs/guides/resolver.md @@ -132,9 +132,9 @@ The SDK `createResolver` function defines a complete resolver with its input, ou The `body` function receives a `context` object with: -| Property | Description | -| --------------- | ----------------------------------------------- | -| `context.input` | The validated input arguments | +| Property | Description | +| ---------------- | ------------------------------------------------------------------------------- | +| `context.input` | The validated input arguments | | `context.caller` | The user or machine user who called the resolver, or `null` for anonymous calls | **Example** diff --git a/docs/guides/tailordb/hooks.md b/docs/guides/tailordb/hooks.md index 0f3af1c..b54e8b9 100644 --- a/docs/guides/tailordb/hooks.md +++ b/docs/guides/tailordb/hooks.md @@ -207,7 +207,9 @@ export const order = db totalPrice: db .int() .description("Total price of a certain product") - .validate(({ value }) => (value >= 100 ? "totalPrice value must be less than 100" : undefined)), + .validate(({ value }) => + value >= 100 ? "totalPrice value must be less than 100" : undefined, + ), }) .hooks({ create: ({ input }) => ({ totalPrice: input.price * input.quantity }), diff --git a/docs/guides/tailordb/validations.md b/docs/guides/tailordb/validations.md index 6db4490..406900e 100644 --- a/docs/guides/tailordb/validations.md +++ b/docs/guides/tailordb/validations.md @@ -61,17 +61,14 @@ The `Action` will be evaluated when the `Expr` returns true. ```typescript // reportNumber value must be less than 100 or over 103 -reportNumber: db - .int() - .validate(({ value }) => - value < 100 || value > 103 ? undefined : "reportNumber value must be less than 100 or over 103", - ); +reportNumber: db.int().validate(({ value }) => + value < 100 || value > 103 ? undefined : "reportNumber value must be less than 100 or over 103", +); ``` ```typescript // Description length should be less than 40 characters -description: db - .string() +description: db.string() .description("Description of the product.") .validate(({ value }) => value.length < 40 ? undefined : "Description length should be less than 40 characters.", diff --git a/docs/guides/workflow/wait-resolve.md b/docs/guides/workflow/wait-resolve.md index 7d15233..7ddcd38 100644 --- a/docs/guides/workflow/wait-resolve.md +++ b/docs/guides/workflow/wait-resolve.md @@ -146,10 +146,9 @@ A common implementation pattern involves three components: ```javascript // Pipeline resolver or executor function export async function main(args) { - const executionId = await tailor.workflow.startWorkflow( - "order-approval-workflow", - { orderId: args.orderId }, - ); + const executionId = await tailor.workflow.startWorkflow("order-approval-workflow", { + orderId: args.orderId, + }); // Save executionId to TailorDB for later retrieval await gql.mutation({ diff --git a/docs/tutorials/manage-data-schema/validate-field-data.md b/docs/tutorials/manage-data-schema/validate-field-data.md index 65a472d..e08f709 100644 --- a/docs/tutorials/manage-data-schema/validate-field-data.md +++ b/docs/tutorials/manage-data-schema/validate-field-data.md @@ -28,7 +28,8 @@ export const project = db.table("Project", { .description("Project name") .validate( ({ value }) => (value.length >= 3 ? undefined : "Project name must be at least 3 characters"), - ({ value }) => (value.length <= 50 ? undefined : "Project name must be 50 characters or less"), + ({ value }) => + value.length <= 50 ? undefined : "Project name must be 50 characters or less", ), description: db.string().optional().description("Project description"), status: db From f41df7f8046a175c138d5390b361720eb0f66210 Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 3 Aug 2026 08:45:12 +0900 Subject: [PATCH 3/4] docs(sdk): rename remaining TailorDB type references to table Fix chained db .type() call sites the earlier single-line sed missed, and align prose describing the schema-definition concept (headings, folder-structure comments, tutorial steps) with the v2 db.type() -> db.table() rename. Leaves literal config keys that are still named 'type' (relation()'s toward.type, executor trigger's type option) and field-level data-type wording (Field Types, GraphQL scalar types) unchanged, matching the published SDK's own reference docs. --- docs/getting-started/console/overview.md | 2 +- .../getting-started/core-concepts/services.md | 2 +- docs/getting-started/graphql.md | 16 +++++++-------- docs/guides/auth/app-login.md | 2 +- docs/guides/auth/overview.md | 2 +- docs/guides/integration/shopify.md | 4 ++-- docs/guides/resolver.md | 2 +- .../tailordb/advanced-settings/aggregation.md | 4 ++-- .../tailordb/advanced-settings/bulk-upsert.md | 6 +++--- .../tailordb/advanced-settings/overview.md | 4 ++-- .../advanced-settings/tailordb-cdc.md | 4 ++-- .../advanced-settings/uncountable-nouns.md | 10 +++++----- docs/guides/tailordb/fields.md | 4 ++-- docs/guides/tailordb/file-type.md | 2 +- docs/guides/tailordb/overview.md | 20 +++++++++---------- docs/guides/tailordb/versioning.md | 2 +- .../tutorials/develop-from-scratch/step-02.md | 6 +++--- docs/tutorials/index.md | 2 +- .../manage-data-schema/create-data-schema.md | 20 +++++++++---------- .../manage-data-schema/data-schema-basics.md | 14 ++++++------- .../manage-data-schema/modify-data-schema.md | 14 ++++++------- docs/tutorials/manage-data-schema/overview.md | 4 ++-- .../manage-data-schema/validate-field-data.md | 2 +- .../setup-executor/event-based-trigger.md | 12 +++++------ 24 files changed, 80 insertions(+), 80 deletions(-) diff --git a/docs/getting-started/console/overview.md b/docs/getting-started/console/overview.md index 2a8ae11..c3bf10f 100644 --- a/docs/getting-started/console/overview.md +++ b/docs/getting-started/console/overview.md @@ -62,7 +62,7 @@ tailor workspace #### 2. Update the configuration file -For example, to modify the `User` type with a new field, update your TailorDB type definition: +For example, to modify the `User` table with a new field, update your TailorDB table definition: ```typescript import { db } from "@tailor-platform/sdk"; diff --git a/docs/getting-started/core-concepts/services.md b/docs/getting-started/core-concepts/services.md index edeb145..e24a878 100644 --- a/docs/getting-started/core-concepts/services.md +++ b/docs/getting-started/core-concepts/services.md @@ -10,7 +10,7 @@ A flexible database service that provides: - **Type-safe schema definition** using the SDK - **Auto-generated GraphQL API** for CRUD operations -- **Relationships** between data types +- **Relationships** between data tables - **Hooks** for custom logic - **Permission-based access control** diff --git a/docs/getting-started/graphql.md b/docs/getting-started/graphql.md index 4432a14..a640878 100644 --- a/docs/getting-started/graphql.md +++ b/docs/getting-started/graphql.md @@ -16,7 +16,7 @@ They are similar to making a GET request in REST APIs. With GraphQL, you can spe ### Query Example -Let's assume we have an API for an e-commerce platform that has a `Product` type with fields like `id`, `name`, and `price`. +Let's assume we have an API for an e-commerce platform that has a `Product` table with fields like `id`, `name`, and `price`. Assume that the `name` field is of the `String` data type and the `price` field is of the `Integer` data type. Here's an example of a GraphQL query to request a list of products and their details: @@ -61,8 +61,8 @@ In this query, we are asking for a list of products and their `id`, `name`, and } ``` -You might notice that the name of the query: `products` is plural of the type name. -In Tailor Platform, `s` (in plural) will give you a list of the records in the ``. +You might notice that the name of the query: `products` is plural of the table name. +In Tailor Platform, `s` (in plural) will give you a list of the records in the `
`. `edges` contains an array of results. @@ -76,7 +76,7 @@ In GraphQL, you can request data from related objects or entities in a single qu ### Nested Object Example -Continuing with our e-commerce platform example, let's assume that the `Product` type has a related `Manufacturer` type with fields like `id`, `name`, `country`, and `directShipping`. You can request data from both the `Product` and the related `Manufacturer` in a single query. +Continuing with our e-commerce platform example, let's assume that the `Product` table has a related `Manufacturer` table with fields like `id`, `name`, `country`, and `directShipping`. You can request data from both the `Product` and the related `Manufacturer` in a single query. Here's an example of a GraphQL query requesting a list of products, their details, and the associated manufacturer information: @@ -155,7 +155,7 @@ Here are examples of GraphQL mutations for each of these operations: #### Create a new Product ```graphql {{ title: 'graphql'}} -# Create "Product C" and set the manufacturer "ABC Corp." with the id in the Manufacturer type +# Create "Product C" and set the manufacturer "ABC Corp." with the id in the Manufacturer table mutation { createProduct( input: { name: "Product C", price: 40, manufacturerId: "6a5e6f81-15fd-493a-b5d5-86a80a8f81db" } @@ -327,7 +327,7 @@ query { } ``` -In this query, we use the `aggregateProduct` query to perform an aggregation on the `price` field of the `Product` type. The `average` field is used to specify the aggregation operation, which in this case is calculating the average of all product prices. +In this query, we use the `aggregateProduct` query to perform an aggregation on the `price` field of the `Product` table. The `average` field is used to specify the aggregation operation, which in this case is calculating the average of all product prices. Aggregation queries can also be combined with filtering to further customize the data you retrieve and perform calculations on specific subsets of your data. @@ -346,13 +346,13 @@ query { } ``` -In this query, we use the `aggreagteOrder` query to perform an aggregation on the `Order` type. The `groupBy` syntax is used to group the orders by the `productId` field. For each group (each unique product), we calculate the sum of the `total` field, which represents the revenue for that specific product. We alias the result as `totalRevenue`. +In this query, we use the `aggreagteOrder` query to perform an aggregation on the `Order` table. The `groupBy` syntax is used to group the orders by the `productId` field. For each group (each unique product), we calculate the sum of the `total` field, which represents the revenue for that specific product. We alias the result as `totalRevenue`. The response will include a list of grouped results, where each group is identified by a unique `key` (the `productId` in this case), and the corresponding aggregated values, such as the total revenue for that specific product. Using `groupBy` with aggregation queries in GraphQL allows you to perform calculations on your data while grouping it by a specific field, which can be helpful for generating summary statistics, comparisons, or insights based on different categories or attributes. -To use the aggregation query, you'll need to enable advanced APIs. For example, to enable aggregation on the `Order` type +To use the aggregation query, you'll need to enable advanced APIs. For example, to enable aggregation on the `Order` table ```typescript db.table("Order", { diff --git a/docs/guides/auth/app-login.md b/docs/guides/auth/app-login.md index 94ffb13..cd94db9 100644 --- a/docs/guides/auth/app-login.md +++ b/docs/guides/auth/app-login.md @@ -63,7 +63,7 @@ Properties | - usernameField | The field that contains the username. | | - attributes | Map of attributes to include in the user profile. | -With this `attributes` configuration, the Auth service fetches the `roles` field from the `user` type and assigns its value to the roles attribute for permission checks. +With this `attributes` configuration, the Auth service fetches the `roles` field from the `user` table and assigns its value to the roles attribute for permission checks. This configuration tells Tailor Platform: diff --git a/docs/guides/auth/overview.md b/docs/guides/auth/overview.md index ff39ca5..fd45598 100644 --- a/docs/guides/auth/overview.md +++ b/docs/guides/auth/overview.md @@ -470,7 +470,7 @@ Here's an example of a User type configured for Auth integration: import { db } from "@tailor-platform/sdk"; export const user = db - .type("User", { + .table("User", { name: db.string({ description: "Name of the user", index: true, diff --git a/docs/guides/integration/shopify.md b/docs/guides/integration/shopify.md index d6085a2..c84d5ed 100644 --- a/docs/guides/integration/shopify.md +++ b/docs/guides/integration/shopify.md @@ -55,7 +55,7 @@ Treat your API key as a sensitive data and never share it publicly or commit it Follow the below steps to create an event based trigger. -1. Enable `PublishRecordEvents` settings in the `Products` type +1. Enable `PublishRecordEvents` settings in the `Products` table To enable event publishing for Product records, add the `publishRecordEvents` feature to your Product type: @@ -63,7 +63,7 @@ To enable event publishing for Product records, add the `publishRecordEvents` fe import { db } from "@tailor-platform/sdk"; export const product = db - .type("Product", "Product data schema", { + .table("Product", "Product data schema", { name: db.string().description("Product name"), description: db.string().description("Product description"), // ... other fields diff --git a/docs/guides/resolver.md b/docs/guides/resolver.md index a6aca5c..954606f 100644 --- a/docs/guides/resolver.md +++ b/docs/guides/resolver.md @@ -112,7 +112,7 @@ export default createResolver({ ### Caveats - `Subscription` operations are not supported, only `Mutation` and `Query` may be used. -- The name of the types **MUST NOT** collide with the existing types' (e.g. `User`, etc.) as well as auto-generated operations (e.g. `createUser`, etc.). +- The name of the resolver **MUST NOT** collide with the existing tables' (e.g. `User`, etc.) as well as auto-generated operations (e.g. `createUser`, etc.). ## Resolver diff --git a/docs/guides/tailordb/advanced-settings/aggregation.md b/docs/guides/tailordb/advanced-settings/aggregation.md index 37d5870..78009b0 100644 --- a/docs/guides/tailordb/advanced-settings/aggregation.md +++ b/docs/guides/tailordb/advanced-settings/aggregation.md @@ -16,7 +16,7 @@ Available operations are `count`, `max`, `min`, `sum`, `avg`, `totalSize`, and ` import { db } from "@tailor-platform/sdk"; export const payroll = db - .type("Payroll", "payroll model", { + .table("Payroll", "payroll model", { name: db.string().description("name"), paidAt: db.datetime().description("paidAt").required(), paidAmount: db.int().description("paidAmount").required(), @@ -30,7 +30,7 @@ export const payroll = db ``` TailorDB auto-generates `aggregatePayrolls`, allowing you to quickly retrieve summarized results for Payroll.\ -Based on the `payroll` type, here's the sample query to aggregate the payroll data: +Based on the `payroll` table, here's the sample query to aggregate the payroll data: ```graphql {{ title: 'query' }} query { diff --git a/docs/guides/tailordb/advanced-settings/bulk-upsert.md b/docs/guides/tailordb/advanced-settings/bulk-upsert.md index 8ae226e..28a6cb6 100644 --- a/docs/guides/tailordb/advanced-settings/bulk-upsert.md +++ b/docs/guides/tailordb/advanced-settings/bulk-upsert.md @@ -11,7 +11,7 @@ We can enable the `bulkUpsert` setting for your type, which will automatically g import { db } from "@tailor-platform/sdk"; export const document = db - .type("Document", "Document model", { + .table("Document", "Document model", { // ...fields }) .features({ @@ -60,13 +60,13 @@ The maximum number of items that can be included in a single bulk upsert operati ## BulkUpsert with PluralForm settings -When `PluralForm` is configured in the settings, as mentioned in the example below, TailorDB auto-generates `bulkUpsertUserDataList` instead of `bulkUpsertUserData` for bulk upserting data of the `UserData` type. Refer [PluralForm](/guides/tailordb/advanced-settings/uncountable-nouns) to learn about its setting. +When `PluralForm` is configured in the settings, as mentioned in the example below, TailorDB auto-generates `bulkUpsertUserDataList` instead of `bulkUpsertUserData` for bulk upserting data of the `UserData` table. Refer [PluralForm](/guides/tailordb/advanced-settings/uncountable-nouns) to learn about its setting. ```typescript import { db } from "@tailor-platform/sdk"; export const userData = db - .type("UserData", "UserData model", { + .table("UserData", "UserData model", { // ...fields }) .features({ diff --git a/docs/guides/tailordb/advanced-settings/overview.md b/docs/guides/tailordb/advanced-settings/overview.md index 1f7e1d7..c7f3e7f 100644 --- a/docs/guides/tailordb/advanced-settings/overview.md +++ b/docs/guides/tailordb/advanced-settings/overview.md @@ -41,7 +41,7 @@ For example, we can define the directive of `@key(fields: "id")` as the followin import { db } from "@tailor-platform/sdk"; export const myType = db - .type("MyType", "My type with directives", { + .table("MyType", "My type with directives", { // ...fields }) .directives([ @@ -83,7 +83,7 @@ import { db } from "@tailor-platform/sdk"; // Example of extending State type export const state = db - .type("State", "State type with extends", { + .table("State", "State type with extends", { // ...fields }) .extends(true) diff --git a/docs/guides/tailordb/advanced-settings/tailordb-cdc.md b/docs/guides/tailordb/advanced-settings/tailordb-cdc.md index e49c9fc..33aa167 100644 --- a/docs/guides/tailordb/advanced-settings/tailordb-cdc.md +++ b/docs/guides/tailordb/advanced-settings/tailordb-cdc.md @@ -6,7 +6,7 @@ doc_type: guide ## Publish record events for database triggers -Change data capture (CDC) enables you to track and capture all changes in your data within TailorDB, including new record insertions, updates to existing records, and deletions. To use this feature, enable `PublishRecordEvents` in the settings for your desired data type in TailorDB. Once enabled, any changes to records of this type will trigger the publication of record events. These events are then processed by the corresponding `Executor` service, which initiates the jobs defined based on these changes. +Change data capture (CDC) enables you to track and capture all changes in your data within TailorDB, including new record insertions, updates to existing records, and deletions. To use this feature, enable `PublishRecordEvents` in the settings for your desired table in TailorDB. Once enabled, any changes to records of this table will trigger the publication of record events. These events are then processed by the corresponding `Executor` service, which initiates the jobs defined based on these changes. For example, to call a trigger for every PurchaseOrder type record created, you'll need to enable `PublishRecordEvents` in the settings. Refer [Event based trigger](/tutorials/setup-executor/event-based-trigger#4enablepublishrecordeventssettings) to learn more. @@ -14,7 +14,7 @@ For example, to call a trigger for every PurchaseOrder type record created, you' import { db } from "@tailor-platform/sdk"; export const purchaseOrder = db - .type( + .table( "PurchaseOrder", "Model for Purchase Order. Each record of the Purchase Order represents unique combination of a Product, a Location, and a Supplier.", { diff --git a/docs/guides/tailordb/advanced-settings/uncountable-nouns.md b/docs/guides/tailordb/advanced-settings/uncountable-nouns.md index 2620cd1..c279d0f 100644 --- a/docs/guides/tailordb/advanced-settings/uncountable-nouns.md +++ b/docs/guides/tailordb/advanced-settings/uncountable-nouns.md @@ -4,16 +4,16 @@ doc_type: guide # Plural form for uncountable nouns -The `PluralForm` field accepts the plural form for an uncountable noun type in your TailorDB. -This setting is necessary to access individual records when the type name is an uncountable noun. +The `PluralForm` field accepts the plural form for an uncountable noun table in your TailorDB. +This setting is necessary to access individual records when the table name is an uncountable noun. -For example, to create type `userData` in the TailorDB, configure the `PluralForm` in the settings as the following: +For example, to create table `userData` in the TailorDB, configure the `PluralForm` in the settings as the following: ```typescript import { db } from "@tailor-platform/sdk"; export const userData = db - .type("UserData", "User Data Model", { + .table("UserData", "User Data Model", { name: db.string().description("Name of the user"), ...db.fields.timestamps(), }) @@ -37,7 +37,7 @@ query { } ``` -Here is the query to retrieve individual records of the UserData type: +Here is the query to retrieve individual records of the UserData table: ```graphql query{ diff --git a/docs/guides/tailordb/fields.md b/docs/guides/tailordb/fields.md index f24d7c7..1625e55 100644 --- a/docs/guides/tailordb/fields.md +++ b/docs/guides/tailordb/fields.md @@ -205,9 +205,9 @@ inventoryType: db.enum([ If the field data type is `TypeNested`, use this to define the nested fields within the structure. The data type of the field is the same as that of the parent field. -### SourceId (used for linking other Type) +### SourceId (used for linking other Table) -If you want to link the data from different type, you can use the SourceId field to create the link. +If you want to link the data from a different table, you can use the SourceId field to create the link. The data type of `SourceId` field is `UUID`. #### Example diff --git a/docs/guides/tailordb/file-type.md b/docs/guides/tailordb/file-type.md index c54f5af..365f37e 100644 --- a/docs/guides/tailordb/file-type.md +++ b/docs/guides/tailordb/file-type.md @@ -22,7 +22,7 @@ TailorDB File Type enables developers to attach files to records in their data m ## Schema Definition -Files are defined in the **Files** section (not Fields) of your type definition. Each file type field in a record has a reserved URL endpoint that serves as both the upload and download location for that specific field. This URL is automatically generated when the record is created and remains constant throughout the record's lifecycle. +Files are defined in the **Files** section (not Fields) of your table definition. Each file type field in a record has a reserved URL endpoint that serves as both the upload and download location for that specific field. This URL is automatically generated when the record is created and remains constant throughout the record's lifecycle. ```typescript {{ title: "Schema Definition" }} db.table("Starship", { diff --git a/docs/guides/tailordb/overview.md b/docs/guides/tailordb/overview.md index 2cf1733..f7d5416 100644 --- a/docs/guides/tailordb/overview.md +++ b/docs/guides/tailordb/overview.md @@ -21,18 +21,18 @@ Below is a typical folder structure for a Tailor Platform project using the SDK: my-app/ ├── tailor.config.ts # Configuration file ├── tailordb/ -│ ├── index.ts # Exports all types -│ ├── product.ts # Product type definition -│ └── category.ts # Category type definition +│ ├── index.ts # Exports all tables +│ ├── product.ts # Product table definition +│ └── category.ts # Category table definition ├── auth/ │ └── index.ts # Auth configuration └── pipeline/ └── index.ts # Pipeline resolvers ``` -### Defining a Type +### Defining a Table -Here's how to define a `Product` type using the SDK: +Here's how to define a `Product` table using the SDK: ```typescript import { db } from "@tailor-platform/sdk"; @@ -56,7 +56,7 @@ The SDK provides: import { db } from "@tailor-platform/sdk"; export const product = db - .type("Product", "Product data schema", { + .table("Product", "Product data schema", { title: db.string().description("Title of the product").index(), description: db.string().description("Description of the product"), }) @@ -116,9 +116,9 @@ In TailorDB, the GraphQL APIs are [automatically generated](/guides/tailordb/aut ### Basic Structure -A TailorDB type definition consists of: +A TailorDB table definition consists of: -- **Type name** (Required): The name of the data model +- **Table name** (Required): The name of the data model - **Description** (Optional): Description shown in GraphQL SDL and playground - **Fields** (Required): Field definitions with types and modifiers @@ -158,7 +158,7 @@ See [Permission](permission) for more information. ## Advanced Features -### Type Features +### Table Features Enable additional GraphQL operations with the `.features()` modifier: @@ -195,7 +195,7 @@ See [Indexes](indexes) for more information. ## Next Steps - [Fields](fields) - Detailed field type configuration -- [Relationships](relationships) - Define relations between types +- [Relationships](relationships) - Define relations between tables - [Hooks](hooks) - Add custom logic on create/update - [Validations](validations) - Field-level data validation - [Permission](permission) - Access control configuration diff --git a/docs/guides/tailordb/versioning.md b/docs/guides/tailordb/versioning.md index 8977f5f..7ef3b1b 100644 --- a/docs/guides/tailordb/versioning.md +++ b/docs/guides/tailordb/versioning.md @@ -18,7 +18,7 @@ This approach preserves historical data and enables change monitoring and analys ### 1. Create a history table -Let's create a history table for the `StockSummary` type to log its data changes. +Let's create a history table for the `StockSummary` table to log its data changes. ```typescript db.table("StockSummaryHistory", { diff --git a/docs/tutorials/develop-from-scratch/step-02.md b/docs/tutorials/develop-from-scratch/step-02.md index 87b4e05..3d5c127 100644 --- a/docs/tutorials/develop-from-scratch/step-02.md +++ b/docs/tutorials/develop-from-scratch/step-02.md @@ -69,7 +69,7 @@ import { db } from "@tailor-platform/sdk"; import { gqlPermissionLoggedIn, permissionLoggedIn } from "../common/permission"; export const user = db - .type("User", { + .table("User", { name: db.string().description("Name of the user"), email: db.string().unique().description("Email address of the user"), role: db.enum(["MANAGER", "STAFF"]), @@ -84,7 +84,7 @@ import { db } from "@tailor-platform/sdk"; import { gqlPermissionLoggedIn, permissionLoggedIn } from "../common/permission"; export const project = db - .type("Project", { + .table("Project", { name: db.string().description("Name of the Project"), description: db.string({ optional: true }).description("Description of the project"), status: db.enum([ @@ -110,7 +110,7 @@ import { user } from "./user"; import { gqlPermissionLoggedIn, permissionLoggedIn } from "../common/permission"; export const task = db - .type("Task", { + .table("Task", { name: db.string().description("Name of the Task"), description: db.string({ optional: true }).description("Description of the task"), projectId: db diff --git a/docs/tutorials/index.md b/docs/tutorials/index.md index 52fe8e9..e1c0bf1 100644 --- a/docs/tutorials/index.md +++ b/docs/tutorials/index.md @@ -13,7 +13,7 @@ These well-structured examples can be modified to suit various templates.
- Learn how to define, modify, and validate your data types with TailorDB. Create relationships and enforce data integrity. + Learn how to define, modify, and validate your data tables with TailorDB. Create relationships and enforce data integrity. diff --git a/docs/tutorials/manage-data-schema/create-data-schema.md b/docs/tutorials/manage-data-schema/create-data-schema.md index 000ff9c..04ab261 100644 --- a/docs/tutorials/manage-data-schema/create-data-schema.md +++ b/docs/tutorials/manage-data-schema/create-data-schema.md @@ -1,6 +1,6 @@ # Adding a New Data Model -With Tailor Platform's TailorDB, you can easily add new types (data models) to your application. The GraphQL API will be automatically generated for the new type. +With Tailor Platform's TailorDB, you can easily add new tables (data models) to your application. The GraphQL API will be automatically generated for the new table. This tutorial demonstrates how to create a new data schema using the SDK. @@ -9,7 +9,7 @@ This tutorial demonstrates how to create a new data schema using the SDK. ## Tutorial Steps -To create a `Task` type in TailorDB, you'll need to: +To create a `Task` table in TailorDB, you'll need to: 1. Define the `Task` schema 2. Deploy the change @@ -17,7 +17,7 @@ To create a `Task` type in TailorDB, you'll need to: ### 1. Define the Task Schema -Create a new file `db/task.ts` in your project and define the Task type: +Create a new file `db/task.ts` in your project and define the Task table: ```typescript import { db } from "@tailor-platform/sdk"; @@ -48,7 +48,7 @@ export type task = typeof task; - **description**: Optional detailed description - **status**: Enumeration with predefined status values - **priority**: Enumeration for priority levels -- **projectId**: Many-to-one relation to the Project type +- **projectId**: Many-to-one relation to the Project table - **assigneeId**: Optional reference to a team member - **dueDate**: Optional date field - **estimatedHours**: Optional float for time estimation @@ -58,7 +58,7 @@ Fields like `id` are automatically generated without being explicitly defined. S ### 2. Deploy the Change -Deploy the new Task type to your workspace: +Deploy the new Task table to your workspace: ```bash npm run deploy -- --workspace-id @@ -66,13 +66,13 @@ npm run deploy -- --workspace-id The SDK will: -- Detect the new type definition +- Detect the new table definition - Create the Task table in TailorDB -- Generate the GraphQL API for the Task type +- Generate the GraphQL API for the Task table ### 3. Verify Schema Change Through GraphQL -The SDK automatically generates the following GraphQL APIs for the `Task` type: +The SDK automatically generates the following GraphQL APIs for the `Task` table: - **Queries**: `task`, `tasks` - Fetch task data - **Mutations**: `createTask`, `updateTask`, `deleteTask` - Modify task data @@ -183,9 +183,9 @@ query { ## Next Steps -Now that you've created a new type, explore more advanced features: +Now that you've created a new table, explore more advanced features: -- [Relationships](../../sdk/services/tailordb#relationships) - Define relations between types +- [Relationships](../../sdk/services/tailordb#relationships) - Define relations between tables - [Field Types](../../sdk/services/tailordb#field-types) - Learn about all available field types - [Indexes](../../sdk/services/tailordb#indexes) - Optimize query performance - [Hooks](../../sdk/services/tailordb#hooks) - Add automatic field population diff --git a/docs/tutorials/manage-data-schema/data-schema-basics.md b/docs/tutorials/manage-data-schema/data-schema-basics.md index 1b59910..34aadc0 100644 --- a/docs/tutorials/manage-data-schema/data-schema-basics.md +++ b/docs/tutorials/manage-data-schema/data-schema-basics.md @@ -6,17 +6,17 @@ This tutorial demonstrates how a data schema is mapped to GraphQL APIs using the ## Data Schema -A data model of your application is defined by the schema. Each type is typically defined in its own file for readability. See [TailorDB Documentation](../../sdk/services/tailordb) to learn more about schema definition. +A data model of your application is defined by the schema. Each table is typically defined in its own file for readability. See [TailorDB Documentation](../../sdk/services/tailordb) to learn more about schema definition. ## Tutorial Steps -1. Define a data type +1. Define a data table 2. View the schema in GraphQL playground 3. See how the configuration maps to GraphQL APIs -### 1. Define a Data Type +### 1. Define a Data Table -Create a `Project` type in your application. In your project's `db/` directory, create a file called `project.ts`: +Create a `Project` table in your application. In your project's `db/` directory, create a file called `project.ts`: ```typescript import { db } from "@tailor-platform/sdk"; @@ -36,7 +36,7 @@ export const project = db.table("Project", { export type project = typeof project; ``` -This TypeScript file defines a `Project` type with various fields: +This TypeScript file defines a `Project` table with various fields: - **name**: Required string field for the project name - **description**: Optional string field for project details @@ -97,6 +97,6 @@ Learn more about working with data schemas: - [TailorDB Service](../../sdk/services/tailordb) - Complete TailorDB reference - [Field Types](../../sdk/services/tailordb#field-types) - Available field types and options -- [Relationships](../../sdk/services/tailordb#relationships) - Define relations between types +- [Relationships](../../sdk/services/tailordb#relationships) - Define relations between tables - [Modifying Data Schema](modify-data-schema) - How to update existing schemas -- [Creating Data Schema](create-data-schema) - Add new types to your application +- [Creating Data Schema](create-data-schema) - Add new tables to your application diff --git a/docs/tutorials/manage-data-schema/modify-data-schema.md b/docs/tutorials/manage-data-schema/modify-data-schema.md index 0e5bdc0..7da329d 100644 --- a/docs/tutorials/manage-data-schema/modify-data-schema.md +++ b/docs/tutorials/manage-data-schema/modify-data-schema.md @@ -1,8 +1,8 @@ # Adding a New Field to Data Model -Tailor Platform makes it easy to modify data schemas. With TailorDB, you can modify existing data types and the GraphQL endpoint will be automatically generated. +Tailor Platform makes it easy to modify data schemas. With TailorDB, you can modify existing data tables and the GraphQL endpoint will be automatically generated. -This tutorial demonstrates how to add a new field to an existing data type using the SDK. +This tutorial demonstrates how to add a new field to an existing data table using the SDK. - See [Core Concepts](/getting-started/core-concepts/) to get an overview of Workspace, Organization, Application and Service. - To follow along with this tutorial, first complete the [SDK Quickstart](../../sdk/quickstart) and the [Data Schema Basics](data-schema-basics) tutorial. @@ -13,13 +13,13 @@ TailorDB is a service that enables you to manage data schemas and automatically ## Tutorial Steps -To add a new `teamSize` field to the `Project` type, you will: +To add a new `teamSize` field to the `Project` table, you will: -1. Add the `teamSize` field to the `Project` type +1. Add the `teamSize` field to the `Project` table 2. Deploy the change 3. Verify the schema change through GraphQL -### 1. Add the `teamSize` Field to the Project Type +### 1. Add the `teamSize` Field to the Project Table Open your `db/project.ts` file and add the new field: @@ -61,7 +61,7 @@ npm run deploy -- --workspace-id The SDK will: - Detect the schema changes -- Update the TailorDB type +- Update the TailorDB table - Regenerate the GraphQL API with the new field ### 3. Verify Schema Change Through GraphQL @@ -136,7 +136,7 @@ Example response: Now that you've learned how to add a field, you can proceed to: -- [Creating Data Schema](create-data-schema) - Learn how to create new types +- [Creating Data Schema](create-data-schema) - Learn how to create new tables - [Field Types](../../sdk/services/tailordb#field-types) - Explore all available field types - [Field Validation](../../sdk/services/tailordb#validation) - Add validation rules to fields diff --git a/docs/tutorials/manage-data-schema/overview.md b/docs/tutorials/manage-data-schema/overview.md index c142abd..1dadc03 100644 --- a/docs/tutorials/manage-data-schema/overview.md +++ b/docs/tutorials/manage-data-schema/overview.md @@ -2,9 +2,9 @@ Tailor Platform offers a powerful feature that allows you to generate GraphQL APIs from schema files. This makes it easy for your company to create a custom data schema that aligns perfectly with your business requirements. -The main database in Tailor Platform, where you store and retrieve your data, is called TailorDB. With TailorDB, you can effortlessly modify existing data schemas (data models) in our templates or add new Types (equivalent to Tables in a Relational Database). The GraphQL endpoint will be automatically generated, eliminating the need for additional coding. +The main database in Tailor Platform, where you store and retrieve your data, is called TailorDB. With TailorDB, you can effortlessly modify existing data schemas (data models) in our templates or add new tables (equivalent to tables in a relational database). The GraphQL endpoint will be automatically generated, eliminating the need for additional coding. -This tutorial introduces you to TailorDB. In the subsequent tutorials, you'll learn how to add fields to existing types, how to create new types and how to validate fields for your application. +This tutorial introduces you to TailorDB. In the subsequent tutorials, you'll learn how to add fields to existing tables, how to create new tables and how to validate fields for your application. ## What you'll learn diff --git a/docs/tutorials/manage-data-schema/validate-field-data.md b/docs/tutorials/manage-data-schema/validate-field-data.md index e08f709..f313e0d 100644 --- a/docs/tutorials/manage-data-schema/validate-field-data.md +++ b/docs/tutorials/manage-data-schema/validate-field-data.md @@ -306,5 +306,5 @@ Learn more about validation and schema design: - [Field Validation](../../sdk/services/tailordb#validation) - Complete validation reference - [Field Types](../../sdk/services/tailordb#field-types) - All available field types -- [Creating Data Schema](create-data-schema) - Learn how to create new types +- [Creating Data Schema](create-data-schema) - Learn how to create new tables - [Best Practices](../../sdk/services/tailordb#best-practices) - Schema design guidelines diff --git a/docs/tutorials/setup-executor/event-based-trigger.md b/docs/tutorials/setup-executor/event-based-trigger.md index 64e9723..3e7dd1a 100644 --- a/docs/tutorials/setup-executor/event-based-trigger.md +++ b/docs/tutorials/setup-executor/event-based-trigger.md @@ -10,7 +10,7 @@ To create an event-based trigger, you'll need to: 1. Configure the Executor service 2. Create the executor with a record updated trigger -3. Enable `publishEvents` feature on the Project type +3. Enable `publishEvents` feature on the Project table 4. Deploy the changes 5. Verify the trigger @@ -92,7 +92,7 @@ export default createExecutor({ **Key Components:** 1. **Trigger**: `recordUpdatedTrigger()` fires when a Project record is updated - - `type: project`: Specifies which type to monitor + - `type: project`: Specifies which table to monitor - `condition`: Only triggers when status changes TO "COMPLETED" (not already completed) 2. **Operation**: `webhook` sends HTTP POST request to Slack @@ -103,7 +103,7 @@ export default createExecutor({ ### 3. Enable `publishEvents` Feature -To ensure the executor triggers when Project records are updated, enable `publishEvents` in your Project type definition. +To ensure the executor triggers when Project records are updated, enable `publishEvents` in your Project table definition. Update your `db/project.ts` file: @@ -111,7 +111,7 @@ Update your `db/project.ts` file: import { db } from "@tailor-platform/sdk"; export const project = db - .type("Project", { + .table("Project", { name: db.string().description("Project name"), description: db.string().optional().description("Project description"), status: db @@ -150,7 +150,7 @@ Deploy your application: npm run deploy -- --workspace-id ``` -The SDK will deploy both the updated Project type with `publishEvents` enabled and the new executor. +The SDK will deploy both the updated Project table with `publishEvents` enabled and the new executor. ### 5. Verify the Trigger @@ -219,7 +219,7 @@ Clicking `View Attempts` displays details of job execution attempts. The Tailor **Troubleshooting:** - **No notification received**: Verify your Slack webhook URL is correct -- **Executor not triggering**: Ensure `publishEvents: true` is set in `.features()` on the Project type +- **Executor not triggering**: Ensure `publishEvents: true` is set in `.features()` on the Project table - **Error in logs**: Check the executor logs in the Console for detailed error messages ## Multi-Event Triggers From 2bb5e4bdf42c6026f324da44e221df7e3c208f3b Mon Sep 17 00:00:00 2001 From: Akira HIGUCHI Date: Mon, 3 Aug 2026 08:53:08 +0900 Subject: [PATCH 4/4] docs(sdk): migrate defineGenerators to definePlugins, fix broken Generator link MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The SDK's own docs (auto-synced under docs/sdk/) removed docs/sdk/generator/ in the latest sync — defineGenerators() is replaced by definePlugins() in v2, with the kysely-type generator becoming the kyselyTypePlugin plugin. - Point the hand-maintained docs/sdk/index.md 'Generator' quick link at the new plugin/ docs instead of the now-removed generator/ path (was breaking the VitePress dead-link check on CI). - Update the develop-from-scratch step-03/step-04 tailor.config.ts samples from defineGenerators(["@tailor-platform/kysely-type", ...]) to definePlugins(kyselyTypePlugin({ ... })). --- docs/sdk/index.md | 2 +- docs/tutorials/develop-from-scratch/step-03.md | 10 ++++------ docs/tutorials/develop-from-scratch/step-04.md | 8 +++----- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/docs/sdk/index.md b/docs/sdk/index.md index 6489621..54d8f93 100644 --- a/docs/sdk/index.md +++ b/docs/sdk/index.md @@ -18,4 +18,4 @@ Learn how to build apps with the Tailor Platform SDK. - [Resolver](services/resolver) - Custom GraphQL resolver service - [Executor](services/executor) - Event-driven automation service - [Workflow](services/workflow) - Job orchestration service -- [Generator](generator/) - Code generation tools for builtin and custom generators +- [Plugin](plugin/) - Extend TailorDB types with plugin-generated types, executors, and output files diff --git a/docs/tutorials/develop-from-scratch/step-03.md b/docs/tutorials/develop-from-scratch/step-03.md index 6f1e69f..4bc63ff 100644 --- a/docs/tutorials/develop-from-scratch/step-03.md +++ b/docs/tutorials/develop-from-scratch/step-03.md @@ -6,12 +6,13 @@ Add a `closeProject` mutation that cancels incomplete tasks and closes a project ## Configuration Files -Changes from Step 2: added `admin` machine user, `resolver` namespace, and `generators` export. +Changes from Step 2: added `admin` machine user, `resolver` namespace, and `plugins` export. > Since the `admin` machine user uses `role: "ADMIN"`, update `src/db/user.ts` to include it: `db.enum(["MANAGER", "STAFF", "ADMIN"])`. ```typescript {{title: 'tailor.config.ts'}} -import { defineAuth, defineConfig, defineGenerators } from "@tailor-platform/sdk"; +import { defineAuth, defineConfig, definePlugins } from "@tailor-platform/sdk"; +import { kyselyTypePlugin } from "@tailor-platform/sdk/plugin/kysely-type"; import { user } from "./src/db/user"; export default defineConfig({ @@ -40,10 +41,7 @@ export default defineConfig({ resolver: { "main-resolver": { files: [`./src/resolver/*.ts`] } }, }); -export const generators = defineGenerators([ - "@tailor-platform/kysely-type", - { distPath: `./src/generated/tailordb.ts` }, -]); +export const plugins = definePlugins(kyselyTypePlugin({ distPath: `./src/generated/tailordb.ts` })); ``` > For optional output fields, use `t.string({ optional: true })`, not `t.string().optional()`. The `.optional()` chain does not exist. diff --git a/docs/tutorials/develop-from-scratch/step-04.md b/docs/tutorials/develop-from-scratch/step-04.md index 79507a8..9cc68d8 100644 --- a/docs/tutorials/develop-from-scratch/step-04.md +++ b/docs/tutorials/develop-from-scratch/step-04.md @@ -9,7 +9,8 @@ Add an executor that sends a Slack notification whenever a Task is created. Only change from Step 3: added `executor` to `tailor.config.ts`. ```typescript {{title: 'tailor.config.ts'}} -import { defineAuth, defineConfig, defineGenerators } from "@tailor-platform/sdk"; +import { defineAuth, defineConfig, definePlugins } from "@tailor-platform/sdk"; +import { kyselyTypePlugin } from "@tailor-platform/sdk/plugin/kysely-type"; import { user } from "./src/db/user"; export default defineConfig({ @@ -39,10 +40,7 @@ export default defineConfig({ executor: { files: ["./src/executor/*.ts"] }, }); -export const generators = defineGenerators([ - "@tailor-platform/kysely-type", - { distPath: `./src/generated/tailordb.ts` }, -]); +export const plugins = definePlugins(kyselyTypePlugin({ distPath: `./src/generated/tailordb.ts` })); ``` `createExecutor` takes an object config with `name`, `trigger`, and `operation`. The trigger `recordCreatedTrigger` takes `{ type: task }`. Available trigger context: `newRecord` (created), `oldRecord`/`newRecord` (updated), `oldRecord` (deleted).