diff --git a/server/types/config.ts b/server/types/config.ts index d25ef3d..46585a3 100644 --- a/server/types/config.ts +++ b/server/types/config.ts @@ -1,11 +1,14 @@ import {ArrayField, Field, RawJsonField} from "sparkson" -import {ClientConfig, JSONObject, Url} from "./shared"; - -// TODO: Replace string types by more specific types like URL, Password, etc. +import {ClientConfig, isSite, isString, JSONObject, toIsNewtype, Url} from "./shared"; export type Username = string & { readonly __tag: unique symbol }; +export const isUsername = toIsNewtype(isString, "" as Username); + export type CleartextPassword = string & { readonly __tag: unique symbol }; +export const isCleartextPassword = toIsNewtype(isString, "" as CleartextPassword); + export type PasswordHash = string & { readonly __tag: unique symbol }; +export const isPasswordHash = toIsNewtype(isString, "" as PasswordHash); export class UsersConfig { constructor( diff --git a/server/types/index.ts b/server/types/index.ts index e8e9647..f409f04 100644 --- a/server/types/index.ts +++ b/server/types/index.ts @@ -3,6 +3,7 @@ import { Domain, DomainSpecificNodeResponse, EmailAddress, + isNumber, JSONObject, MonitoringResponse, MonitoringState, @@ -13,6 +14,7 @@ import { Site, StoredNode, toIsEnum, + toIsNewtype, } from "./shared"; export * from "./config"; @@ -90,12 +92,13 @@ export function toMonitoringResponse(node: StoredNode): MonitoringResponse { }; } -// TODO: Complete interface / class declaration. export type NodeSecrets = { monitoringToken?: MonitoringToken, }; export type MailId = number & { readonly __tag: unique symbol }; +export const isMailId = toIsNewtype(isNumber, NaN as MailId); + export type MailData = JSONObject; export enum MailType { diff --git a/server/types/shared.ts b/server/types/shared.ts index 6e39b42..62c1a58 100644 --- a/server/types/shared.ts +++ b/server/types/shared.ts @@ -85,6 +85,13 @@ export function isString(arg: unknown): arg is string { return typeof arg === "string" } +export function toIsNewtype< + Type extends Value & { readonly __tag: symbol }, + Value, +>(isValue: TypeGuard, _example: Type): TypeGuard { + return (arg: unknown): arg is Type => isValue(arg); +} + export function isNumber(arg: unknown): arg is number { return typeof arg === "number" } @@ -114,13 +121,13 @@ export function isOptional(arg: unknown, isT: TypeGuard): arg is (T | unde } export type Url = string & { readonly __tag: unique symbol }; -export const isUrl = isString; +export const isUrl = toIsNewtype(isString, "" as Url); export type Version = string & { readonly __tag: unique symbol }; -export const isVersion = isString; +export const isVersion = toIsNewtype(isString, "" as Version); export type EmailAddress = string & { readonly __tag: unique symbol }; -export const isEmailAddress = isString; +export const isEmailAddress = toIsNewtype(isString, "" as EmailAddress); export type NodeStatistics = { registered: number; @@ -321,31 +328,30 @@ export function isClientConfig(arg: unknown): arg is ClientConfig { ); } -// TODO: Token type. export type Token = string & { readonly __tag: unique symbol }; -export const isToken = isString; +export const isToken = toIsNewtype(isString, "" as Token); export type FastdKey = string & { readonly __tag: unique symbol }; -export const isFastdKey = isString; +export const isFastdKey = toIsNewtype(isString, "" as FastdKey); export type MAC = string & { readonly __tag: unique symbol }; -export const isMAC = isString; +export const isMAC = toIsNewtype(isString, "" as MAC); export type DurationSeconds = number & { readonly __tag: unique symbol }; -export const isDurationSeconds = isNumber; +export const isDurationSeconds = toIsNewtype(isNumber, NaN as DurationSeconds); export type UnixTimestampSeconds = number & { readonly __tag: unique symbol }; -export const isUnixTimestampSeconds = isNumber; +export const isUnixTimestampSeconds = toIsNewtype(isNumber, NaN as UnixTimestampSeconds); export type UnixTimestampMilliseconds = number & { readonly __tag: unique symbol }; -export const isUnixTimestampMilliseconds = isNumber; +export const isUnixTimestampMilliseconds = toIsNewtype(isNumber, NaN as UnixTimestampMilliseconds); export function toUnixTimestampSeconds(ms: UnixTimestampMilliseconds): UnixTimestampSeconds { return Math.floor(ms) as UnixTimestampSeconds; } export type MonitoringToken = string & { readonly __tag: unique symbol }; -export const isMonitoringToken = isString; +export const isMonitoringToken = toIsNewtype(isString, "" as MonitoringToken); export enum MonitoringState { ACTIVE = "active", @@ -356,15 +362,16 @@ export enum MonitoringState { export const isMonitoringState = toIsEnum(MonitoringState); export type NodeId = string & { readonly __tag: unique symbol }; +export const isNodeId = toIsNewtype(isString, "" as NodeId); -export type Hostname = string & { readonly __tag: unique symbol }; -export const isHostname = isString; +export type Hostname = string & { readonly __tag: unique symbol } +export const isHostname = toIsNewtype(isString, "" as Hostname); export type Nickname = string & { readonly __tag: unique symbol }; -export const isNickname = isString; +export const isNickname = toIsNewtype(isString, "" as Nickname); export type Coordinates = string & { readonly __tag: unique symbol }; -export const isCoordinates = isString; +export const isCoordinates = toIsNewtype(isString, "" as Coordinates); /** * Basic node data. @@ -473,10 +480,10 @@ export enum OnlineState { export const isOnlineState = toIsEnum(OnlineState); export type Site = string & { readonly __tag: unique symbol }; -export const isSite = isString; +export const isSite = toIsNewtype(isString, "" as Site); export type Domain = string & { readonly __tag: unique symbol }; -export const isDomain = isString; +export const isDomain = toIsNewtype(isString, "" as Domain); /** * Represents a node in the context of a Freifunk site and domain.