From 3e0498f96bc63ce485a368d225d593e91aae98f5 Mon Sep 17 00:00:00 2001 From: Dragory <2606411+Dragory@users.noreply.github.com> Date: Mon, 22 Jul 2019 00:09:45 +0300 Subject: [PATCH] Typing fixes; show last reload time in !about --- src/plugins/AutoReactionsPlugin.ts | 2 +- src/plugins/Cases.ts | 5 +-- src/plugins/Censor.ts | 21 +++++++------ src/plugins/CompanionChannels.ts | 2 +- src/plugins/CustomEvents.ts | 2 +- src/plugins/LocateUser.ts | 2 +- src/plugins/Logs.ts | 2 +- src/plugins/MessageSaver.ts | 2 +- src/plugins/ModActions.ts | 13 ++++---- src/plugins/Mutes.ts | 15 ++++----- src/plugins/NameHistory.ts | 2 +- src/plugins/Persist.ts | 2 +- src/plugins/PingableRolesPlugin.ts | 2 +- src/plugins/Post.ts | 2 +- src/plugins/ReactionRoles.ts | 2 +- src/plugins/Reminders.ts | 2 +- src/plugins/SelfGrantableRolesPlugin.ts | 2 +- src/plugins/Slowmode.ts | 2 +- src/plugins/Spam.ts | 41 +++++++++++------------- src/plugins/Starboard.ts | 2 +- src/plugins/Tags.ts | 2 +- src/plugins/Utility.ts | 11 ++++++- src/plugins/ZeppelinPlugin.ts | 42 ++++++++++++++++++++----- src/plugins/availablePlugins.ts | 3 +- src/utils.ts | 7 ++++- src/validatorUtils.ts | 39 +++++++++++++++++++++-- 26 files changed, 154 insertions(+), 75 deletions(-) diff --git a/src/plugins/AutoReactionsPlugin.ts b/src/plugins/AutoReactionsPlugin.ts index c8d07d9d..e937dc7d 100644 --- a/src/plugins/AutoReactionsPlugin.ts +++ b/src/plugins/AutoReactionsPlugin.ts @@ -21,7 +21,7 @@ export class AutoReactionsPlugin extends ZeppelinPlugin { private onMessageCreateFn; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/Cases.ts b/src/plugins/Cases.ts index b51a339a..5d333435 100644 --- a/src/plugins/Cases.ts +++ b/src/plugins/Cases.ts @@ -10,10 +10,11 @@ import { IPluginOptions } from "knub"; import { GuildLogs } from "../data/GuildLogs"; import { LogType } from "../data/LogType"; import * as t from "io-ts"; +import { tNullable } from "../utils"; const ConfigSchema = t.type({ log_automatic_actions: t.boolean, - case_log_channel: t.string, + case_log_channel: tNullable(t.string), }); type TConfigSchema = t.TypeOf; @@ -50,7 +51,7 @@ export class CasesPlugin extends ZeppelinPlugin { protected archives: GuildArchives; protected logs: GuildLogs; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { log_automatic_actions: true, diff --git a/src/plugins/Censor.ts b/src/plugins/Censor.ts index 2dca48ce..7ee32dca 100644 --- a/src/plugins/Censor.ts +++ b/src/plugins/Censor.ts @@ -9,6 +9,7 @@ import { getInviteCodesInString, getUrlsInString, stripObjectToScalars, + tNullable, } from "../utils"; import { ZalgoRegex } from "../data/Zalgo"; import { GuildSavedMessages } from "../data/GuildSavedMessages"; @@ -20,17 +21,17 @@ import * as t from "io-ts"; const ConfigSchema = t.type({ filter_zalgo: t.boolean, filter_invites: t.boolean, - invite_guild_whitelist: t.array(t.string), - invite_guild_blacklist: t.array(t.string), - invite_code_whitelist: t.array(t.string), - invite_code_blacklist: t.array(t.string), + invite_guild_whitelist: tNullable(t.array(t.string)), + invite_guild_blacklist: tNullable(t.array(t.string)), + invite_code_whitelist: tNullable(t.array(t.string)), + invite_code_blacklist: tNullable(t.array(t.string)), allow_group_dm_invites: t.boolean, filter_domains: t.boolean, - domain_whitelist: t.array(t.string), - domain_blacklist: t.array(t.string), - blocked_tokens: t.array(t.string), - blocked_words: t.array(t.string), - blocked_regex: t.array(t.string), + domain_whitelist: tNullable(t.array(t.string)), + domain_blacklist: tNullable(t.array(t.string)), + blocked_tokens: tNullable(t.array(t.string)), + blocked_words: tNullable(t.array(t.string)), + blocked_regex: tNullable(t.array(t.string)), }); type TConfigSchema = t.TypeOf; @@ -43,7 +44,7 @@ export class CensorPlugin extends ZeppelinPlugin { private onMessageCreateFn; private onMessageUpdateFn; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { filter_zalgo: false, diff --git a/src/plugins/CompanionChannels.ts b/src/plugins/CompanionChannels.ts index 4949f84c..8269deef 100644 --- a/src/plugins/CompanionChannels.ts +++ b/src/plugins/CompanionChannels.ts @@ -25,7 +25,7 @@ export class CompanionChannelPlugin extends ZeppelinPlugin { companionChannels: Map = new Map(); - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { channels: {}, diff --git a/src/plugins/CustomEvents.ts b/src/plugins/CustomEvents.ts index 5843d103..aaeb9926 100644 --- a/src/plugins/CustomEvents.ts +++ b/src/plugins/CustomEvents.ts @@ -75,7 +75,7 @@ export class CustomEventsPlugin extends ZeppelinPlugin { private clearTriggers: () => void; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { events: {}, diff --git a/src/plugins/LocateUser.ts b/src/plugins/LocateUser.ts index 948b61ab..1fde5681 100644 --- a/src/plugins/LocateUser.ts +++ b/src/plugins/LocateUser.ts @@ -23,7 +23,7 @@ export class LocatePlugin extends ZeppelinPlugin { private outdatedAlertsTimeout; private usersWithAlerts: string[] = []; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_where: false, diff --git a/src/plugins/Logs.ts b/src/plugins/Logs.ts index 6939f058..78a8974f 100644 --- a/src/plugins/Logs.ts +++ b/src/plugins/Logs.ts @@ -71,7 +71,7 @@ export class LogsPlugin extends ZeppelinPlugin { private excludedUserProps = ["user", "member", "mod"]; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { channels: {}, diff --git a/src/plugins/MessageSaver.ts b/src/plugins/MessageSaver.ts index 4939ff8f..ca4d138d 100644 --- a/src/plugins/MessageSaver.ts +++ b/src/plugins/MessageSaver.ts @@ -16,7 +16,7 @@ export class MessageSaverPlugin extends ZeppelinPlugin { protected savedMessages: GuildSavedMessages; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/ModActions.ts b/src/plugins/ModActions.ts index 7a97b27e..43f74c93 100644 --- a/src/plugins/ModActions.ts +++ b/src/plugins/ModActions.ts @@ -13,6 +13,7 @@ import { NotifyUserStatus, stripObjectToScalars, successMessage, + tNullable, trimLines, ucfirst, UnknownUser, @@ -35,12 +36,12 @@ const ConfigSchema = t.type({ message_on_warn: t.boolean, message_on_kick: t.boolean, message_on_ban: t.boolean, - message_channel: t.string, - warn_message: t.string, - kick_message: t.string, - ban_message: t.string, + message_channel: tNullable(t.string), + warn_message: tNullable(t.string), + kick_message: tNullable(t.string), + ban_message: tNullable(t.string), alert_on_rejoin: t.boolean, - alert_channel: t.string, + alert_channel: tNullable(t.string), can_note: t.boolean, can_warn: t.boolean, can_mute: t.boolean, @@ -84,7 +85,7 @@ export class ModActionsPlugin extends ZeppelinPlugin { this.ignoredEvents = []; } - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { dm_on_warn: true, diff --git a/src/plugins/Mutes.ts b/src/plugins/Mutes.ts index 8e0f3772..926cc84d 100644 --- a/src/plugins/Mutes.ts +++ b/src/plugins/Mutes.ts @@ -13,6 +13,7 @@ import { NotifyUserStatus, stripObjectToScalars, successMessage, + tNullable, ucfirst, UnknownUser, } from "../utils"; @@ -28,14 +29,14 @@ import { Case } from "../data/entities/Case"; import * as t from "io-ts"; const ConfigSchema = t.type({ - mute_role: t.string, - move_to_voice_channel: t.string, + mute_role: tNullable(t.string), + move_to_voice_channel: tNullable(t.string), dm_on_mute: t.boolean, message_on_mute: t.boolean, - message_channel: t.string, - mute_message: t.string, - timed_mute_message: t.string, + message_channel: tNullable(t.string), + mute_message: tNullable(t.string), + timed_mute_message: tNullable(t.string), can_view_list: t.boolean, can_cleanup: t.boolean, @@ -70,7 +71,7 @@ export class MutesPlugin extends ZeppelinPlugin { protected serverLogs: GuildLogs; private muteClearIntervalId: NodeJS.Timer; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { mute_role: null, @@ -366,7 +367,7 @@ export class MutesPlugin extends ZeppelinPlugin { if (!member) { if (!bannedIds) { const bans = await this.guild.getBans(); - bannedIds = bans.map(u => u.id); + bannedIds = bans.map(u => u.user.id); } muteWithDetails.banned = bannedIds.includes(mute.user_id); diff --git a/src/plugins/NameHistory.ts b/src/plugins/NameHistory.ts index aeb2ed36..d869b04e 100644 --- a/src/plugins/NameHistory.ts +++ b/src/plugins/NameHistory.ts @@ -18,7 +18,7 @@ export class NameHistoryPlugin extends ZeppelinPlugin { protected nicknameHistory: GuildNicknameHistory; protected usernameHistory: UsernameHistory; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_view: false, diff --git a/src/plugins/Persist.ts b/src/plugins/Persist.ts index 24628874..1e3b8d74 100644 --- a/src/plugins/Persist.ts +++ b/src/plugins/Persist.ts @@ -22,7 +22,7 @@ export class PersistPlugin extends ZeppelinPlugin { protected persistedData: GuildPersistedData; protected logs: GuildLogs; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { persisted_roles: [], diff --git a/src/plugins/PingableRolesPlugin.ts b/src/plugins/PingableRolesPlugin.ts index d4d6f1be..7638c976 100644 --- a/src/plugins/PingableRolesPlugin.ts +++ b/src/plugins/PingableRolesPlugin.ts @@ -21,7 +21,7 @@ export class PingableRolesPlugin extends ZeppelinPlugin { protected cache: Map; protected timeouts: Map; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/Post.ts b/src/plugins/Post.ts index 8750a56f..5822b17a 100644 --- a/src/plugins/Post.ts +++ b/src/plugins/Post.ts @@ -58,7 +58,7 @@ export class PostPlugin extends ZeppelinPlugin { clearTimeout(this.scheduledPostLoopTimeout); } - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_post: false, diff --git a/src/plugins/ReactionRoles.ts b/src/plugins/ReactionRoles.ts index 08a78200..17dab7ee 100644 --- a/src/plugins/ReactionRoles.ts +++ b/src/plugins/ReactionRoles.ts @@ -55,7 +55,7 @@ export class ReactionRolesPlugin extends ZeppelinPlugin { private autoRefreshTimeout; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { auto_refresh_interval: MIN_AUTO_REFRESH, diff --git a/src/plugins/Reminders.ts b/src/plugins/Reminders.ts index e91598a4..6f2c214d 100644 --- a/src/plugins/Reminders.ts +++ b/src/plugins/Reminders.ts @@ -32,7 +32,7 @@ export class RemindersPlugin extends ZeppelinPlugin { private postRemindersTimeout; private unloaded = false; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_use: false, diff --git a/src/plugins/SelfGrantableRolesPlugin.ts b/src/plugins/SelfGrantableRolesPlugin.ts index adf72f3f..bf9cb936 100644 --- a/src/plugins/SelfGrantableRolesPlugin.ts +++ b/src/plugins/SelfGrantableRolesPlugin.ts @@ -18,7 +18,7 @@ export class SelfGrantableRolesPlugin extends ZeppelinPlugin { protected selfGrantableRoles: GuildSelfGrantableRoles; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/Slowmode.ts b/src/plugins/Slowmode.ts index e0809a50..df4801d0 100644 --- a/src/plugins/Slowmode.ts +++ b/src/plugins/Slowmode.ts @@ -42,7 +42,7 @@ export class SlowmodePlugin extends ZeppelinPlugin { private onMessageCreateFn; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { use_native_slowmode: true, diff --git a/src/plugins/Spam.ts b/src/plugins/Spam.ts index cdd32399..d398fb3f 100644 --- a/src/plugins/Spam.ts +++ b/src/plugins/Spam.ts @@ -8,6 +8,7 @@ import { getUserMentions, noop, stripObjectToScalars, + tNullable, trimLines, } from "../utils"; import { LogType } from "../data/LogType"; @@ -23,30 +24,26 @@ import { MuteResult, MutesPlugin } from "./Mutes"; import { CasesPlugin } from "./Cases"; import * as t from "io-ts"; -const BaseSingleSpamConfig = t.intersection([ - t.type({ - interval: t.number, - count: t.number, - }), - t.partial({ - mute: t.boolean, - mute_time: t.number, - clean: t.boolean, - }), -]); +const BaseSingleSpamConfig = t.type({ + interval: t.number, + count: t.number, + mute: tNullable(t.boolean), + mute_time: tNullable(t.number), + clean: tNullable(t.boolean), +}); type TBaseSingleSpamConfig = t.TypeOf; const ConfigSchema = t.type({ - max_censor: BaseSingleSpamConfig, - max_messages: BaseSingleSpamConfig, - max_mentions: BaseSingleSpamConfig, - max_links: BaseSingleSpamConfig, - max_attachments: BaseSingleSpamConfig, - max_emojis: BaseSingleSpamConfig, - max_newlines: BaseSingleSpamConfig, - max_duplicates: BaseSingleSpamConfig, - max_characters: BaseSingleSpamConfig, - max_voice_moves: BaseSingleSpamConfig, + max_censor: tNullable(BaseSingleSpamConfig), + max_messages: tNullable(BaseSingleSpamConfig), + max_mentions: tNullable(BaseSingleSpamConfig), + max_links: tNullable(BaseSingleSpamConfig), + max_attachments: tNullable(BaseSingleSpamConfig), + max_emojis: tNullable(BaseSingleSpamConfig), + max_newlines: tNullable(BaseSingleSpamConfig), + max_duplicates: tNullable(BaseSingleSpamConfig), + max_characters: tNullable(BaseSingleSpamConfig), + max_voice_moves: tNullable(BaseSingleSpamConfig), }); type TConfigSchema = t.TypeOf; @@ -99,7 +96,7 @@ export class SpamPlugin extends ZeppelinPlugin { private expiryInterval; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { max_censor: null, diff --git a/src/plugins/Starboard.ts b/src/plugins/Starboard.ts index 032217e1..02af31e8 100644 --- a/src/plugins/Starboard.ts +++ b/src/plugins/Starboard.ts @@ -32,7 +32,7 @@ export class StarboardPlugin extends ZeppelinPlugin { private onMessageDeleteFn; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_manage: false, diff --git a/src/plugins/Tags.ts b/src/plugins/Tags.ts index 8ee095d7..2937f026 100644 --- a/src/plugins/Tags.ts +++ b/src/plugins/Tags.ts @@ -34,7 +34,7 @@ export class TagsPlugin extends ZeppelinPlugin { protected tagFunctions; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { prefix: "!!", diff --git a/src/plugins/Utility.ts b/src/plugins/Utility.ts index d86a02e4..de49a7f4 100644 --- a/src/plugins/Utility.ts +++ b/src/plugins/Utility.ts @@ -90,8 +90,9 @@ export class UtilityPlugin extends ZeppelinPlugin { protected archives: GuildArchives; protected lastFullMemberRefresh = 0; + protected lastReload; - getDefaultOptions(): IPluginOptions { + protected static getStaticDefaultOptions(): IPluginOptions { return { config: { can_roles: false, @@ -142,6 +143,8 @@ export class UtilityPlugin extends ZeppelinPlugin { this.savedMessages = GuildSavedMessages.getGuildInstance(this.guildId); this.archives = GuildArchives.getGuildInstance(this.guildId); + this.lastReload = Date.now(); + if (activeReloads && activeReloads.has(this.guildId)) { activeReloads.get(this.guildId).createMessage(successMessage("Reloaded!")); activeReloads.delete(this.guildId); @@ -1003,8 +1006,14 @@ export class UtilityPlugin extends ZeppelinPlugin { const shard = this.bot.shards.get(this.bot.guildShardMap[this.guildId]); + const lastReload = humanizeDuration(Date.now() - this.lastReload, { + largest: 2, + round: true, + }); + const basicInfoRows = [ ["Uptime", prettyUptime], + ["Last reload", `${lastReload} ago`], ["Last update", moment(lastCommit.committer.date, "X").format("LL [at] H:mm [(UTC)]")], ["Version", lastCommit.shortHash], ["API latency", `${shard.latency}ms`], diff --git a/src/plugins/ZeppelinPlugin.ts b/src/plugins/ZeppelinPlugin.ts index 8d870c3d..a5ddd86a 100644 --- a/src/plugins/ZeppelinPlugin.ts +++ b/src/plugins/ZeppelinPlugin.ts @@ -1,4 +1,4 @@ -import { IBasePluginConfig, IPluginOptions, logger, Plugin } from "knub"; +import { IBasePluginConfig, IPluginOptions, logger, Plugin, configUtils } from "knub"; import { PluginRuntimeError } from "../PluginRuntimeError"; import * as t from "io-ts"; import { pipe } from "fp-ts/lib/pipeable"; @@ -29,19 +29,47 @@ export class ZeppelinPlugin extends Plug return ourLevel > memberLevel; } + protected static getStaticDefaultOptions() { + // Implemented by plugin + return {}; + } + + protected getDefaultOptions(): IPluginOptions { + return (this.constructor as typeof ZeppelinPlugin).getStaticDefaultOptions() as IPluginOptions; + } + public static validateOptions(options: any): string[] | null { // Validate config values if (this.configSchema) { if (options.config) { - const errors = validateStrict(this.configSchema, options.config); - if (errors) return errors; + const merged = configUtils.mergeConfig( + {}, + (this.getStaticDefaultOptions() as any).config || {}, + options.config, + ); + const errors = validateStrict(this.configSchema, merged); + if (errors) { + return errors; + } } if (options.overrides) { - for (const override of options.overrides) { + for (const [i, override] of options.overrides.entries()) { if (override.config) { - const errors = validateStrict(this.configSchema, override.config); - if (errors) return errors; + // For type checking overrides, apply default config + supplied config + any overrides preceding this override + finally this override + // Exhaustive type checking would require checking against all combinations of preceding overrides but that's... costy. This will do for now. + // TODO: Override default config retrieval functions and do some sort of memoized checking there? + const merged = configUtils.mergeConfig( + {}, + (this.getStaticDefaultOptions() as any).config || {}, + options.config || {}, + ...options.overrides.slice(0, i), + override.config, + ); + const errors = validateStrict(this.configSchema, merged); + if (errors) { + return errors; + } } } } @@ -55,7 +83,7 @@ export class ZeppelinPlugin extends Plug const mergedOptions = this.getMergedOptions(); const validationErrors = ((this.constructor as unknown) as typeof ZeppelinPlugin).validateOptions(mergedOptions); if (validationErrors) { - throw new Error(`Invalid options:\n${validationErrors.join("\n")}`); + throw new Error(validationErrors.join("\n")); } return super.runLoad(); diff --git a/src/plugins/availablePlugins.ts b/src/plugins/availablePlugins.ts index e0e00c1d..acde2d9e 100644 --- a/src/plugins/availablePlugins.ts +++ b/src/plugins/availablePlugins.ts @@ -25,6 +25,7 @@ import { GuildInfoSaverPlugin } from "./GuildInfoSaver"; import { LogServerPlugin } from "./LogServer"; import { CompanionChannelPlugin } from "./CompanionChannels"; import { LocatePlugin } from "./LocateUser"; +import { GuildConfigReloader } from "./GuildConfigReloader"; /** * Plugins available to be loaded for individual guilds @@ -70,4 +71,4 @@ export const basePlugins = [ /** * Available global plugins (can't be loaded per-guild, only globally) */ -export const availableGlobalPlugins = [BotControlPlugin, UsernameSaver, LogServerPlugin]; +export const availableGlobalPlugins = [BotControlPlugin, UsernameSaver, LogServerPlugin, GuildConfigReloader]; diff --git a/src/utils.ts b/src/utils.ts index e67b94c1..27c2081c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -11,7 +11,8 @@ import { } from "eris"; import url from "url"; import tlds from "tlds"; -import emojiRegex from "emoji-regex/es2015/text"; +import emojiRegex from "emoji-regex/text"; +import * as t from "io-ts"; import fs from "fs"; const fsp = fs.promises; @@ -28,6 +29,10 @@ const delayStringMultipliers = { s: 1000, }; +export function tNullable(type: t.Mixed) { + return t.union([type, t.undefined, t.null]); +} + /** * Turns a "delay string" such as "1h30m" to milliseconds */ diff --git a/src/validatorUtils.ts b/src/validatorUtils.ts index 80714cbd..94aa9cd8 100644 --- a/src/validatorUtils.ts +++ b/src/validatorUtils.ts @@ -1,7 +1,42 @@ import * as t from "io-ts"; import { pipe } from "fp-ts/lib/pipeable"; import { fold } from "fp-ts/lib/Either"; -import { PathReporter } from "io-ts/lib/PathReporter"; +import { noop } from "./utils"; + +// From io-ts/lib/PathReporter +function stringify(v) { + if (typeof v === "function") { + return t.getFunctionName(v); + } + if (typeof v === "number" && !isFinite(v)) { + if (isNaN(v)) { + return "NaN"; + } + return v > 0 ? "Infinity" : "-Infinity"; + } + return JSON.stringify(v); +} + +// From io-ts/lib/PathReporter +// tslint:disable +function getContextPath(context) { + return context + .map(function(_a) { + var key = _a.key, + type = _a.type; + return key + ": " + type.name; + }) + .join("/"); +} +// tslint:enable + +const report = fold((errors: any) => { + return errors.map(err => { + if (err.message) return err.message; + const context = err.context.map(c => c.key).filter(k => k && !k.startsWith("{")); + return `Invalid value <${stringify(err.value)}> supplied to <${context.join("/")}>`; + }); +}, noop); /** * Validates the given value against the given schema while also disallowing extra properties @@ -12,7 +47,7 @@ export function validateStrict(schema: t.Type, value: any): strin return pipe( validationResult, fold( - err => PathReporter.report(validationResult), + err => report(validationResult), result => { // Make sure there are no extra properties if (JSON.stringify(value) !== JSON.stringify(result)) {