@athenox/talos
Version:
Talos: a backend you describe instead of write. Declare a schema, write the rules in plain language, map URLs to sentences; a small built-in model turns them into query plans.
95 lines (94 loc) • 4.37 kB
TypeScript
import { type H3Event } from 'h3';
import type { Store } from './runtime.ts';
/**
* Accounts: sign-in, sign-up, sign-out, the current account, forgotten and
* reset passwords — declared, not written.
*
* Deliberately not the model's: whether a password matches, who is signed in
* and whether a reset token is good are plain code here, and the same code for
* every app. What differs between apps is data — the table, its columns, how
* passwords are hashed, where the session lives, the messages — so that is all
* an app gives.
*
* const readers = accounts({ store, table: 'subscribers', hash, verify, session,
* expose: ['id', 'email', 'name'], reset: { table: 'password_reset_tokens', owner: 'subscriber_id', send } })
* // routes: 'POST /api/subscriber/login': readers.login, … ; guard: readers.required
*
* Emails are compared lower-cased and trimmed. Forgot-password answers the
* same whether the account exists or not. Reset tokens are 32 random bytes,
* expire, serve once, and asking again drops the unused ones.
*/
export type AccountsOptions<A = Record<string, any>> = {
store: Store;
table: string;
email?: string;
password?: string;
/** columns returned and kept in the session (never the password) */
expose: string[];
/** how a column reads back (`{ permissions: v => JSON.parse(v) }`), for the exposed ones */
read?: Record<string, (v: any) => unknown>;
hash: (password: string) => string | Promise<string>;
verify: (password: string, hash: string) => boolean | Promise<boolean>;
/** where the signed-in account lives (e.g. nuxt-auth-utils' setUserSession / getUserSession / clearUserSession) */
session: {
/** `body` is the sign-in form, for a remember-me box */
set: (event: H3Event, account: A, body?: Record<string, any>) => unknown;
get: (event: H3Event) => A | null | undefined | Promise<A | null | undefined>;
clear: (event: H3Event) => unknown;
};
/** key of the account in responses (`{ success, subscriber }`); default `account` */
as?: string;
/** other columns a sign-up may set, besides email and password */
signUp?: string[];
minPassword?: number;
reset?: {
table: string;
owner: string;
token?: string;
expires?: string;
used?: string;
minutes?: number;
/** sends the link; `token` goes in it */
send: (account: A, token: string, event: H3Event) => unknown;
};
messages?: Partial<Record<'invalid' | 'taken' | 'required' | 'short' | 'signedOut' | 'badToken', string>>;
/** how a date is stored (a Date for MySQL drivers, ISO text for SQLite by default) */
date?: (d: Date) => unknown;
/** a column that must be truthy for the account to sign in or pass a guard (disabled accounts) */
active?: string;
/** columns written on each sign-in: `{ last_login: () => new Date() }` */
onLogin?: Record<string, () => unknown>;
/** for a plain HTML form (no script): where a sign-in goes on success and on failure */
redirect?: {
success: string;
failure: string;
};
};
export declare function accounts<A extends Record<string, any> = Record<string, any>>(o: AccountsOptions<A>): {
login: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<void | {
[x: string]: boolean | A;
success: boolean;
}>>;
register: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
[x: string]: boolean | A;
success: boolean;
}>>;
logout: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
success: boolean;
}>>;
me: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
[x: string]: A;
}>>;
forgot: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
success: boolean;
}>>;
reset: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
success: boolean;
}>>;
change: import("h3").EventHandler<import("h3").EventHandlerRequest, Promise<{
success: boolean;
}>>;
current: (event: H3Event) => Promise<A | null>;
required: (event: H3Event) => Promise<A>;
guard: (check?: (account: A) => boolean, status?: number, message?: string) => (event: H3Event) => Promise<A>;
};