@tailor-platform/sdk 0.16.3 → 0.17.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,225 @@
1
+ # Auth
2
+
3
+ Auth is a service for configuring authentication and authorization in your Tailor Platform application.
4
+
5
+ ## Overview
6
+
7
+ Auth provides:
8
+
9
+ - User profile mapping to TailorDB types
10
+ - Machine users for service-to-service authentication
11
+ - OAuth 2.0 client configuration
12
+ - Identity provider integration
13
+
14
+ For the official Tailor Platform documentation, see [Auth Guide](https://docs.tailor.tech/guides/auth/overview).
15
+
16
+ ## Configuration
17
+
18
+ Configure Auth service using `defineAuth()`:
19
+
20
+ ```typescript
21
+ import { defineAuth } from "@tailor-platform/sdk";
22
+ import { user } from "./tailordb/user";
23
+
24
+ const auth = defineAuth("my-auth", {
25
+ userProfile: {
26
+ type: user,
27
+ usernameField: "email",
28
+ attributes: { role: true },
29
+ },
30
+ machineUsers: {
31
+ "admin-machine-user": {
32
+ attributes: { role: "ADMIN" },
33
+ },
34
+ },
35
+ oauth2Clients: {
36
+ "my-oauth2-client": {
37
+ redirectURIs: ["https://example.com/callback"],
38
+ grantTypes: ["authorization_code", "refresh_token"],
39
+ },
40
+ },
41
+ idProvider: idp.provider("my-provider", "my-client"),
42
+ });
43
+
44
+ export default defineConfig({
45
+ auth,
46
+ });
47
+ ```
48
+
49
+ ## User Profile
50
+
51
+ Maps authenticated identities to a TailorDB type:
52
+
53
+ ```typescript
54
+ userProfile: {
55
+ type: user, // TailorDB type for user records
56
+ usernameField: "email", // Field used as username
57
+ attributes: {
58
+ role: true, // Enable 'role' as a user attribute
59
+ },
60
+ },
61
+ ```
62
+
63
+ **type**: The TailorDB type that stores user records.
64
+
65
+ **usernameField**: The field in the TailorDB type used as the username.
66
+
67
+ **attributes**: Specifies which fields from the TailorDB type are used as user attributes. Set to `true` to enable a field. Enabled attributes must be assigned values in all machine user definitions.
68
+
69
+ ## Machine Users
70
+
71
+ Service accounts for automated access without user interaction:
72
+
73
+ ```typescript
74
+ machineUsers: {
75
+ "admin-machine-user": {
76
+ attributes: { role: "ADMIN" },
77
+ },
78
+ "readonly-machine-user": {
79
+ attributes: { role: "READER" },
80
+ },
81
+ },
82
+ ```
83
+
84
+ **attributes**: Values for attributes enabled in `userProfile.attributes`. All fields marked as `true` in `userProfile.attributes` must be set here. These values are accessible via `user.attributes`:
85
+
86
+ ```typescript
87
+ // In a resolver
88
+ body: (context) => {
89
+ const role = context.user.attributes?.role;
90
+ },
91
+
92
+ // In TailorDB hooks
93
+ .hooks({
94
+ field: {
95
+ create: ({ user }) => user.attributes?.role === "ADMIN" ? "default" : null,
96
+ },
97
+ })
98
+
99
+ // In TailorDB validate
100
+ .validate({
101
+ field: [
102
+ ({ user }) => user.attributes?.role === "ADMIN",
103
+ "Only admins can set this field",
104
+ ],
105
+ })
106
+ ```
107
+
108
+ Machine users are useful for:
109
+
110
+ - CI/CD pipelines
111
+ - Background jobs
112
+ - Service-to-service communication
113
+ - E2E testing
114
+
115
+ Get a machine user token using the CLI:
116
+
117
+ ```bash
118
+ tailor-sdk machineuser token <name>
119
+ ```
120
+
121
+ ### Using auth.invoker()
122
+
123
+ The `auth.invoker()` method creates a type-safe reference to a machine user for use in workflow triggers. This specifies which machine user's permissions should be used when executing the workflow.
124
+
125
+ ```typescript
126
+ // tailor.config.ts
127
+ export const auth = defineAuth("my-auth", {
128
+ machineUsers: {
129
+ "admin-machine-user": {
130
+ attributes: { role: "ADMIN" },
131
+ },
132
+ },
133
+ // ... other config
134
+ });
135
+ ```
136
+
137
+ ```typescript
138
+ // resolvers/trigger-workflow.ts
139
+ import { createResolver, t } from "@tailor-platform/sdk";
140
+ import { auth } from "../tailor.config";
141
+ import myWorkflow from "../workflows/my-workflow";
142
+
143
+ export default createResolver({
144
+ name: "triggerMyWorkflow",
145
+ operation: "mutation",
146
+ input: {
147
+ id: t.string(),
148
+ },
149
+ body: async ({ input }) => {
150
+ // Trigger workflow with machine user permissions
151
+ const workflowRunId = await myWorkflow.trigger(
152
+ { id: input.id },
153
+ { authInvoker: auth.invoker("admin-machine-user") },
154
+ );
155
+ return { workflowRunId };
156
+ },
157
+ output: t.object({
158
+ workflowRunId: t.string(),
159
+ }),
160
+ });
161
+ ```
162
+
163
+ The `invoker()` method is type-safe and only accepts machine user names defined in the auth configuration.
164
+
165
+ ## OAuth 2.0 Clients
166
+
167
+ Configure OAuth 2.0 clients for third-party applications:
168
+
169
+ ```typescript
170
+ oauth2Clients: {
171
+ "my-oauth2-client": {
172
+ redirectURIs: [
173
+ "https://example.com/callback",
174
+ `${website.url}/callback`, // Type-safe URL from StaticWebsite
175
+ ],
176
+ description: "My OAuth2 client",
177
+ grantTypes: ["authorization_code", "refresh_token"],
178
+ },
179
+ },
180
+ ```
181
+
182
+ **redirectURIs**: Allowed redirect URIs after authentication.
183
+
184
+ **description**: Optional description of the client.
185
+
186
+ **grantTypes**: Supported OAuth 2.0 grant types:
187
+
188
+ - `authorization_code` - Standard OAuth 2.0 authorization code flow
189
+ - `refresh_token` - Allow refreshing access tokens
190
+
191
+ Get OAuth2 client credentials using the CLI:
192
+
193
+ ```bash
194
+ tailor-sdk oauth2client get <name>
195
+ ```
196
+
197
+ ## Identity Provider
198
+
199
+ Connect to an external identity provider:
200
+
201
+ ```typescript
202
+ idProvider: idp.provider("my-provider", "my-client"),
203
+ ```
204
+
205
+ See [IdP](./idp.md) for configuring identity providers.
206
+
207
+ ## CLI Commands
208
+
209
+ Manage Auth resources using the CLI:
210
+
211
+ ```bash
212
+ # List machine users
213
+ tailor-sdk machineuser list
214
+
215
+ # Get machine user token
216
+ tailor-sdk machineuser token <name>
217
+
218
+ # List OAuth2 clients
219
+ tailor-sdk oauth2client list
220
+
221
+ # Get OAuth2 client credentials
222
+ tailor-sdk oauth2client get <name>
223
+ ```
224
+
225
+ See [Auth Resource Commands](../cli/auth.md) for full documentation.
@@ -0,0 +1,304 @@
1
+ # Executor
2
+
3
+ Executors are event-driven handlers that automatically trigger in response to data changes, schedules, or external events.
4
+
5
+ ## Overview
6
+
7
+ Executors provide:
8
+
9
+ - Automatic triggers on record changes (create, update, delete)
10
+ - Scheduled execution via cron expressions
11
+ - Incoming webhook handlers
12
+ - Post-resolver execution hooks
13
+ - Multiple execution targets (functions, webhooks, GraphQL)
14
+
15
+ For the official Tailor Platform documentation, see [Executor Guide](https://docs.tailor.tech/guides/executor/overview).
16
+
17
+ ## Creating an Executor
18
+
19
+ Define executors in files matching glob patterns specified in `tailor.config.ts`.
20
+
21
+ ```typescript
22
+ import { createExecutor, recordCreatedTrigger, t } from "@tailor-platform/sdk";
23
+ import { user } from "../tailordb/user";
24
+
25
+ export default createExecutor({
26
+ name: "user-welcome",
27
+ description: "Send welcome email to new users",
28
+ trigger: recordCreatedTrigger({
29
+ type: user,
30
+ condition: ({ newRecord }) => !!newRecord.email && newRecord.isActive,
31
+ }),
32
+ operation: {
33
+ kind: "function",
34
+ body: async ({ newRecord }) => {
35
+ // Send welcome email logic here
36
+ },
37
+ },
38
+ });
39
+ ```
40
+
41
+ ## Trigger Types
42
+
43
+ ### Record Triggers
44
+
45
+ Fire when records are created, updated, or deleted:
46
+
47
+ - `recordCreatedTrigger()`: Fires when a new record is created
48
+ - `recordUpdatedTrigger()`: Fires when a record is updated
49
+ - `recordDeletedTrigger()`: Fires when a record is deleted
50
+
51
+ Each trigger can include an optional filter function:
52
+
53
+ ```typescript
54
+ recordUpdatedTrigger({
55
+ type: order,
56
+ condition: ({ newRecord, oldRecord }) =>
57
+ newRecord.status === "completed" && oldRecord.status !== "completed",
58
+ });
59
+ ```
60
+
61
+ ### Schedule Trigger
62
+
63
+ Fires on a cron schedule:
64
+
65
+ ```typescript
66
+ scheduleTrigger({ cron: "*/5 * * * *" }); // Every 5 minutes
67
+ scheduleTrigger({ cron: "0 9 * * 1" }); // Every Monday at 9am
68
+ scheduleTrigger({ cron: "0 0 1 * *" }); // First day of every month
69
+ scheduleTrigger({ cron: "0 * * * *", timezone: "Asia/Tokyo" });
70
+ ```
71
+
72
+ ### Incoming Webhook Trigger
73
+
74
+ Fires when an external webhook is received:
75
+
76
+ ```typescript
77
+ incomingWebhookTrigger<WebhookPayload>();
78
+ ```
79
+
80
+ ### Resolver Executed Trigger
81
+
82
+ Fires when a resolver is executed:
83
+
84
+ ```typescript
85
+ resolverExecutedTrigger({
86
+ resolver: createOrderResolver,
87
+ condition: ({ result, error }) => !error && result?.order?.id,
88
+ });
89
+ ```
90
+
91
+ ## Execution Targets
92
+
93
+ ### Function Execution
94
+
95
+ Execute JavaScript/TypeScript functions:
96
+
97
+ ```typescript
98
+ createExecutor({
99
+ operation: {
100
+ kind: "function",
101
+ body: async ({ newRecord }) => {
102
+ console.log("New record created:", newRecord);
103
+ },
104
+ },
105
+ });
106
+ ```
107
+
108
+ ### Webhook Execution
109
+
110
+ Call external webhooks with dynamic data:
111
+
112
+ ```typescript
113
+ createExecutor({
114
+ operation: {
115
+ kind: "webhook",
116
+ url: ({ newRecord }) =>
117
+ `https://api.example.com/webhooks/${newRecord.type}`,
118
+ headers: {
119
+ "Content-Type": "application/json",
120
+ "X-API-Key": { vault: "api-keys", key: "external-api" },
121
+ },
122
+ requestBody: ({ newRecord }) => ({
123
+ id: newRecord.id,
124
+ timestamp: new Date(),
125
+ data: newRecord,
126
+ }),
127
+ },
128
+ });
129
+ ```
130
+
131
+ ### GraphQL Execution
132
+
133
+ Execute GraphQL queries and mutations:
134
+
135
+ ```typescript
136
+ import { gql } from "@tailor-platform/sdk";
137
+
138
+ createExecutor({
139
+ operation: {
140
+ kind: "graphql",
141
+ appName: "my-app",
142
+ query: gql`
143
+ mutation UpdateUserStatus($id: ID!, $status: String!) {
144
+ updateUser(id: $id, input: { status: $status }) {
145
+ id
146
+ status
147
+ updatedAt
148
+ }
149
+ }
150
+ `,
151
+ variables: ({ newRecord }) => ({
152
+ id: newRecord.userId,
153
+ status: "active",
154
+ }),
155
+ },
156
+ });
157
+ ```
158
+
159
+ ## Event Payloads
160
+
161
+ Each trigger type provides specific context data in the callback functions.
162
+
163
+ ### Record Event Payloads
164
+
165
+ Record triggers receive context based on the operation type:
166
+
167
+ #### Created Event
168
+
169
+ ```typescript
170
+ interface RecordCreatedContext<T> {
171
+ workspaceId: string; // Workspace identifier
172
+ namespaceName: string; // Application/namespace name
173
+ typeName: string; // TailorDB type name
174
+ newRecord: T; // The newly created record
175
+ }
176
+ ```
177
+
178
+ #### Updated Event
179
+
180
+ ```typescript
181
+ interface RecordUpdatedContext<T> {
182
+ workspaceId: string;
183
+ namespaceName: string;
184
+ typeName: string;
185
+ oldRecord: T; // Previous record state
186
+ newRecord: T; // Current record state
187
+ }
188
+ ```
189
+
190
+ #### Deleted Event
191
+
192
+ ```typescript
193
+ interface RecordDeletedContext<T> {
194
+ workspaceId: string;
195
+ namespaceName: string;
196
+ typeName: string;
197
+ oldRecord: T; // The deleted record
198
+ }
199
+ ```
200
+
201
+ **Usage Example:**
202
+
203
+ ```typescript
204
+ import { createExecutor, recordUpdatedTrigger, t } from "@tailor-platform/sdk";
205
+ import { order } from "../tailordb/order";
206
+
207
+ export default createExecutor({
208
+ name: "order-status-changed",
209
+ trigger: recordUpdatedTrigger({
210
+ type: order,
211
+ condition: ({ oldRecord, newRecord }) =>
212
+ oldRecord.status !== newRecord.status,
213
+ }),
214
+ operation: {
215
+ kind: "function",
216
+ body: async ({ oldRecord, newRecord, typeName }) => {
217
+ console.log(`${typeName} status changed:`);
218
+ console.log(` From: ${oldRecord.status}`);
219
+ console.log(` To: ${newRecord.status}`);
220
+ },
221
+ },
222
+ });
223
+ ```
224
+
225
+ ### Schedule Event Payload
226
+
227
+ Schedule triggers receive minimal context:
228
+
229
+ ```typescript
230
+ interface ScheduleContext {
231
+ scheduledTime: string; // ISO 8601 timestamp
232
+ }
233
+ ```
234
+
235
+ ### Incoming Webhook Payload
236
+
237
+ Webhook triggers receive HTTP request data:
238
+
239
+ ```typescript
240
+ interface WebhookContext<T = unknown> {
241
+ body: T; // Parsed request body
242
+ headers: Record<string, string>; // Request headers
243
+ method: string; // HTTP method (POST, etc.)
244
+ rawBody: string; // Raw request body as string
245
+ }
246
+ ```
247
+
248
+ **Usage Example:**
249
+
250
+ ```typescript
251
+ import { createExecutor, incomingWebhookTrigger } from "@tailor-platform/sdk";
252
+
253
+ interface StripeWebhook {
254
+ type: string;
255
+ data: { object: { id: string; amount: number } };
256
+ }
257
+
258
+ export default createExecutor({
259
+ name: "stripe-webhook",
260
+ trigger: incomingWebhookTrigger<StripeWebhook>(),
261
+ operation: {
262
+ kind: "function",
263
+ body: async ({ body, headers }) => {
264
+ const signature = headers["stripe-signature"];
265
+ console.log(`Received ${body.type} event`);
266
+ // Process webhook...
267
+ },
268
+ },
269
+ });
270
+ ```
271
+
272
+ ### Resolver Executed Payload
273
+
274
+ Resolver triggers receive the resolver's result or error:
275
+
276
+ ```typescript
277
+ interface ResolverExecutedContext<TResult> {
278
+ resolverName: string; // Name of the executed resolver
279
+ result?: TResult; // Return value (on success)
280
+ error?: Error; // Error object (on failure)
281
+ }
282
+ ```
283
+
284
+ **Usage Example:**
285
+
286
+ ```typescript
287
+ import { createExecutor, resolverExecutedTrigger } from "@tailor-platform/sdk";
288
+ import { createOrderResolver } from "../resolvers/create-order";
289
+
290
+ export default createExecutor({
291
+ name: "order-created-notification",
292
+ trigger: resolverExecutedTrigger({
293
+ resolver: createOrderResolver,
294
+ condition: ({ result, error }) => !error && !!result?.order,
295
+ }),
296
+ operation: {
297
+ kind: "function",
298
+ body: async ({ result, resolverName }) => {
299
+ console.log(`${resolverName} completed successfully`);
300
+ console.log(`Order ID: ${result.order.id}`);
301
+ },
302
+ },
303
+ });
304
+ ```
@@ -0,0 +1,106 @@
1
+ # IdP (Identity Provider)
2
+
3
+ IdP is a built-in identity provider service for managing user authentication.
4
+
5
+ ## Overview
6
+
7
+ The Built-in IdP provides:
8
+
9
+ - User registration and authentication
10
+ - OAuth client management
11
+ - Integration with Auth service
12
+
13
+ For the official Tailor Platform documentation, see [Identity Provider Setup](https://docs.tailor.tech/tutorials/setup-auth/setup-identity-provider).
14
+
15
+ ## Configuration
16
+
17
+ Configure the Built-in IdP using `defineIdp()`:
18
+
19
+ ```typescript
20
+ import { defineIdp, defineConfig } from "@tailor-platform/sdk";
21
+
22
+ const idp = defineIdp("my-idp", {
23
+ authorization: "loggedIn",
24
+ clients: ["my-client"],
25
+ });
26
+
27
+ export default defineConfig({
28
+ idp: [idp],
29
+ });
30
+ ```
31
+
32
+ ## Options
33
+
34
+ ### authorization
35
+
36
+ User management permissions. Controls who can manage users in the IdP.
37
+
38
+ ```typescript
39
+ defineIdp("my-idp", {
40
+ authorization: "loggedIn", // Only logged-in users can manage
41
+ });
42
+
43
+ defineIdp("my-idp", {
44
+ authorization: "insecure", // Anyone can manage (development only)
45
+ });
46
+
47
+ defineIdp("my-idp", {
48
+ authorization: "user.role == 'admin'", // CEL expression
49
+ });
50
+ ```
51
+
52
+ **Values:**
53
+
54
+ - `"insecure"` - No authentication required (use only for development)
55
+ - `"loggedIn"` - Requires authenticated user
56
+ - CEL expression - Custom authorization logic
57
+
58
+ ### clients
59
+
60
+ OAuth client names that can use this IdP:
61
+
62
+ ```typescript
63
+ defineIdp("my-idp", {
64
+ clients: ["default-client", "mobile-client"],
65
+ });
66
+ ```
67
+
68
+ ## Using idp.provider()
69
+
70
+ The `idp.provider()` method creates a type-safe reference to the IdP for use in Auth configuration. The client name is validated at compile time against the clients defined in the IdP.
71
+
72
+ ```typescript
73
+ import { defineIdp, defineAuth, defineConfig } from "@tailor-platform/sdk";
74
+ import { user } from "./tailordb/user";
75
+
76
+ const idp = defineIdp("my-idp", {
77
+ authorization: "loggedIn",
78
+ clients: ["default-client", "mobile-client"],
79
+ });
80
+
81
+ const auth = defineAuth("my-auth", {
82
+ userProfile: {
83
+ type: user,
84
+ usernameField: "email",
85
+ attributes: { role: true },
86
+ },
87
+ // Type-safe: only "default-client" or "mobile-client" are allowed
88
+ idProvider: idp.provider("my-provider", "default-client"),
89
+ });
90
+
91
+ export default defineConfig({
92
+ idp: [idp],
93
+ auth,
94
+ });
95
+ ```
96
+
97
+ **Parameters:**
98
+
99
+ ```typescript
100
+ idp.provider(
101
+ "provider-name", // Name for the provider reference
102
+ "client-name", // Must be one of the clients defined in the IdP
103
+ );
104
+ ```
105
+
106
+ The second argument only accepts client names that were defined in the `clients` array of the IdP configuration.