Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7c0a567
feat(tailordb,resolver): add createTable object-literal API and resol…
dqn Apr 1, 2026
cfdcf83
fix(resolver,tailordb): strengthen descriptor discrimination and vali…
dqn Apr 1, 2026
1f035b1
refactor(resolver): deduplicate KindToFieldType, optimize resolveReso…
dqn Apr 1, 2026
f378f5b
fix(resolver,tailordb): reject unknown descriptor kind values at runtime
dqn Apr 2, 2026
86dcf48
fix(resolver,tailordb): validate enum descriptor values and document …
dqn Apr 2, 2026
a2fb4bc
fix(tailordb): fix array+hooks type collapse and reject malformed pas…
dqn Apr 2, 2026
8c4b878
fix(resolver,tailordb): validate passthrough field entries have type …
dqn Apr 2, 2026
0478fdf
refactor(resolver): deduplicate resolveResolverFieldMap and remove ob…
dqn Apr 2, 2026
c22dc34
Merge remote-tracking branch 'origin/main' into worktree-staged-huggi…
dqn Apr 2, 2026
3e275c3
revert: restore processOrder.ts import order to match main
dqn Apr 2, 2026
d5be2b8
chore: add changeset for object-literal descriptor API
dqn Apr 2, 2026
6910b23
test(tailordb): add type-level option tests for createTable
dqn Apr 3, 2026
47ab17c
docs: add createTable and descriptor syntax documentation
dqn Apr 3, 2026
f987b90
feat(example): add Product type using createTable API
dqn Apr 3, 2026
4f7cc03
fix(tailordb): type array field hooks with correct output type
dqn Apr 4, 2026
20fe3d4
fix(tailordb): add createTable overload for inline hook contextual ty…
dqn Apr 4, 2026
4420200
test(tailordb): document inline enum hook TS limitation with workarou…
dqn Apr 4, 2026
216ef75
chore(example): generate migration for Product type
dqn Apr 4, 2026
26f1771
refactor(tailordb)!: move hooks and validate from fields to record level
dqn Apr 11, 2026
4d887c9
fix(tailordb): add generated metadata flag for timestamp fields
dqn Apr 15, 2026
ee21dde
chore: trigger CI for generated metadata flag fix
dqn Apr 15, 2026
60f3d0f
Merge remote-tracking branch 'origin/main' into feat/object-literal-d…
dqn Apr 15, 2026
4273de3
fix(tailordb): wire generated timestamp hooks to server and local seed
dqn Apr 15, 2026
cbc856e
chore(example): generate migration for timestamp hooks change
dqn Apr 15, 2026
f71ff68
fix(example): add fullAddress to Customer seed data
dqn Apr 15, 2026
7123d42
fix(example): provide fullAddress in E2E tests for Customer mutations
dqn Apr 15, 2026
af53d48
fix(tailordb): wire record-level validators to platform via field-lev…
dqn Apr 15, 2026
e447c81
chore(example): generate migration for record-level validators
dqn Apr 15, 2026
88a4339
fix(tailordb): distribute record-level validators to first non-id field
dqn Apr 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .changeset/object-literal-descriptor-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
"@tailor-platform/sdk": major
---

TailorDB API refactor: object-literal descriptor API and record-level hooks/validate

- **New**: `createTable(name, fields, options?)` accepts object-literal field descriptors alongside the existing fluent API.
- **New**: Resolver fields accept object-literal descriptors.
- **Breaking**: Removed field-level `.hooks()` and `.validate()` from the TailorDB field builder (`db.string().hooks(...)`, `db.int().validate(...)`, etc.) and from field descriptors passed to `createTable`.
- **Breaking**: `createTable` type-level `hooks` / `validate` options are now **record-level** callbacks that receive the full record via `({ data, user }) => ...`. Hooks must return a complete record (spread incoming `data` to keep unchanged fields: `{ ...data, field: newValue }`). `validate` accepts a single function, a `[fn, message]` tuple, or an array of either.
- **Breaking**: `db.fields.timestamps()` / `timestampFields()` now returns fields only — it no longer installs automatic `create` / `update` hooks. Define record-level hooks explicitly to populate `createdAt` / `updatedAt`.

Migration: move field-level hook/validate logic into record-level callbacks on the type.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,4 @@ CLAUDE.local.md
llm-challenge/results/
llm-challenge/problems/*/work
.claude/tmp/
.agent/tmp/
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Refer to `example/` for working implementations of all patterns (config, models,
Key files:

- `example/tailor.config.ts` - Configuration with defineConfig, defineAuth, defineIdp, defineStaticWebSite, defineGenerators
- `example/tailordb/*.ts` - Model definitions with `db.type()`
- `example/tailordb/*.ts` - Model definitions with `db.type()` or `createTable`
- `example/resolvers/*.ts` - Resolver implementations with `createResolver`
- `example/executors/*.ts` - Executor implementations with `createExecutor`
- `example/workflows/*.ts` - Workflow implementations with `createWorkflow` / `createWorkflowJob`
Expand Down
1 change: 1 addition & 0 deletions example/e2e/executor.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ describe("dataplane", () => {
email: "customer@example.com"
country: "USA"
postalCode: "12345"
fullAddress: "12345"
state: "California"
}
) {
Expand Down
6 changes: 6 additions & 0 deletions example/e2e/tailordb.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,7 @@ describe("dataplane", () => {
email: "customer-${randomUUID()}@example.com"
country: "USA"
postalCode: "12345"
fullAddress: "12345"
state: "California"
}
) {
Expand Down Expand Up @@ -419,6 +420,7 @@ describe("dataplane", () => {
email: "customer@example.com"
country: "USA"
postalCode: "12345"
fullAddress: "12345"
state: "California"
}
) {
Expand Down Expand Up @@ -535,6 +537,8 @@ describe("dataplane", () => {
});
});

// TODO(record-level-hooks): once the platform supports record-level hooks,
// remove the explicit fullAddress input and verify the hook computes it.
test("custom hooks execute correctly", async () => {
const query = gql`
mutation {
Expand All @@ -546,6 +550,7 @@ describe("dataplane", () => {
postalCode: "12345"
address: "123 Main St"
city: "Los Angeles"
fullAddress: "12345 123 Main St Los Angeles"
state: "California"
}
) {
Expand Down Expand Up @@ -577,6 +582,7 @@ describe("dataplane", () => {
email: "bob@example.com"
country: "USA"
postalCode: "12345"
fullAddress: "12345"
state: "California"
}
) {
Expand Down
2 changes: 2 additions & 0 deletions example/executors/userRecordLog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ export default async ({ newRecord }: { newRecord: t.infer<typeof user> }) => {
.values({
userID: newRecord.id,
message: `User created: ${record?.name} (${record?.email})`,
createdAt: new Date(),
updatedAt: new Date(),
})
.execute();
};
7 changes: 7 additions & 0 deletions example/generated/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,13 @@ export const InvoiceStatus = {
} as const;
export type InvoiceStatus = (typeof InvoiceStatus)[keyof typeof InvoiceStatus];

export const ProductCategory = {
"electronics": "electronics",
"clothing": "clothing",
"food": "food"
} as const;
export type ProductCategory = (typeof ProductCategory)[keyof typeof ProductCategory];

export const PurchaseOrderAttachedFilesType = {
"text": "text",
"image": "image"
Expand Down
34 changes: 23 additions & 11 deletions example/generated/tailordb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ export interface Namespace {
postalCode: string;
address: string | null;
city: string | null;
fullAddress: Generated<string>;
fullAddress: string;
state: string;
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

Invoice: {
Expand All @@ -38,7 +38,7 @@ export interface Namespace {
sequentialId: Serial<number>;
status: "draft" | "sent" | "paid" | "cancelled" | null;
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

NestedProfile: {
Expand All @@ -57,7 +57,19 @@ export interface Namespace {
}>;
archived: boolean | null;
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

Product: {
id: Generated<string>;
name: string;
sku: string;
price: number;
stock: number;
category: "electronics" | "clothing" | "food";
supplierId: string;
createdAt: Generated<Timestamp>;
updatedAt: Generated<Timestamp | null>;
}

PurchaseOrder: {
Expand All @@ -73,7 +85,7 @@ export interface Namespace {
type: "text" | "image";
}[];
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

SalesOrder: {
Expand All @@ -86,7 +98,7 @@ export interface Namespace {
cancelReason: string | null;
canceledAt: Timestamp | null;
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

SalesOrderCreated: {
Expand Down Expand Up @@ -115,7 +127,7 @@ export interface Namespace {
state: "Alabama" | "Alaska";
city: string;
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

User: {
Expand All @@ -126,31 +138,31 @@ export interface Namespace {
department: string | null;
role: "MANAGER" | "STAFF";
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

UserLog: {
id: Generated<string>;
userID: string;
message: string;
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}

UserSetting: {
id: Generated<string>;
language: "jp" | "en";
userID: string;
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}
},
"analyticsdb": {
Event: {
id: Generated<string>;
name: "CLICK" | "VIEW" | "PURCHASE";
createdAt: Generated<Timestamp>;
updatedAt: Timestamp | null;
updatedAt: Generated<Timestamp | null>;
}
}
}
Expand Down
Loading
Loading