Skip to content

feat(tailordb): type-level hooks/validators with issues() API and default support#1487

Draft
toiroakr wants to merge 6 commits into
mainfrom
chore/type-level-hook
Draft

feat(tailordb): type-level hooks/validators with issues() API and default support#1487
toiroakr wants to merge 6 commits into
mainfrom
chore/type-level-hook

Conversation

@toiroakr

@toiroakr toiroakr commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Summary

  • TailorDB field hooks and validators are now compiled into a single per-type create/update script (sent as type_hook / type_validate) instead of per-field scripts.
  • Hooks gain a now (Date) argument computed once per operation and shared across every field, so multiple fields can be stamped with an identical timestamp.
  • All of a type's hooks run together and observe the same submitted input: a hook's data no longer reflects other fields' hook results. The previous per-field behavior was order-dependent and non-deterministic.
  • Hook functions use newRecord/oldRecord arguments matching the platform _input/_oldRecord variables.
  • Add .default() builder method for TailorDBField with TypeLevelError type guards (prevents double-call, nested, serial). Defaults are applied after hooks on create only via ?? defaultValue in the generated type-hook script. Datetime/date/time fields accept "now" to use the shared operation timestamp.
  • Regenerate proto bindings (tailor-proto) to include optionalOnCreate field. Use optionalOnCreate: true instead of the field-level hook placeholder (_value), resolving the platform validation error where type_hook and field-level hooks could not coexist.
  • Field validators simplified: return string (error message) or void (pass) instead of the previous [fn, "message"] tuple form. Validators receive { newValue, oldValue } only (no user, no data).
  • Type-level validate via .validate((args, issues) => { ... }): accepts a function with { newRecord, oldRecord, user } and an issues(field, message) callback for cross-field validation. Field path argument is type-checked via DottedPaths<TData> utility type, providing autocomplete for top-level and nested field paths.
  • Full type-level validate pipeline: configure → parser → bundler → snapshot → manifest. The deploy diff now compares type_hook/type_validate so hook/validator changes are detected as updates, and treats an empty field-level validate as unset.

@changeset-bot

changeset-bot Bot commented Jun 17, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: f1b7b8c

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
@tailor-platform/sdk Minor
@tailor-platform/create-sdk Minor

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@pkg-pr-new

pkg-pr-new Bot commented Jun 17, 2026

Copy link
Copy Markdown

Open in StackBlitz

pnpm add https://pkg.pr.new/@tailor-platform/create-sdk@f1b7b8c
pnpm add https://pkg.pr.new/@tailor-platform/sdk@f1b7b8c

commit: f1b7b8c

@github-actions

This comment has been minimized.

toiroakr added 4 commits June 30, 2026 18:59
Field and type-level hooks and validators are now compiled into a single per-type create/update script. Hooks receive a `now` (Date) shared across every field hooked in the same operation, so multiple fields can be stamped with one identical timestamp.
… preserve Create-input optionality

The platform derives GraphQL Create-input optionality from the presence of a field-level create hook, not from type_hook. Emit a passthrough create hook (`_value`) per field so required hooked fields stay optional in input; the real shared-now logic in type_hook runs afterward and overwrites the value.
…oto field

Add .default() builder method for TailorDBField with TypeLevelError
type guards. Regenerate proto bindings to use optionalOnCreate instead
of field-level hook placeholder, resolving type_hook coexistence conflict.
@toiroakr toiroakr force-pushed the chore/type-level-hook branch from 176cf40 to 21b053b Compare June 30, 2026 10:40
@toiroakr toiroakr changed the title feat(tailordb): aggregate hooks and validators into type-level scripts feat(tailordb): aggregate hooks/validators into type-level scripts with default support Jun 30, 2026
@toiroakr toiroakr requested a review from Copilot June 30, 2026 10:41
@github-actions

Copy link
Copy Markdown

Code Metrics Report (packages/sdk)

main (fc572f3) #1487 (a470092) +/-
Coverage 70.7% 70.8% +0.1%
Code to Test Ratio 1:0.4 1:0.4 +0.0
Details
  |                    | main (fc572f3) | #1487 (a470092) |  +/-  |
  |--------------------|----------------|-----------------|-------|
+ | Coverage           |          70.7% |           70.8% | +0.1% |
  |   Files            |            425 |             426 |    +1 |
  |   Lines            |          15503 |           15563 |   +60 |
+ |   Covered          |          10967 |           11030 |   +63 |
+ | Code to Test Ratio |          1:0.4 |           1:0.4 |  +0.0 |
  |   Code             |         106278 |          106539 |  +261 |
+ |   Test             |          47984 |           48115 |  +131 |

Code coverage of files in pull request scope (77.0% → 77.8%)

Files Coverage +/- Status
packages/sdk/src/cli/commands/deploy/tailordb/index.ts 76.7% +0.3% modified
packages/sdk/src/cli/commands/tailordb/migrate/snapshot-manifest.ts 60.2% -0.5% modified
packages/sdk/src/cli/commands/tailordb/migrate/snapshot-schema.ts 100.0% 0.0% modified
packages/sdk/src/cli/commands/tailordb/migrate/snapshot-types.ts 100.0% 0.0% modified
packages/sdk/src/cli/commands/tailordb/migrate/snapshot.ts 75.9% +0.0% modified
packages/sdk/src/cli/commands/workflow/waiter.ts 75.0% +2.1% affected
packages/sdk/src/cli/services/tailordb/hooks-validate-bundler.ts 82.6% +0.0% modified
packages/sdk/src/configure/services/tailordb/schema.ts 81.6% +0.0% modified
packages/sdk/src/parser/service/tailordb/field.ts 95.2% 0.0% modified
packages/sdk/src/parser/service/tailordb/schema.ts 76.4% 0.0% modified
packages/sdk/src/parser/service/tailordb/type-script.ts 100.0% +100.0% added

SDK Configure Bundle Size

main (fc572f3) #1487 (a470092) +/-
configure-index-size 20.32KB 20.32KB 0KB
dependency-chunks-size 47.13KB 47.19KB 0.06KB
total-bundle-size 67.45KB 67.51KB 0.06KB

Runtime Performance

main (fc572f3) #1487 (a470092) +/-
Generate Median 2,934ms 3,094ms 160ms
Generate Max 2,957ms 3,118ms 161ms
Apply Build Median 2,974ms 3,108ms 134ms
Apply Build Max 2,990ms 3,166ms 176ms

Type Performance (instantiations)

main (fc572f3) #1487 (a470092) +/-
tailordb-basic 39,476 38,707 -769
tailordb-optional 4,385 4,460 75
tailordb-relation 5,103 5,156 53
tailordb-validate 742 742 0
tailordb-hooks 5,222 5,416 194
tailordb-object 12,510 12,554 44
tailordb-enum 1,450 1,497 47
resolver-basic 9,272 9,272 0
resolver-nested 26,139 26,139 0
resolver-array 18,078 18,078 0
executor-schedule 4,310 4,310 0
executor-webhook 949 949 0
executor-record 5,664 5,684 20
executor-resolver 4,108 4,108 0
executor-operation-function 937 937 0
executor-operation-gql 945 945 0
executor-operation-webhook 956 956 0
executor-operation-workflow 1,785 1,785 0

Reported by octocov

This comment was marked as outdated.

…y field validators

- Add type-level validate function form to .validate() accepting (args, issues) callback
- Field validators now return string (error) | void (pass) instead of [fn, message] tuple
- Type-safe field path autocomplete for issues() via DottedPaths utility type
- Full pipeline: configure -> parser -> bundler -> snapshot -> manifest
- Hook functions use newRecord/oldRecord args matching platform _input/_oldRecord variables
- Field validators receive { newValue, oldValue } only
@toiroakr toiroakr changed the title feat(tailordb): aggregate hooks/validators into type-level scripts with default support feat(tailordb): type-level hooks/validators with issues() API and default support Jul 1, 2026
@toiroakr toiroakr requested a review from Copilot July 1, 2026 03:49

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 41 out of 44 changed files in this pull request and generated 4 comments.

Comment on lines +125 to +129
const chain = validators.map((v) => `(${v.script?.expr})`).join(" ?? ");
statements.push(
`{ const _value = ${access}; const _oldValue = ${oldAccess} ?? null;` +
` const __r = ${chain}; if (typeof __r === "string") { __errs[${key(fieldPath)}] = __r; } }`,
);
Comment on lines +259 to +266
Add hooks to execute functions during data creation or update. Hooks receive four arguments:

- `value`: User input if provided, otherwise existing value on update or null on create
- `data`: Entire record data (for accessing other field values)
- `data`: The submitted record data (for accessing other field values)
- `user`: User performing the operation
- `now`: Operation timestamp (`Date`). The same instant is shared by every field's hook in the same create/update, so multiple fields can be stamped with an identical timestamp.

All of a type's hooks run together as one operation and observe the same submitted input: `data` reflects what the client sent, so a hook does not see another field's hook result. Order between fields is not significant.
Comment on lines +5 to +7
Add a `now` argument to TailorDB hooks. `now` is the operation timestamp and is shared across every field hooked in the same create/update, so multiple fields can be stamped with an identical `Date`. Hooks and validators are now applied per type rather than per field, which is what makes the shared timestamp possible.

As part of this, all of a type's hooks now run together and observe the same submitted input: a hook's `data` reflects what the client sent and does not include other fields' hook results.
Comment on lines 62 to +66
value: (data as Record<string, unknown>)[key],
data: data,
newRecord: data,
oldRecord: null,
user: unauthenticatedTailorUser,
now: new Date(),
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants