@tailor-platform/sdk 1.2.6 → 1.4.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.
@@ -13,8 +13,8 @@ const unauthenticatedTailorUser = {
13
13
  * - Recursively processes nested types
14
14
  * - Executes hooks.create for fields with create hooks
15
15
  * @template T - The output type of the hook function
16
- * @param {TailorDBType<unknown, unknown>} type - TailorDB type definition
17
- * @returns {(data: unknown) => Partial<output<T>>} A function that transforms input data according to field hooks
16
+ * @param type - TailorDB type definition
17
+ * @returns A function that transforms input data according to field hooks
18
18
  */
19
19
  function createTailorDBHook(type) {
20
20
  return (data) => {
@@ -38,9 +38,9 @@ function createTailorDBHook(type) {
38
38
  * Creates the standard schema definition for lines-db
39
39
  * This returns the first argument for defineSchema with the ~standard section
40
40
  * @template T - The output type after validation
41
- * @param {TailorField<unknown, T>} schemaType - TailorDB field schema for validation
42
- * @param {(data: unknown) => Partial<T>} hook - Hook function to transform data before validation
43
- * @returns {{ "~standard": StandardSchemaV1<T>["~standard"] }} Schema object with ~standard section for defineSchema
41
+ * @param schemaType - TailorDB field schema for validation
42
+ * @param hook - Hook function to transform data before validation
43
+ * @returns Schema object with ~standard section for defineSchema
44
44
  */
45
45
  function createStandardSchema(schemaType, hook) {
46
46
  return { "~standard": {
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/utils/test/index.ts"],"sourcesContent":["import type { output, TailorUser } from \"@/configure\";\nimport type { TailorDBType } from \"@/configure/services/tailordb/schema\";\nimport type { TailorField } from \"@/configure/types/type\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/** Represents an unauthenticated user in the Tailor platform. */\nexport const unauthenticatedTailorUser = {\n id: \"00000000-0000-0000-0000-000000000000\",\n type: \"\",\n workspaceId: \"00000000-0000-0000-0000-000000000000\",\n attributes: null,\n attributeList: [],\n} as const satisfies TailorUser;\n\n/**\n * Creates a hook function that processes TailorDB type fields\n * - Uses existing id from data if provided, otherwise generates UUID for id fields\n * - Recursively processes nested types\n * - Executes hooks.create for fields with create hooks\n * @template T - The output type of the hook function\n * @param {TailorDBType<unknown, unknown>} type - TailorDB type definition\n * @returns {(data: unknown) => Partial<output<T>>} A function that transforms input data according to field hooks\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createTailorDBHook<T extends TailorDBType<any, any>>(type: T) {\n return (data: unknown) => {\n return Object.entries(type.fields).reduce(\n (hooked, [key, value]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const field = value as TailorField<any, any, any>;\n if (key === \"id\") {\n // Use existing id from data if provided, otherwise generate new UUID\n const existingId =\n data && typeof data === \"object\" ? (data as Record<string, unknown>)[key] : undefined;\n hooked[key] = existingId ?? crypto.randomUUID();\n } else if (field.type === \"nested\") {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n hooked[key] = createTailorDBHook({ fields: field.fields } as any)(\n (data as Record<string, unknown>)[key],\n );\n } else if (field.metadata.hooks?.create) {\n hooked[key] = field.metadata.hooks.create({\n value: (data as Record<string, unknown>)[key],\n data: data,\n user: unauthenticatedTailorUser,\n });\n if (hooked[key] instanceof Date) {\n hooked[key] = hooked[key].toISOString();\n }\n } else if (data && typeof data === \"object\") {\n hooked[key] = (data as Record<string, unknown>)[key];\n }\n return hooked;\n },\n {} as Record<string, unknown>,\n ) as Partial<output<T>>;\n };\n}\n\n/**\n * Creates the standard schema definition for lines-db\n * This returns the first argument for defineSchema with the ~standard section\n * @template T - The output type after validation\n * @param {TailorField<unknown, T>} schemaType - TailorDB field schema for validation\n * @param {(data: unknown) => Partial<T>} hook - Hook function to transform data before validation\n * @returns {{ \"~standard\": StandardSchemaV1<T>[\"~standard\"] }} Schema object with ~standard section for defineSchema\n */\nexport function createStandardSchema<T = Record<string, unknown>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schemaType: TailorField<any, T>,\n hook: (data: unknown) => Partial<T>,\n) {\n return {\n \"~standard\": {\n version: 1,\n vendor: \"@tailor-platform/sdk\",\n validate: (value: unknown) => {\n const hooked = hook(value);\n const result = schemaType.parse({\n value: hooked,\n data: hooked,\n user: unauthenticatedTailorUser,\n });\n if (result.issues) {\n return result;\n }\n return { value: hooked as T };\n },\n },\n } as const satisfies StandardSchemaV1<T>;\n}\n"],"mappings":";;AAMA,MAAa,4BAA4B;CACvC,IAAI;CACJ,MAAM;CACN,aAAa;CACb,YAAY;CACZ,eAAe,EAAE;CAClB;;;;;;;;;;AAYD,SAAgB,mBAAqD,MAAS;AAC5E,SAAQ,SAAkB;AACxB,SAAO,OAAO,QAAQ,KAAK,OAAO,CAAC,QAChC,QAAQ,CAAC,KAAK,WAAW;GAExB,MAAM,QAAQ;AACd,OAAI,QAAQ,KAIV,QAAO,QADL,QAAQ,OAAO,SAAS,WAAY,KAAiC,OAAO,WAClD,OAAO,YAAY;YACtC,MAAM,SAAS,SAExB,QAAO,OAAO,mBAAmB,EAAE,QAAQ,MAAM,QAAQ,CAAQ,CAC9D,KAAiC,KACnC;YACQ,MAAM,SAAS,OAAO,QAAQ;AACvC,WAAO,OAAO,MAAM,SAAS,MAAM,OAAO;KACxC,OAAQ,KAAiC;KACnC;KACN,MAAM;KACP,CAAC;AACF,QAAI,OAAO,gBAAgB,KACzB,QAAO,OAAO,OAAO,KAAK,aAAa;cAEhC,QAAQ,OAAO,SAAS,SACjC,QAAO,OAAQ,KAAiC;AAElD,UAAO;KAET,EAAE,CACH;;;;;;;;;;;AAYL,SAAgB,qBAEd,YACA,MACA;AACA,QAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,UAAmB;GAC5B,MAAM,SAAS,KAAK,MAAM;GAC1B,MAAM,SAAS,WAAW,MAAM;IAC9B,OAAO;IACP,MAAM;IACN,MAAM;IACP,CAAC;AACF,OAAI,OAAO,OACT,QAAO;AAET,UAAO,EAAE,OAAO,QAAa;;EAEhC,EACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/utils/test/index.ts"],"sourcesContent":["import type { output, TailorUser } from \"@/configure\";\nimport type { TailorDBType } from \"@/configure/services/tailordb/schema\";\nimport type { TailorField } from \"@/configure/types/type\";\nimport type { StandardSchemaV1 } from \"@standard-schema/spec\";\n\n/** Represents an unauthenticated user in the Tailor platform. */\nexport const unauthenticatedTailorUser = {\n id: \"00000000-0000-0000-0000-000000000000\",\n type: \"\",\n workspaceId: \"00000000-0000-0000-0000-000000000000\",\n attributes: null,\n attributeList: [],\n} as const satisfies TailorUser;\n\n/**\n * Creates a hook function that processes TailorDB type fields\n * - Uses existing id from data if provided, otherwise generates UUID for id fields\n * - Recursively processes nested types\n * - Executes hooks.create for fields with create hooks\n * @template T - The output type of the hook function\n * @param type - TailorDB type definition\n * @returns A function that transforms input data according to field hooks\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function createTailorDBHook<T extends TailorDBType<any, any>>(type: T) {\n return (data: unknown) => {\n return Object.entries(type.fields).reduce(\n (hooked, [key, value]) => {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const field = value as TailorField<any, any, any>;\n if (key === \"id\") {\n // Use existing id from data if provided, otherwise generate new UUID\n const existingId =\n data && typeof data === \"object\" ? (data as Record<string, unknown>)[key] : undefined;\n hooked[key] = existingId ?? crypto.randomUUID();\n } else if (field.type === \"nested\") {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n hooked[key] = createTailorDBHook({ fields: field.fields } as any)(\n (data as Record<string, unknown>)[key],\n );\n } else if (field.metadata.hooks?.create) {\n hooked[key] = field.metadata.hooks.create({\n value: (data as Record<string, unknown>)[key],\n data: data,\n user: unauthenticatedTailorUser,\n });\n if (hooked[key] instanceof Date) {\n hooked[key] = hooked[key].toISOString();\n }\n } else if (data && typeof data === \"object\") {\n hooked[key] = (data as Record<string, unknown>)[key];\n }\n return hooked;\n },\n {} as Record<string, unknown>,\n ) as Partial<output<T>>;\n };\n}\n\n/**\n * Creates the standard schema definition for lines-db\n * This returns the first argument for defineSchema with the ~standard section\n * @template T - The output type after validation\n * @param schemaType - TailorDB field schema for validation\n * @param hook - Hook function to transform data before validation\n * @returns Schema object with ~standard section for defineSchema\n */\nexport function createStandardSchema<T = Record<string, unknown>>(\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n schemaType: TailorField<any, T>,\n hook: (data: unknown) => Partial<T>,\n) {\n return {\n \"~standard\": {\n version: 1,\n vendor: \"@tailor-platform/sdk\",\n validate: (value: unknown) => {\n const hooked = hook(value);\n const result = schemaType.parse({\n value: hooked,\n data: hooked,\n user: unauthenticatedTailorUser,\n });\n if (result.issues) {\n return result;\n }\n return { value: hooked as T };\n },\n },\n } as const satisfies StandardSchemaV1<T>;\n}\n"],"mappings":";;AAMA,MAAa,4BAA4B;CACvC,IAAI;CACJ,MAAM;CACN,aAAa;CACb,YAAY;CACZ,eAAe,EAAE;CAClB;;;;;;;;;;AAYD,SAAgB,mBAAqD,MAAS;AAC5E,SAAQ,SAAkB;AACxB,SAAO,OAAO,QAAQ,KAAK,OAAO,CAAC,QAChC,QAAQ,CAAC,KAAK,WAAW;GAExB,MAAM,QAAQ;AACd,OAAI,QAAQ,KAIV,QAAO,QADL,QAAQ,OAAO,SAAS,WAAY,KAAiC,OAAO,WAClD,OAAO,YAAY;YACtC,MAAM,SAAS,SAExB,QAAO,OAAO,mBAAmB,EAAE,QAAQ,MAAM,QAAQ,CAAQ,CAC9D,KAAiC,KACnC;YACQ,MAAM,SAAS,OAAO,QAAQ;AACvC,WAAO,OAAO,MAAM,SAAS,MAAM,OAAO;KACxC,OAAQ,KAAiC;KACnC;KACN,MAAM;KACP,CAAC;AACF,QAAI,OAAO,gBAAgB,KACzB,QAAO,OAAO,OAAO,KAAK,aAAa;cAEhC,QAAQ,OAAO,SAAS,SACjC,QAAO,OAAQ,KAAiC;AAElD,UAAO;KAET,EAAE,CACH;;;;;;;;;;;AAYL,SAAgB,qBAEd,YACA,MACA;AACA,QAAO,EACL,aAAa;EACX,SAAS;EACT,QAAQ;EACR,WAAW,UAAmB;GAC5B,MAAM,SAAS,KAAK,MAAM;GAC1B,MAAM,SAAS,WAAW,MAAM;IAC9B,OAAO;IACP,MAAM;IACN,MAAM;IACP,CAAC;AACF,OAAI,OAAO,OACT,QAAO;AAET,UAAO,EAAE,OAAO,QAAa;;EAEhC,EACF"}
@@ -134,3 +134,132 @@ tailor-sdk tailordb truncate User Post --yes
134
134
  - `--namespace`: requires typing `truncate <namespace-name>`
135
135
  - Specific types: requires typing `yes`
136
136
  - Use `--yes` flag to skip confirmation prompts (useful for scripts and CI/CD)
137
+
138
+ ### tailordb erd (beta)
139
+
140
+ Generate ERD artifacts for TailorDB namespaces using [Liam ERD](https://liambx.com/erd).
141
+
142
+ ```bash
143
+ tailor-sdk tailordb erd <subcommand> [options]
144
+ ```
145
+
146
+ **Notes:**
147
+
148
+ - This command is a beta feature and may introduce breaking changes in future releases
149
+ - `@liam-hq/cli` is required for `export`, `serve`, and `deploy`
150
+ - `serve` is required only for `tailordb erd serve`
151
+
152
+ Install dependencies:
153
+
154
+ ```bash
155
+ npm i -D @liam-hq/cli serve
156
+ # OR
157
+ yarn add -D @liam-hq/cli serve
158
+ # OR
159
+ pnpm add -D @liam-hq/cli serve
160
+ ```
161
+
162
+ #### tailordb erd export
163
+
164
+ Export Liam ERD dist from applied TailorDB schema.
165
+
166
+ ```bash
167
+ tailor-sdk tailordb erd export [options]
168
+ ```
169
+
170
+ **Options:**
171
+
172
+ - `-n, --namespace` - TailorDB namespace name (optional - exports all namespaces with erdSite if omitted)
173
+ - `-o, --output` - Output directory path for tbls-compatible ERD JSON (writes to `<outputDir>/<namespace>/schema.json`) (default: `.tailor-sdk/erd`)
174
+ - `-j, --json` - Output as JSON
175
+ - `-w, --workspace-id` - ID of the workspace
176
+ - `-p, --profile` - Workspace profile to use
177
+ - `-c, --config` - Path to the SDK config file (default: `tailor.config.ts`)
178
+ - `-e, --env-file` - Path to the environment file
179
+
180
+ **Usage Examples:**
181
+
182
+ ```bash
183
+ # Export ERD for all namespaces with erdSite configured
184
+ tailor-sdk tailordb erd export
185
+
186
+ # Export ERD for a specific namespace
187
+ tailor-sdk tailordb erd export --namespace myNamespace
188
+
189
+ # Export ERD with custom output directory
190
+ tailor-sdk tailordb erd export --output ./my-erd
191
+
192
+ # Export ERD with JSON output
193
+ tailor-sdk tailordb erd export --json
194
+ ```
195
+
196
+ #### tailordb erd serve
197
+
198
+ Generate and serve ERD locally (liam build + `serve dist`).
199
+
200
+ ```bash
201
+ tailor-sdk tailordb erd serve [options]
202
+ ```
203
+
204
+ **Options:**
205
+
206
+ - `-n, --namespace` - TailorDB namespace name (uses first namespace with erdSite if not specified)
207
+ - `-w, --workspace-id` - ID of the workspace
208
+ - `-p, --profile` - Workspace profile to use
209
+ - `-c, --config` - Path to the SDK config file (default: `tailor.config.ts`)
210
+ - `-e, --env-file` - Path to the environment file
211
+
212
+ **Usage Examples:**
213
+
214
+ ```bash
215
+ # Serve ERD for the first namespace with erdSite configured
216
+ tailor-sdk tailordb erd serve
217
+
218
+ # Serve ERD for a specific namespace
219
+ tailor-sdk tailordb erd serve --namespace myNamespace
220
+ ```
221
+
222
+ #### tailordb erd deploy
223
+
224
+ Deploy ERD static website for TailorDB namespace(s).
225
+
226
+ ```bash
227
+ tailor-sdk tailordb erd deploy [options]
228
+ ```
229
+
230
+ **Options:**
231
+
232
+ - `-n, --namespace` - TailorDB namespace name (optional - deploys all namespaces with erdSite if omitted)
233
+ - `-j, --json` - Output as JSON
234
+ - `-w, --workspace-id` - ID of the workspace
235
+ - `-p, --profile` - Workspace profile to use
236
+ - `-c, --config` - Path to the SDK config file (default: `tailor.config.ts`)
237
+ - `-e, --env-file` - Path to the environment file
238
+
239
+ **Usage Examples:**
240
+
241
+ ```bash
242
+ # Deploy ERD for all namespaces with erdSite configured
243
+ tailor-sdk tailordb erd deploy
244
+
245
+ # Deploy ERD for a specific namespace
246
+ tailor-sdk tailordb erd deploy --namespace myNamespace
247
+
248
+ # Deploy ERD with JSON output
249
+ tailor-sdk tailordb erd deploy --json
250
+ ```
251
+
252
+ **Notes:**
253
+
254
+ - Requires `erdSite` to be configured in `tailor.config.ts` for each namespace you want to deploy
255
+ - Example config:
256
+ ```typescript
257
+ export default defineConfig({
258
+ db: {
259
+ myNamespace: {
260
+ // ... table definitions
261
+ erdSite: "my-erd-site-name",
262
+ },
263
+ },
264
+ });
265
+ ```
@@ -12,15 +12,32 @@ tailor-sdk <command> [options]
12
12
 
13
13
  The following options are available for most commands:
14
14
 
15
- | Option | Short | Description |
16
- | ---------------- | ----- | --------------------------------------------------- |
17
- | `--env-file` | `-e` | Specify a custom environment file path |
18
- | `--verbose` | | Enable verbose logging (show stack traces on error) |
19
- | `--json` | `-j` | Output as JSON (where applicable) |
20
- | `--workspace-id` | `-w` | Workspace ID (for deployment commands) |
21
- | `--profile` | `-p` | Workspace profile |
22
- | `--config` | `-c` | Path to SDK config file |
23
- | `--yes` | `-y` | Skip confirmation prompts |
15
+ | Option | Short | Description |
16
+ | ---------------------- | ----- | --------------------------------------------------- |
17
+ | `--env-file` | `-e` | Path to environment file (error if not found) |
18
+ | `--env-file-if-exists` | | Path to environment file (ignored if not found) |
19
+ | `--verbose` | | Enable verbose logging (show stack traces on error) |
20
+ | `--json` | `-j` | Output as JSON (where applicable) |
21
+ | `--workspace-id` | `-w` | Workspace ID (for deployment commands) |
22
+ | `--profile` | `-p` | Workspace profile |
23
+ | `--config` | `-c` | Path to SDK config file |
24
+ | `--yes` | `-y` | Skip confirmation prompts |
25
+
26
+ ### Environment File Loading
27
+
28
+ Both `--env-file` and `--env-file-if-exists` can be specified multiple times and follow Node.js `--env-file` behavior:
29
+
30
+ - Variables already set in the environment are **not** overwritten
31
+ - Later files override earlier files
32
+ - `--env-file` files are loaded first, then `--env-file-if-exists` files
33
+
34
+ ```bash
35
+ # Load .env (required) and .env.local (optional, if exists)
36
+ tailor-sdk apply --env-file .env --env-file-if-exists .env.local
37
+
38
+ # Load multiple files
39
+ tailor-sdk apply --env-file .env --env-file .env.production
40
+ ```
24
41
 
25
42
  ## Environment Variables
26
43
 
@@ -17,6 +17,11 @@ For the official Tailor Platform documentation, see [Auth Guide](https://docs.ta
17
17
 
18
18
  Configure Auth service using `defineAuth()`:
19
19
 
20
+ **Definition Rules:**
21
+
22
+ - **One auth per application**: Each application can have exactly one Auth service
23
+ - **Configuration location**: Define in `tailor.config.ts` using `defineAuth()` and reference directly in the config's `auth` field
24
+
20
25
  ```typescript
21
26
  import { defineAuth } from "@tailor-platform/sdk";
22
27
  import { user } from "./tailordb/user";
@@ -18,6 +18,12 @@ For the official Tailor Platform documentation, see [Executor Guide](https://doc
18
18
 
19
19
  Define executors in files matching glob patterns specified in `tailor.config.ts`.
20
20
 
21
+ **Definition Rules:**
22
+
23
+ - **One executor per file**: Each file must contain exactly one executor definition
24
+ - **Export method**: Must use `export default`
25
+ - **Uniqueness**: Executor names must be unique globally across your entire application
26
+
21
27
  ```typescript
22
28
  import { createExecutor, recordCreatedTrigger, t } from "@tailor-platform/sdk";
23
29
  import { user } from "../tailordb/user";
@@ -16,6 +16,12 @@ For the official Tailor Platform documentation, see [Identity Provider Setup](ht
16
16
 
17
17
  Configure the Built-in IdP using `defineIdp()`:
18
18
 
19
+ **Definition Rules:**
20
+
21
+ - **Multiple IdPs allowed**: You can define multiple IdP instances in your config file
22
+ - **Configuration location**: Define in `tailor.config.ts` and add to the `idp` array
23
+ - **Uniqueness**: IdP names must be unique across all IdP instances
24
+
19
25
  ```typescript
20
26
  import { defineIdp, defineConfig } from "@tailor-platform/sdk";
21
27
 
@@ -24,8 +30,14 @@ const idp = defineIdp("my-idp", {
24
30
  clients: ["my-client"],
25
31
  });
26
32
 
33
+ // You can define multiple IdPs
34
+ const anotherIdp = defineIdp("another-idp", {
35
+ authorization: "loggedIn",
36
+ clients: ["another-client"],
37
+ });
38
+
27
39
  export default defineConfig({
28
- idp: [idp],
40
+ idp: [idp, anotherIdp], // Add all IdPs to the array
29
41
  });
30
42
  ```
31
43
 
@@ -74,6 +74,12 @@ createResolver({
74
74
 
75
75
  Define resolvers in files matching glob patterns specified in `tailor.config.ts`.
76
76
 
77
+ **Definition Rules:**
78
+
79
+ - **One resolver per file**: Each file must contain exactly one resolver definition
80
+ - **Export method**: Must use `export default`
81
+ - **Uniqueness**: Resolver names must be unique per namespace
82
+
77
83
  ```typescript
78
84
  import { createResolver, t } from "@tailor-platform/sdk";
79
85
 
@@ -16,6 +16,12 @@ For the official Tailor Platform documentation, see [Static Website Guide](https
16
16
 
17
17
  Configure static website hosting using `defineStaticWebSite()`:
18
18
 
19
+ **Definition Rules:**
20
+
21
+ - **Multiple websites allowed**: You can define multiple static websites in your config file
22
+ - **Configuration location**: Define in `tailor.config.ts` and add to the `staticWebsites` array
23
+ - **Uniqueness**: Website names must be unique across all static websites
24
+
19
25
  ```typescript
20
26
  import { defineStaticWebSite, defineConfig } from "@tailor-platform/sdk";
21
27
 
@@ -23,8 +29,13 @@ const website = defineStaticWebSite("my-website", {
23
29
  description: "My Static Website",
24
30
  });
25
31
 
32
+ // You can define multiple static websites
33
+ const adminWebsite = defineStaticWebSite("admin-website", {
34
+ description: "Admin Dashboard",
35
+ });
36
+
26
37
  export default defineConfig({
27
- staticWebsites: [website],
38
+ staticWebsites: [website, adminWebsite], // Add all websites to the array
28
39
  });
29
40
  ```
30
41
 
@@ -18,15 +18,30 @@ For the official Tailor Platform documentation, see [TailorDB Guide](https://doc
18
18
 
19
19
  Define TailorDB Types in files matching glob patterns specified in `tailor.config.ts`.
20
20
 
21
+ **Definition Rules:**
22
+
23
+ - **Multiple types per file**: You can define multiple TailorDB types in a single file
24
+ - **Export method**: Use named exports (`export const`)
25
+ - **Export both value and type**: Always export both the runtime value and TypeScript type
26
+ - **Uniqueness**: Type names must be unique across all TailorDB files
27
+
21
28
  ```typescript
22
29
  import { db } from "@tailor-platform/sdk";
23
30
 
31
+ // Export both value and type
24
32
  export const user = db.type("User", {
25
33
  name: db.string(),
26
34
  email: db.string().unique(),
27
35
  age: db.int(),
28
36
  ...db.fields.timestamps(),
29
37
  });
38
+ export type user = typeof user;
39
+
40
+ // You can define multiple types in the same file
41
+ export const role = db.type("Role", {
42
+ name: db.string().unique(),
43
+ });
44
+ export type role = typeof role;
30
45
  ```
31
46
 
32
47
  Specify plural form by passing an array as first argument:
@@ -190,26 +205,130 @@ type User {
190
205
 
191
206
  ### Hooks
192
207
 
193
- Add hooks to execute functions during data creation or update:
208
+ Add hooks to execute functions during data creation or update. Hooks receive three arguments:
209
+
210
+ - `value`: User input if provided, otherwise existing value on update or null on create
211
+ - `data`: Entire record data (for accessing other field values)
212
+ - `user`: User performing the operation
213
+
214
+ #### Field-level Hooks
215
+
216
+ Set hooks directly on individual fields:
194
217
 
195
218
  ```typescript
196
- db.datetime().hooks({
197
- create: () => new Date(),
198
- update: () => new Date(),
219
+ db.string().hooks({
220
+ create: ({ user }) => user.id,
221
+ update: ({ value }) => value,
199
222
  });
200
223
  ```
201
224
 
202
- Function arguments include: `value` (field value), `data` (entire record value), `user` (user performing the operation).
225
+ **Note:** When setting hooks at the field level, the `data` argument type is `unknown` since the field doesn't know about other fields in the type. Use type-level hooks if you need to access other fields with type safety.
226
+
227
+ #### Type-level Hooks
228
+
229
+ Set hooks for multiple fields at once using `db.type().hooks()`:
230
+
231
+ ```typescript
232
+ export const customer = db
233
+ .type("Customer", {
234
+ firstName: db.string(),
235
+ lastName: db.string(),
236
+ fullName: db.string(),
237
+ })
238
+ .hooks({
239
+ fullName: {
240
+ create: ({ data }) => `${data.firstName} ${data.lastName}`,
241
+ update: ({ data }) => `${data.firstName} ${data.lastName}`,
242
+ },
243
+ });
244
+ ```
245
+
246
+ **⚠️ Important:** Field-level and type-level hooks cannot coexist on the same field. TypeScript will prevent this at compile time:
247
+
248
+ ```typescript
249
+ // ❌ Compile error - cannot set hooks on the same field twice
250
+ export const user = db
251
+ .type("User", {
252
+ name: db.string().hooks({ create: ({ data }) => data.firstName }), // Field-level
253
+ })
254
+ .hooks({
255
+ name: { create: ({ data }) => data.lastName }, // Type-level - ERROR
256
+ });
257
+
258
+ // ✅ Correct - set hooks on different fields
259
+ export const user = db
260
+ .type("User", {
261
+ firstName: db.string().hooks({ create: () => "John" }), // Field-level on firstName
262
+ lastName: db.string(),
263
+ })
264
+ .hooks({
265
+ lastName: { create: () => "Doe" }, // Type-level on lastName
266
+ });
267
+ ```
203
268
 
204
269
  ### Validation
205
270
 
271
+ Add validation rules to fields. Validators receive three arguments (executed after hooks):
272
+
273
+ - `value`: Field value after hook transformation
274
+ - `data`: Entire record data after hook transformations (for accessing other field values)
275
+ - `user`: User performing the operation
276
+
277
+ Validators return `true` for success, `false` for failure. Use array form `[validator, errorMessage]` for custom error messages.
278
+
279
+ #### Field-level Validation
280
+
281
+ Set validators directly on individual fields:
282
+
206
283
  ```typescript
207
284
  db.string().validate(
208
- ({ value }) => value.length > 5,
209
- [({ value }) => value.length < 10, "Value must be shorter than 10 characters"],
285
+ ({ value }) => value.includes("@"),
286
+ [({ value }) => value.length >= 5, "Email must be at least 5 characters"],
210
287
  );
211
288
  ```
212
289
 
290
+ #### Type-level Validation
291
+
292
+ Set validators for multiple fields at once using `db.type().validate()`:
293
+
294
+ ```typescript
295
+ export const user = db
296
+ .type("User", {
297
+ name: db.string(),
298
+ email: db.string(),
299
+ })
300
+ .validate({
301
+ name: [({ value }) => value.length > 5, "Name must be longer than 5 characters"],
302
+ email: [
303
+ ({ value }) => value.includes("@"),
304
+ [({ value }) => value.length >= 5, "Email must be at least 5 characters"],
305
+ ],
306
+ });
307
+ ```
308
+
309
+ **⚠️ Important:** Field-level and type-level validation cannot coexist on the same field. TypeScript will prevent this at compile time:
310
+
311
+ ```typescript
312
+ // ❌ Compile error - cannot set validation on the same field twice
313
+ export const user = db
314
+ .type("User", {
315
+ name: db.string().validate(({ value }) => value.length > 0), // Field-level
316
+ })
317
+ .validate({
318
+ name: [({ value }) => value.length < 100, "Too long"], // Type-level - ERROR
319
+ });
320
+
321
+ // ✅ Correct - set validation on different fields
322
+ export const user = db
323
+ .type("User", {
324
+ name: db.string().validate(({ value }) => value.length > 0), // Field-level on name
325
+ email: db.string(),
326
+ })
327
+ .validate({
328
+ email: [({ value }) => value.includes("@"), "Invalid email"], // Type-level on email
329
+ });
330
+ ```
331
+
213
332
  ### Vector Search
214
333
 
215
334
  ```typescript
@@ -18,6 +18,14 @@ For the official Tailor Platform documentation, see [Workflow Guide](https://doc
18
18
 
19
19
  All workflow components must follow these rules:
20
20
 
21
+ **Definition Rules:**
22
+
23
+ - **One workflow + multiple jobs per file**: Each file can define multiple jobs (named exports) and one workflow (default export)
24
+ - **Workflow export method**: Must use `export default`
25
+ - **Job export method**: Must use named exports (`export const`)
26
+ - **Job name uniqueness**: Job names must be unique across the entire project (not just within one file)
27
+ - **mainJob required**: Every workflow must specify a `mainJob`
28
+
21
29
  | Rule | Description |
22
30
  | ---------------------------------------------- | -------------------------------------------------------------------------------------------------------- |
23
31
  | `createWorkflow` result must be default export | Workflow files must export the workflow as default |
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@tailor-platform/sdk",
3
- "version": "1.2.6",
3
+ "version": "1.4.0",
4
4
  "description": "Tailor Platform SDK - The SDK to work with Tailor Platform",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -43,7 +43,7 @@
43
43
  "@bufbuild/protobuf": "2.10.2",
44
44
  "@connectrpc/connect": "2.1.1",
45
45
  "@connectrpc/connect-node": "2.1.1",
46
- "@oxc-project/types": "0.107.0",
46
+ "@oxc-project/types": "0.108.0",
47
47
  "@standard-schema/spec": "1.1.0",
48
48
  "@urql/core": "6.0.1",
49
49
  "chalk": "5.6.2",
@@ -59,15 +59,16 @@
59
59
  "multiline-ts": "4.0.1",
60
60
  "open": "11.0.0",
61
61
  "ora": "9.0.0",
62
- "oxc-parser": "0.107.0",
62
+ "oxc-parser": "0.108.0",
63
63
  "p-limit": "7.2.0",
64
+ "pathe": "2.0.3",
64
65
  "pkg-types": "2.3.0",
65
- "rolldown": "1.0.0-beta.59",
66
+ "rolldown": "1.0.0-beta.60",
66
67
  "std-env": "3.10.0",
67
68
  "table": "6.9.0",
68
69
  "ts-cron-validator": "1.1.5",
69
70
  "tsx": "4.21.0",
70
- "type-fest": "5.3.1",
71
+ "type-fest": "5.4.1",
71
72
  "xdg-basedir": "5.1.0",
72
73
  "zod": "4.3.5"
73
74
  },
@@ -77,21 +78,33 @@
77
78
  "@toiroakr/lines-db": "0.6.1",
78
79
  "@types/madge": "5.0.3",
79
80
  "@types/mime-types": "3.0.1",
80
- "@types/node": "24.10.7",
81
- "@typescript/native-preview": "7.0.0-dev.20260111.1",
82
- "@vitest/coverage-v8": "4.0.16",
81
+ "@types/node": "24.10.9",
82
+ "@typescript/native-preview": "7.0.0-dev.20260117.1",
83
+ "@vitest/coverage-v8": "4.0.17",
83
84
  "cross-env": "10.1.0",
84
85
  "eslint": "9.39.2",
85
- "eslint-plugin-jsdoc": "62.0.0",
86
- "eslint-plugin-oxlint": "1.38.0",
86
+ "eslint-plugin-jsdoc": "62.1.0",
87
+ "eslint-plugin-oxlint": "1.39.0",
87
88
  "globals": "17.0.0",
88
- "oxlint": "1.38.0",
89
- "oxlint-tsgolint": "0.11.0",
89
+ "oxlint": "1.39.0",
90
+ "oxlint-tsgolint": "0.11.1",
90
91
  "sonda": "0.10.1",
91
92
  "tsdown": "0.19.0",
92
93
  "typescript": "5.9.3",
93
- "typescript-eslint": "8.52.0",
94
- "vitest": "4.0.16"
94
+ "typescript-eslint": "8.53.0",
95
+ "vitest": "4.0.17"
96
+ },
97
+ "peerDependencies": {
98
+ "@liam-hq/cli": "*",
99
+ "serve": "*"
100
+ },
101
+ "peerDependenciesMeta": {
102
+ "@liam-hq/cli": {
103
+ "optional": true
104
+ },
105
+ "serve": {
106
+ "optional": true
107
+ }
95
108
  },
96
109
  "scripts": {
97
110
  "test": "vitest",