Functional Weave
Code in Rust

auth.validate-registration@1.0.0

impl/typescript.ts

2,435 bytes · the TypeScript implementation · view raw

Imports name this capability’s declared dependencies, which fune builds next to it in your project; each one links to its page.

import { normaliseEmail } from "./auth_normalise_email.ts";  ← from auth.normalise-email ^1.0.0 · built alongside by fune
import { checkPassword } from "./auth_password_policy_check_password.ts";
import { type PasswordPolicy } from "./auth_password_policy_types.ts";
import { type RegistrationCheck } from "./auth_validate_registration_types.ts";

const MAX_NAME = 100;

function isAsciiSpace(ch: string): boolean {
  return ch === " " || ch === "\t" || ch === "\n" || ch === "\r" || ch === "\f" || ch === "\v";
}

function trimAscii(text: string): string {
  let start = 0;
  let end = text.length;
  while (start < end && isAsciiSpace(text[start])) start++;
  while (end > start && isAsciiSpace(text[end - 1])) end--;
  return text.slice(start, end);
}

/**
 * A sign-up form's email, password and name checked together: the values to
 * store when valid, and a message for each field that needs fixing.
 */
export function validateRegistration(email: string, password: string, name: string, policy: PasswordPolicy): RegistrationCheck {
  // A non-string (a malformed JSON body) is an empty field, not an exception.
  const emailText = typeof email === "string" ? email : "";
  const passwordText = typeof password === "string" ? password : "";
  const nameText = typeof name === "string" ? name : "";
  const fields: Record<string, string> = {};

  const normalised = normaliseEmail(emailText);
  if (normalised === null) {
    fields.email = trimAscii(emailText) === "" ? "Enter your email address." : "Enter a valid email address, like name@example.com.";
  }

  const trimmedName = trimAscii(nameText);
  const nameChars = Array.from(trimmedName);
  let nameError: string | null = null;
  if (nameChars.length === 0) nameError = "Enter your name.";
  else if (nameChars.length > MAX_NAME) nameError = `Use no more than ${MAX_NAME} characters for your name.`;
  else if (nameChars.some((ch) => ch.charCodeAt(0) < 0x20 || ch.charCodeAt(0) === 0x7f)) nameError = "Your name cannot contain control characters.";

  const check = checkPassword(passwordText, normalised, nameError === null ? trimmedName : null, policy);
  if (passwordText === "") {
    fields.password = "Enter a password.";
  } else if (!check.valid) {
    fields.password = check.failures.map((f) => f.message).join(" ");
  }
  if (nameError !== null) fields.name = nameError;

  return {
    valid: Object.keys(fields).length === 0,
    email: normalised,
    name: nameError === null ? trimmedName : null,
    fields,
  };
}