Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
75 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
144402f
chore(tailor-proto): regenerate proto with type_hook / type_validate
dqn May 20, 2026
bb83103
feat(tailordb)!: emit record-level hooks/validate via type_hook / typ…
dqn May 20, 2026
3aefae0
chore(example): regenerate migration 0004 as type-level hooks/validate
dqn May 20, 2026
15308bc
Merge remote-tracking branch 'origin/main' into feat/object-literal-d…
dqn May 20, 2026
34c3c08
fix(tailordb): skip auto-generated timestamp hooks when type has reco…
dqn May 20, 2026
f3a345e
chore(example): regenerate migration 0005 to drop field-level timesta…
dqn May 20, 2026
bcab9d8
fix(tailordb): bind type_hook scripts to _input per platform contract
dqn May 20, 2026
24ade46
fix(example): provide createdAt/updatedAt in Customer seed jsonl
dqn May 20, 2026
20f394d
fix(tailordb)!: align record-level hook contract with platform (overr…
dqn May 20, 2026
a34ce7c
fix(tailordb): emit type_validate as a map per platform contract
dqn May 20, 2026
8f93ad6
fix(tailordb)!: emit record-level hooks as per-field FieldHook
dqn May 21, 2026
5314d19
chore(tailordb): plain-text JSDoc for compareTypeHooksValidate
dqn May 21, 2026
ec14a40
fix(create-sdk): drop ...data spread from template record hooks
dqn May 21, 2026
68d34e5
chore(create-sdk): regenerate kysely types and exclude generated/seed…
dqn May 21, 2026
d39ed65
Merge remote-tracking branch 'origin/main' into feat/object-literal-d…
dqn May 21, 2026
fc9ef29
chore(tailordb): rename SnapshotType to TailorDBSnapshotType after ma…
dqn May 21, 2026
1afb22e
chore(example): regenerate migration 0005 after main merge
dqn May 21, 2026
4bc9f73
fix(tailordb): persist record-level validator removal and reject bran…
dqn May 21, 2026
674a80b
test(sdk): cover descriptor API and record-level hooks/validate gaps
dqn May 21, 2026
d9c9caa
fix(sdk): mirror platform semantics in seed/test helpers and docs
dqn May 21, 2026
29f8c71
docs(tailordb): align record-level hooks/validate examples with parser
dqn May 21, 2026
32e8693
fix(parser): reject getter/setter/method-shorthand in record-hook ret…
dqn May 21, 2026
24a114c
feat(cli): tighten migration script subcommand and deploy logging
dqn May 21, 2026
a9b5ba5
fix(migrate): restore removed relationships during Pre-phase
dqn May 21, 2026
c4cbed5
test(tailordb,resolver): cover descriptor option permutations and plu…
dqn May 21, 2026
c25eb56
chore: run generate
dqn May 21, 2026
88f9ee7
fix(deploy): detect record-level validator diffs and reset relationsh…
dqn May 21, 2026
849f4b2
fix(tailordb): reject id overrides in record-level hooks
dqn May 21, 2026
950d6eb
refactor(tailordb): tidy createTable buildField branching
dqn May 21, 2026
1264f46
refactor(sdk): extract record-validator tuple guard in test utils
dqn May 21, 2026
ad341ea
refactor(resolver): align descriptor classification with createTable
dqn May 21, 2026
d2828c6
refactor(resolver): drop redundant descriptor casts in resolver hot p…
dqn May 21, 2026
8688609
refactor(tailordb): extract validate-target push helper in script bun…
dqn May 21, 2026
7451488
fix(sdk): reject unknown descriptor option keys at runtime
dqn May 21, 2026
36c14c1
test(sdk): drop unnecessary as-casts in descriptor reject tests
dqn May 21, 2026
c192ec5
fix(sdk): synthesize createdAt for generated datetime in test hook
dqn May 21, 2026
871dfaf
fix(tailordb): detect remote type_validate drift in snapshot check
dqn May 21, 2026
36de4d5
fix(kysely-type): only wrap insert-generated fields in Generated<T>
dqn May 21, 2026
b7a62cc
refactor(tailordb): consolidate type_validate proto emitter and tight…
dqn May 21, 2026
af6771a
refactor(sdk): dedupe decimal scale check, map get-or-create, seed sc…
dqn May 21, 2026
b9efcae
refactor(tailordb): route deploy manifest through snapshot-manifest c…
dqn May 21, 2026
3cb3864
refactor(sdk): unify script-expr compilation and proto-helper sharing
dqn May 21, 2026
0008cb7
test(parser): cover precompiled-expr lookup for record-level hooks/va…
dqn May 21, 2026
809bce8
refactor(sdk): share SCRIPT_ARG_MAPS with hooks-validate-bundler
dqn May 21, 2026
fd1e726
refactor(parser): extract normalizeValidatorEntry for predicate decom…
dqn May 21, 2026
532df7f
Merge remote-tracking branch 'origin/main' into feat/object-literal-d…
dqn May 21, 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
12 changes: 12 additions & 0 deletions .changeset/object-literal-descriptor-api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
"@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 return an object containing **only the fields to override**; omitted fields keep their incoming values. The SDK statically extracts the override key set from the returned object literal and expands each entry into a field-level hook on the affected field, so the platform-generated GraphQL `CreateInput` treats those fields as optional. `validate` accepts a single function, a `[fn, message]` tuple, or an array of either.

Migration: move field-level hook/validate logic into record-level callbacks on the type. Record-level hook bodies must end in a static object literal (`({ data }) => ({ k1: v1, k2: v2 })`) so the override keys can be statically resolved; branched or computed return shapes will throw at parse time.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -55,3 +55,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, definePlugins
- `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
32 changes: 22 additions & 10 deletions example/generated/tailordb.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface Namespace {
fullAddress: Generated<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