You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
feat: add a first-class user field type (person picker) (#2351)
* feat: add a first-class `user` field type (person picker)
A `user` field type — the equivalent of Airtable's Collaborator / Notion's
Person / Salesforce's `Lookup(User)`. Authored as `Field.user({ ... })`;
`{ multiple: true }` for collaborators/watchers and
`{ defaultValue: 'current_user' }` to auto-fill the acting user on create.
Designed as a SEMANTIC SPECIALIZATION OF `lookup` with the target fixed to the
`sys_user` system object — NOT a new storage primitive. It shares the exact
lookup code path: same FK string column (multiple ⇒ JSON), same `$expand`
resolution, same indexing. So referential integrity and fresh display names
come for free, an existing `Field.lookup('sys_user')` is equivalent at the
storage layer (zero data migration), and nothing is re-implemented.
The distinct type exists for modelling discoverability — a "User" entry in the
Studio/AI field palette instead of requiring authors (and AI) to know to
reference the internal `sys_user` object — plus a user-search picker and
`current_user` defaults.
Ownership semantics are unchanged: the existing `owner_id` convention +
plugin-security auto-stamp/RLS still apply. A declarative `owner` field type
was intentionally NOT added — ownership is a system role (one per object,
auto-stamped), already handled, and a second type would only grow the
platform-wide `FieldType` surface for marginal benefit.
Changes:
- spec: `FieldType` gains `'user'` + `Field.user()` builder; seed-loader /
GraphQL / SQL type-compat reference maps accept `user`.
- drivers (sql, mongodb): treat `user` exactly like `lookup`.
- engine: resolve `$expand` for `user` fields; honour a new
`defaultValue: 'current_user'` token (resolved app-side from the execution
context, mirroring the `NOW()` convention).
- kanban group-by + symbolic seed references + approvals enrichment accept `user`.
Tests: engine $expand-for-user + current_user stamp (present / absent actor);
driver-sql string-id + multiple⇒JSON round-trip. Public API surface unchanged
(additive enum member).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* test(showcase): cover the new `user` field type in field-zoo
`field-zoo` is the exhaustive FieldType coverage object, and
`coverage.test.ts` asserts every member of `FieldType` appears across the
showcase objects. Adding `'user'` to the enum without a representative field
made "covers every FieldType" fail (uncovered → user). Add single, multiple,
and `current_user`-default `user` fields so the guard is satisfied and the new
type gets real data-layer coverage.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* fix(rest): gate `user` fields on anonymous public forms (privacy)
The public-form schema endpoint strips `lookup`/`master_detail` fields from
anonymous forms unless the designer opts in via `publicPicker` — so a stray
spec mistake can't expose unrestricted record search to the internet. A `user`
field is a lookup specialized to `sys_user`, so the same risk applies: it would
surface a user search/list to unauthenticated visitors. Gate it behind the same
`publicPicker` opt-in. (The companion `/forms/:slug/lookup/:field` endpoint
already 403s without `publicPicker`, independent of type — this closes the
form-schema side.)
Found while dogfooding the new `user` field type end-to-end.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
0 commit comments