/** Public login-method aggregator and login-result schemas. */ import { z } from 'zod'; /** * One field of a `form` login method. `secret` fields render as password * inputs; `autocomplete` passes through to the input (e.g. "email", * "current-password", "new-password"). */ export const loginFormField = z.object({ name: z.string().min(1), label: z.string().min(1), secret: z.boolean(), autocomplete: z.string().optional(), }); /** * The two — and only two — login-method shapes. Discriminated on `shape`; an * unknown shape fails validation loudly (contract drift, not a soft skip). * Optional fields are `.optional()` (absent), never `.nullable()`: the server * serializes with `exclude_none`, so an absent field is OMITTED, never `null`. */ export const loginMethod = z.discriminatedUnion('shape', [ z.object({ shape: z.literal('form'), id: z.string().min(1), title: z.string().min(1), purpose: z.enum(['login', 'invite']).default('login'), fields: z.array(loginFormField).min(1), submit_path: z.string().min(1), }), z.object({ shape: z.literal('button'), id: z.string().min(1), label: z.string().min(1), icon: z.string().optional(), href: z.string().min(1), }), ]); /** * `GET /api/login/methods` (PUBLIC). `needs_setup: true` ⇒ no principal exists * yet, so the deployment must be initialized through the setup door before anyone * can sign in. `setup_login` names the login-credential kinds the setup door can * attach the owner's login with; it is `null`/absent when no login-attaching * accounts provider is configured (a keys-only deployment). Non-strict — the * skeleton may grow additive fields. */ export const loginMethods = z.object({ methods: z.array(loginMethod), needs_setup: z.boolean(), setup_login: z .object({ kinds: z.array(z.enum(['password', 'invite'])) }) .nullable() .optional(), }); /** * A successful login/exchange: the minted session token (opaque, `tai-sess-` * prefixed — the client treats it as an opaque string) + its user. */ export const loginResult = z.object({ token: z.string().min(1), user_id: z.string().min(1), }); export type LoginFormField = z.infer; export type LoginMethod = z.infer; export type LoginMethods = z.infer; export type LoginResult = z.infer;