@smartsoft001/auth-shell-dtos

One class, two required fields: the credentials model the auth family renders as a login form.


Install

npm install @smartsoft001/auth-shell-dtos @smartsoft001/models reflect-metadata

The manifest declares @smartsoft001/models as a peer dependency, and reflect-metadata has to be installed alongside it as well. LoginDto carries @Model and @Field from the models package, and those decorators write their metadata through reflect-metadata. There is no NestJS dependency, no database and no HTTP client here.

What it is

The package exports a single class and nothing else. LoginDto is a @Model({}) with username and password, both marked @Field({ required: true }), and username additionally marked focused: true.

It is not a validation schema in the class-validator sense, and no ValidationPipe is involved anywhere in this family. What the decorators buy is metadata that the framework reads at runtime: a form can be generated from the class, the still-empty required fields can be reported before anything is sent, and the object can be trimmed to the fields a given operation may touch. focused: true tells a generated form which control takes the cursor.

Nothing inside the workspace imports it. The token endpoint in @smartsoft001/auth-shell-nestjs takes an IAuthTokenRequest, not a LoginDto, so this class exists for the client side of the exchange: the screen that collects the two values before they become a password grant.

Usage

import { ItemChangedData, UserDto } from '@smartsoft001/crud-shell-dtos';
import { getInvalidFields } from '@smartsoft001/models';

/**
 * `UserDto` marks `username` and `password` as required, so the same metadata
 * check the CRUD service runs before an insert can be reused on the caller
 * side. It is the `@smartsoft001/models` metadata API, not class-validator.
 */
export function missingCredentials(dto: UserDto): string[] {
  return getInvalidFields(dto, 'create', []);
}

/**
 * Turns one entry of the change feed returned by `CrudService.changes(...)`
 * into a log line. The feed is a discriminated union, so the `type` field
 * narrows the payload: only an update carries `removedFields` and
 * `updatedFields`.
 */
export function toChangeMessage(change: ItemChangedData): string {
  switch (change.type) {
    case 'create':
      return `${change.id} created`;
    case 'update': {
      const changed = Object.keys(change.data.updatedFields).length;
      const removed = change.data.removedFields.length;

      return `${change.id} updated: ${changed} changed, ${removed} removed`;
    }
    case 'delete':
      return `${change.id} deleted`;
    default:
      return 'unknown change';
  }
}

The example is the shared one, written against UserDto from @smartsoft001/crud-shell-dtos, and it applies to LoginDto unchanged. The two classes are identical field for field: the same two string properties, the same required: true on both, the same focused: true on username. Swap the import and the type annotation and every line behaves the same way.

The half that matters here is missingCredentials. It runs getInvalidFields(dto, 'create', []) over the model and gets back the names of the required fields that are still empty, in declaration order, which is the same check the server runs before it accepts a record. Doing it on the caller's side is how a login form refuses to submit an incomplete pair. The spec proves both ends of it: an empty instance reports ['username', 'password'], and a filled one reports nothing.

The rest of the region, toChangeMessage and the change-feed union it switches on, belongs to the CRUD package and has no counterpart here. This package has no feed and no interfaces, only the model.

API

LoginDto

A @Model({}) class with two string properties.

FieldDecoratorWhat it means
username@Field({ required: true, focused: true })Mandatory in every mode. focused marks it as the control a generated form focuses first.
password@Field({ required: true })Mandatory in every mode. A property named password defaults to FieldType.password, so no explicit type is needed.

That is the entire public surface. The barrel re-exports ./lib/login.dto and nothing more: no provider array, no interfaces, no constants.