129 lines
4.7 KiB
TypeScript
129 lines
4.7 KiB
TypeScript
// The shapes the application sees, and the seam that produces them.
|
|
//
|
|
// Everything below is the GENERATED contract schema (`schema.ts`, made by openapi-typescript from
|
|
// `docs/api-contract/openapi.yaml`) with one change: every field carrying a closed vocabulary is
|
|
// widened to `… | null`, and the value is narrowed here, once, where it enters. A screen therefore
|
|
// never indexes a map by a raw wire string — that was the silent crash of BACKLOG Ф-22.
|
|
//
|
|
// The application imports these names, not `schema.ts`: the only `Book` it knows is the narrowed
|
|
// one.
|
|
|
|
import {
|
|
bookStatus,
|
|
noteSeverity,
|
|
pausedReason,
|
|
rejectReason,
|
|
termKind,
|
|
termOrigin,
|
|
termStatus,
|
|
unitState,
|
|
usageState,
|
|
} from './vocabulary';
|
|
import type { components } from './schema';
|
|
|
|
type Schemas = components['schemas'];
|
|
|
|
export type BookStatus = Schemas['BookStatus'];
|
|
export type PausedReason = Schemas['PausedReason'];
|
|
export type RejectReason = Schemas['RejectReason'];
|
|
export type NoteSeverity = Schemas['NoteSeverity'];
|
|
export type TermKind = Schemas['TermKind'];
|
|
export type TermStatus = Schemas['TermStatus'];
|
|
export type TermOrigin = Schemas['TermOrigin'];
|
|
export type UnitState = Schemas['UnitState'];
|
|
export type UsageState = Schemas['Usage']['state'];
|
|
|
|
export type Id = Schemas['Id'];
|
|
export type Revision = Schemas['Revision'];
|
|
export type Progress = Schemas['Progress'];
|
|
export type Counter = Schemas['Counter'];
|
|
export type Chapter = Schemas['Chapter'];
|
|
export type CeilingBounds = Schemas['CeilingBounds'];
|
|
export type BankDecision = Schemas['BankDecision'];
|
|
export type Problem = Schemas['Problem'];
|
|
|
|
export type Book = Omit<Schemas['Book'], 'status' | 'reject_reason'> & {
|
|
status: BookStatus | null;
|
|
// Optional on the wire and narrowed to `null` when unknown: the contract says an absent reason
|
|
// and one this build does not know are the same fact — "why is not known" — and are shown alike.
|
|
reject_reason?: RejectReason | null;
|
|
};
|
|
export type Run = Omit<Schemas['Run'], 'status' | 'paused_reason'> & {
|
|
status: BookStatus | null;
|
|
paused_reason: PausedReason | null;
|
|
};
|
|
export type Note = Omit<Schemas['Note'], 'severity'> & { severity: NoteSeverity | null };
|
|
export type Unit = Omit<Schemas['Unit'], 'state' | 'note'> & {
|
|
state: UnitState | null;
|
|
note?: Note | null;
|
|
};
|
|
export type BankTerm = Omit<Schemas['BankTerm'], 'kind' | 'status' | 'origin'> & {
|
|
kind: TermKind | null;
|
|
status: TermStatus | null;
|
|
origin: TermOrigin | null;
|
|
};
|
|
export type Usage = Omit<Schemas['Usage'], 'state'> & { state: UsageState | null };
|
|
|
|
export type Library = Omit<Schemas['Library'], 'books'> & { books: Book[] };
|
|
export type BookDetail = Omit<Schemas['BookDetail'], 'book' | 'run'> & {
|
|
book: Book;
|
|
run?: Run | null;
|
|
};
|
|
export type ChapterList = Schemas['ChapterList'];
|
|
export type UnitList = Omit<Schemas['UnitList'], 'units'> & { units: Unit[] };
|
|
export type NoteList = Omit<Schemas['NoteList'], 'notes'> & { notes: Note[] };
|
|
export type Bank = Omit<Schemas['Bank'], 'terms'> & { terms: BankTerm[] };
|
|
export type RunOptions = Schemas['RunOptions'];
|
|
|
|
/** Anything the platform answers to a list read: a revision, a cursor and rows. */
|
|
export interface Page {
|
|
revision: Revision;
|
|
next_cursor: Schemas['NextCursor'];
|
|
}
|
|
|
|
export const narrow = {
|
|
book: (wire: Schemas['Book']): Book => ({
|
|
...wire,
|
|
status: bookStatus.read(wire.status),
|
|
reject_reason:
|
|
wire.reject_reason === undefined || wire.reject_reason === null
|
|
? wire.reject_reason
|
|
: rejectReason.read(wire.reject_reason),
|
|
}),
|
|
|
|
run: (wire: Schemas['Run']): Run => ({
|
|
...wire,
|
|
status: bookStatus.read(wire.status),
|
|
paused_reason: wire.paused_reason === null ? null : pausedReason.read(wire.paused_reason),
|
|
}),
|
|
|
|
note: (wire: Schemas['Note']): Note => ({
|
|
...wire,
|
|
severity: noteSeverity.read(wire.severity),
|
|
}),
|
|
|
|
unit: (wire: Schemas['Unit']): Unit => ({
|
|
...wire,
|
|
state: unitState.read(wire.state),
|
|
note: wire.note ? narrow.note(wire.note) : wire.note,
|
|
}),
|
|
|
|
term: (wire: Schemas['BankTerm']): BankTerm => ({
|
|
...wire,
|
|
// A row legally carries no kind at all: a ruby candidate that is neither a name nor a place
|
|
// gets none. "Undecided" and "unknown to this build" collapse into the same null on purpose —
|
|
// both mean the screen shows the row and invents nothing.
|
|
kind: wire.kind === null ? null : termKind.read(wire.kind),
|
|
status: termStatus.read(wire.status),
|
|
origin: termOrigin.read(wire.origin),
|
|
}),
|
|
|
|
usage: (wire: Schemas['Usage']): Usage => ({
|
|
...wire,
|
|
state: usageState.read(wire.state),
|
|
paused_reason:
|
|
wire.paused_reason === undefined || wire.paused_reason === null
|
|
? wire.paused_reason
|
|
: pausedReason.read(wire.paused_reason),
|
|
}),
|
|
};
|