From 5990a9a45a3f7d611ef239956a5562cedeca80b5 Mon Sep 17 00:00:00 2001 From: kirillgroshkov Date: Sat, 9 Nov 2024 11:45:44 +0100 Subject: [PATCH] feat: use branded IsoDate --- scripts/dateStringTodayBench.script.ts | 4 ++-- src/validation/ajv/ajv.test.ts | 12 +++++++++--- src/validation/joi/joi.shared.schemas.test.ts | 6 +++--- src/validation/joi/joi.shared.schemas.ts | 6 ++++-- src/validation/joi/string.extensions.test.ts | 4 ++-- src/validation/joi/string.extensions.ts | 18 +++++++++--------- yarn.lock | 18 +++++++++--------- 7 files changed, 38 insertions(+), 30 deletions(-) diff --git a/scripts/dateStringTodayBench.script.ts b/scripts/dateStringTodayBench.script.ts index bd9dbe8..cf53de8 100644 --- a/scripts/dateStringTodayBench.script.ts +++ b/scripts/dateStringTodayBench.script.ts @@ -5,7 +5,7 @@ yarn tsn dateStringTodayBench */ import { runBenchScript } from '@naturalcycles/bench-lib' -import { localDate } from '@naturalcycles/js-lib' +import { IsoDate, localDate } from '@naturalcycles/js-lib' import { isValid, objectSchema, stringSchema } from '../src' const entries = localDate @@ -15,7 +15,7 @@ const entries = localDate })) const entrySchema1 = objectSchema({ - date: stringSchema.dateString('2000-01-01', 'today'), + date: stringSchema.dateString('2000-01-01' as IsoDate, 'today'), }) const entrySchema2 = objectSchema({ diff --git a/src/validation/ajv/ajv.test.ts b/src/validation/ajv/ajv.test.ts index b599f9f..ab734ef 100644 --- a/src/validation/ajv/ajv.test.ts +++ b/src/validation/ajv/ajv.test.ts @@ -1,4 +1,4 @@ -import type { JsonSchema } from '@naturalcycles/js-lib' +import type { IsoDate, JsonSchema } from '@naturalcycles/js-lib' import { _deepFreeze, _try, jsonSchema, localTime } from '@naturalcycles/js-lib' import { _inspect } from '../../index' import { testDir } from '../../test/paths.cnst' @@ -187,9 +187,15 @@ test.each([ [{ type: 'string', format: 'countryCode' }, ['se', 'sve']], [{ type: 'string', format: 'currency' }, ['sek', 'us']], [{ type: 'number', format: 'unixTimestamp' }, [1232342342000, -1]], - [{ type: 'number', format: 'unixTimestamp2000' }, [1232342342000, localTime('1999-01-01').unix]], + [ + { type: 'number', format: 'unixTimestamp2000' }, + [1232342342000, localTime('1999-01-01' as IsoDate).unix], + ], [{ type: 'number', format: 'unixTimestampMillis' }, [-1]], - [{ type: 'number', format: 'unixTimestampMillis2000' }, [-1, localTime('1999-01-01').unixMillis]], + [ + { type: 'number', format: 'unixTimestampMillis2000' }, + [-1, localTime('1999-01-01' as IsoDate).unixMillis], + ], [{ type: 'number', format: 'utcOffset' }, [-15 * 60]], [{ type: 'number', format: 'utcOffsetHours' }, [-15, 15]], ] as [JsonSchema, any[]][])('%s should be invalid', (schema, objects: any[]) => { diff --git a/src/validation/joi/joi.shared.schemas.test.ts b/src/validation/joi/joi.shared.schemas.test.ts index 226ecbe..e41e827 100644 --- a/src/validation/joi/joi.shared.schemas.test.ts +++ b/src/validation/joi/joi.shared.schemas.test.ts @@ -1,5 +1,5 @@ import { expectTypeOf } from '@naturalcycles/dev-lib/dist/testing' -import { BaseDBEntity, localTime } from '@naturalcycles/js-lib' +import { BaseDBEntity, IsoDateTime, localTime } from '@naturalcycles/js-lib' import { testValidation } from '../../test/validation.test.util' import { baseDBEntitySchema, @@ -226,7 +226,7 @@ describe('dateTimeStringSchema', () => { '2024-09-30T00:55:12', '2024-09-30T00:55:12+02:00', '2024-09-30T00:55:12Z', - ] + ] as IsoDateTime[] test.each(validDateTimes)('valid dateTime: %s', s => { expect(isValid(s, dateTimeStringSchema)).toBe(true) @@ -249,7 +249,7 @@ describe('dateTimeStringSchema', () => { 'extra2024-07-25T00:55Z', // Extra characters before a valid datetime 'Some random string', // Random string '2024 was a good year', // Year with some text - ] + ] as IsoDateTime[] test.each(invalidDateTimes)('invalid dateTime: %s', s => { expect(isValid(s, dateTimeStringSchema)).toBe(false) diff --git a/src/validation/joi/joi.shared.schemas.ts b/src/validation/joi/joi.shared.schemas.ts index f7aa9b0..d0f16a5 100644 --- a/src/validation/joi/joi.shared.schemas.ts +++ b/src/validation/joi/joi.shared.schemas.ts @@ -4,6 +4,8 @@ import { _stringEnumKeys, _stringEnumValues, BaseDBEntity, + IsoDate, + IsoDateTime, NumberEnum, StringEnum, UnixTimestamp, @@ -22,7 +24,7 @@ export const numberSchema = Joi.number() export const numberSchemaTyped = (): NumberSchema => Joi.number() export const integerSchema = Joi.number().integer() export const percentageSchema = Joi.number().integer().min(0).max(100) -export const dateStringSchema = stringSchema.dateString() +export const dateStringSchema = stringSchema.dateString() as StringSchema export const binarySchema = Joi.binary() export const dateObjectSchema = Joi.object().instance(Date) @@ -36,7 +38,7 @@ export const DATE_TIME_STRING_REGEX = export const dateTimeStringSchema = stringSchema.regex(DATE_TIME_STRING_REGEX).messages({ 'string.pattern.base': `must be a DateTime string`, -}) +}) as StringSchema /** * Allows all values of a String Enum. diff --git a/src/validation/joi/string.extensions.test.ts b/src/validation/joi/string.extensions.test.ts index 34ec550..126de87 100644 --- a/src/validation/joi/string.extensions.test.ts +++ b/src/validation/joi/string.extensions.test.ts @@ -1,4 +1,4 @@ -import { localTime } from '@naturalcycles/js-lib' +import { IsoDate, localTime } from '@naturalcycles/js-lib' import { testValidation } from '../../test/validation.test.util' import { stringSchema } from './joi.shared.schemas' @@ -13,7 +13,7 @@ test('dateString', () => { }) test('dateString min/max', async () => { - const schema = stringSchema.dateString('2017-06-21', '2017-06-23') + const schema = stringSchema.dateString('2017-06-21' as IsoDate, '2017-06-23' as IsoDate) testValidation( schema, diff --git a/src/validation/joi/string.extensions.ts b/src/validation/joi/string.extensions.ts index 7e65614..4410de3 100644 --- a/src/validation/joi/string.extensions.ts +++ b/src/validation/joi/string.extensions.ts @@ -1,14 +1,14 @@ -import { localTime } from '@naturalcycles/js-lib' +import { IsoDate, localTime } from '@naturalcycles/js-lib' import type Joi from 'joi' import { Extension, StringSchema as JoiStringSchema } from 'joi' export interface StringSchema extends JoiStringSchema { - dateString: (min?: string, max?: string) => this + dateString: (min?: IsoDate | 'today', max?: IsoDate | 'today') => this } export interface JoiDateStringOptions { - min?: string - max?: string + min?: IsoDate | 'today' + max?: IsoDate | 'today' } export function stringExtensions(joi: typeof Joi): Extension { @@ -24,7 +24,7 @@ export function stringExtensions(joi: typeof Joi): Extension { }, rules: { dateString: { - method(min?: string, max?: string) { + method(min?: IsoDate, max?: IsoDate) { return this.$_addRule({ name: 'dateString', args: { min, max } satisfies JoiDateStringOptions, @@ -101,11 +101,11 @@ function isLeapYear(year: number): boolean { } let lastCheckedPlus = 0 -let todayStrPlusCached: string +let todayStrPlusCached: IsoDate let lastCheckedMinus = 0 -let todayStrMinusCached: string +let todayStrMinusCached: IsoDate -function getTodayStrPlus15(): string { +function getTodayStrPlus15(): IsoDate { const now = Date.now() if (now - lastCheckedPlus < 3_600_000) { // cached for 1 hour @@ -116,7 +116,7 @@ function getTodayStrPlus15(): string { return (todayStrPlusCached = localTime.now().plus(15, 'hour').toISODate()) } -function getTodayStrMinus15(): string { +function getTodayStrMinus15(): IsoDate { const now = Date.now() if (now - lastCheckedMinus < 3_600_000) { // cached for 1 hour diff --git a/yarn.lock b/yarn.lock index 933f17a..b0c1f59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1009,9 +1009,9 @@ typescript "^5.0.2" "@naturalcycles/dev-lib@^15.0.3": - version "15.27.1" - resolved "https://registry.yarnpkg.com/@naturalcycles/dev-lib/-/dev-lib-15.27.1.tgz#8620e279e92f6b76bf01446094bfba1fa4482aa9" - integrity sha512-3mov6Hfd7BXpjXhuLi5MZC2bBiLfXNLDkbVsYAvYorx587w9sQMLtI7PvlnI+R370joO+/kAxjJFXIb3RaINCg== + version "15.27.2" + resolved "https://registry.yarnpkg.com/@naturalcycles/dev-lib/-/dev-lib-15.27.2.tgz#b69473f2cbf191b44dfb219b1dc728a858876eff" + integrity sha512-54o9VExhppDsQ7NxiX+7soxpvg++5grsVgL1pyYGDypeH/xIhaAipLj9z3GALCyRk3ohAa8RkQ0I+FrW59ZVsg== dependencies: "@biomejs/biome" "^1.8.3" "@commitlint/cli" "^19.0.0" @@ -1047,17 +1047,17 @@ yargs "^17.0.0" "@naturalcycles/js-lib@^14.0.0", "@naturalcycles/js-lib@^14.244.0": - version "14.260.0" - resolved "https://registry.yarnpkg.com/@naturalcycles/js-lib/-/js-lib-14.260.0.tgz#1086a684ff4ccd028be053d9fa701d9cd5358dbf" - integrity sha512-GfkmD+xAP8GODx2kh++5MErmDMu50S/QAslbtoaKtpWrmFlfaMEtNpYLXLDYGjcj4NJRilbE9hwiM8Rizs5V2g== + version "14.261.0" + resolved "https://registry.yarnpkg.com/@naturalcycles/js-lib/-/js-lib-14.261.0.tgz#e85aaa6c68b5efeada4e4cfdcb5c00e5e30a8d3a" + integrity sha512-7+jI98zjNcifAkQu8q4UHhXzeclJVIDfoE8GHpwdqjbaT6a9p2KOikvBjMd1SdCH1OTR6ijBPNvax1VS9Jb2qw== dependencies: tslib "^2.0.0" zod "^3.20.2" "@naturalcycles/nodejs-lib@^13.0.1", "@naturalcycles/nodejs-lib@^13.0.2": - version "13.35.0" - resolved "https://registry.yarnpkg.com/@naturalcycles/nodejs-lib/-/nodejs-lib-13.35.0.tgz#473f7ddfb3432e5a91d803950868c070fcba0e59" - integrity sha512-Dwil0k3oDjDBHKeEw6qxEiHUidoFHYlaxiLtUaADeTSa8FwgHKw6PIyax3pYcaITkJ0EQftpDTosTKhOr2UVRw== + version "13.36.0" + resolved "https://registry.yarnpkg.com/@naturalcycles/nodejs-lib/-/nodejs-lib-13.36.0.tgz#cea71bd4e88ba37d28cb4af9a12fd5e0678d4fd0" + integrity sha512-v6t2P2HGFhaCFN9iM5Fgvg7qnf7FAK76SatCLGoQtdozyUX7xnPj8xUe35ft2YGvmu1TaWU4UWqRKTJEWGwByA== dependencies: "@naturalcycles/js-lib" "^14.244.0" "@types/js-yaml" "^4.0.9"