From 560de6bba8fa8c01b2da27d6537c8ac6e2974d95 Mon Sep 17 00:00:00 2001 From: raveclassic Date: Thu, 27 Jan 2022 20:20:54 +0300 Subject: [PATCH] feat: abstract --- .../form/src/abstract/build-state.spec.ts | 46 ------ packages/form/src/abstract/build-state.ts | 57 -------- packages/form/src/form.spec.ts | 39 ++++- packages/form/src/form.ts | 123 ++++------------ packages/form/src/hkt.ts | 3 + packages/form/src/index.ts | 31 ++-- .../src/{abstract/schema-f.ts => types.ts} | 137 +++++++----------- 7 files changed, 136 insertions(+), 300 deletions(-) delete mode 100644 packages/form/src/abstract/build-state.spec.ts delete mode 100644 packages/form/src/abstract/build-state.ts rename packages/form/src/{abstract/schema-f.ts => types.ts} (70%) diff --git a/packages/form/src/abstract/build-state.spec.ts b/packages/form/src/abstract/build-state.spec.ts deleted file mode 100644 index 8beddea..0000000 --- a/packages/form/src/abstract/build-state.spec.ts +++ /dev/null @@ -1,46 +0,0 @@ -// import * as I from 'io-ts' -// import * as IT from 'io-ts-types' -// import * as Z from 'zod' -// import * as J from 'joi' -// import * as R from 'runtypes' -// import { Schema11, Schema21, Validation1 } from './schema-f' -// import { either } from 'fp-ts' -// import { buildState } from './build-state' -// import { identity } from '@frp-ts/utils' -// -// describe('build-state', () => { -// describe('zod', () => { -// const buildStateF = buildState(ZODSchema, Exception) -// const state = buildStateF({ foo: Z.string() }, { foo: '' }) -// state.foo.decoded.toLowerCase() -// state.foo.encoded.toLowerCase() -// }) -// describe('zod safe', () => { -// const buildStateF = buildState(ZODSafeSchema, ZODSafeParseReturnType) -// const state = buildStateF({ foo: Z.string() }, { foo: '' }) -// if (state.foo.decoded.success) { -// state.foo.decoded.data.toLowerCase() -// } -// state.foo.encoded.toLowerCase() -// }) -// describe('io-ts', () => { -// it('builds', () => { -// const buildStateF = buildState(IOTSSchema, IOTSValidation) -// const state = buildStateF({ foo: IT.NumberFromString }, { foo: 123 }) -// state.foo.encoded -// either.Monad.map(state.foo.decoded, (n) => n) -// }) -// }) -// describe('joi', () => { -// const buildStateF = buildState(JOISchema, JOIValidationResult) -// const state = buildStateF({ foo: J.string() }, { foo: '123' }) -// state.foo.encoded -// state.foo.decoded -// }) -// describe('runtypes', () => { -// const buildStateF = buildState(RuntypesSchema, RuntypesResult) -// const state = buildStateF({ foo: R.String }, { foo: '123' }) -// state.foo.encoded -// state.foo.decoded -// }) -// }) diff --git a/packages/form/src/abstract/build-state.ts b/packages/form/src/abstract/build-state.ts deleted file mode 100644 index 7684f46..0000000 --- a/packages/form/src/abstract/build-state.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { - DecodedObjectValue1, - DecodedObjectValue2, - FormState11, - FormState21, - FormStateHKT, - Schema11, - Schema21, - SchemaHKT, - UnknownObjectSchema1, - UnknownObjectSchema2, - Validation1, - ValidationHKT, -} from './schema-f' -import { mapRecord } from '@frp-ts/utils' -import { HKT, URIS, URIS2 } from '../hkt' - -export function buildState( - schemaF: Schema21, - validationF: Validation1, -): >( - schema: Schema, - initial: DecodedObjectValue2, -) => FormState21 -export function buildState( - schemaF: Schema11, - validationF: Validation1, -): >( - schema: Schema, - initial: DecodedObjectValue1, -) => FormState11 -export function buildState( - schemaF: SchemaHKT, - validationF: ValidationHKT, -): ( - schema: Record>, - initial: Record, -) => FormStateHKT>> -export function buildState( - schemaF: SchemaHKT, - validationF: ValidationHKT, -): ( - schema: Record>, - initial: Record, -) => FormStateHKT>> { - return (schema, initial) => - mapRecord(schema, (field, name) => { - const value = initial[name] - const encoded = schemaF.encode(field, value) - const decoded = validationF.success(value) - return { - encoded, - decoded, - isDirty: false, - } - }) -} diff --git a/packages/form/src/form.spec.ts b/packages/form/src/form.spec.ts index 1874a52..c4dbb7c 100644 --- a/packages/form/src/form.spec.ts +++ b/packages/form/src/form.spec.ts @@ -4,14 +4,13 @@ import { NumberFromString } from 'io-ts-types/lib/NumberFromString' import * as Z from 'zod' import * as J from 'joi' import * as R from 'runtypes' -import { Schema11, Schema21, Validation1C } from './abstract/schema-f' +import { Schema11, Schema21, Validation1C } from './types' import { either } from 'fp-ts' import { makeNewForm } from './form' import { ValidationError } from 'joi' import { Failure } from 'runtypes/lib/result' declare module './hkt' { - interface URItoKind3 {} interface URItoKind2 { readonly IOTSSchema: I.Type } @@ -20,14 +19,14 @@ declare module './hkt' { readonly JOISchema: J.Schema readonly RuntypesSchema: R.Runtype readonly IOTSValidation: I.Validation - readonly Exception: A + readonly Identity: A readonly ZODSafeParseReturnType: Z.SafeParseReturnType readonly JOIValidationResult: J.ValidationResult readonly RuntypesResult: R.Result } } -const ZODSchema: Schema11<'ZODSchema', 'Exception'> = { +const ZODSchema: Schema11<'ZODSchema', 'Identity'> = { URI: 'ZODSchema', encode: (schema, decoded) => decoded, decode: (schema, encoded) => schema.parse(encoded), @@ -57,8 +56,14 @@ const RuntypesSchema: Schema11<'RuntypesSchema', 'RuntypesResult'> = { decode: (schema, value) => schema.validate(value), } -const Exception: Validation1C<'Exception', never> = { - URI: 'Exception', +const IdentitySchema: Schema11<'Identity', 'Identity'> = { + URI: 'Identity', + encode: (schema, value) => value, + decode: (schema, value) => value, +} + +const IdentityValidation: Validation1C<'Identity', never> = { + URI: 'Identity', success: identity, failure: identity, isSuccess: () => true, @@ -70,8 +75,10 @@ const ZODSafeParseReturnType: Validation1C<'ZODSafeParseReturnType', Z.ZodError> URI: 'ZODSafeParseReturnType', success: (data) => ({ success: true, data }), failure: (error) => ({ success: false, error }), + // eslint-disable-next-line no-restricted-syntax map: (value, f) => (value.success ? { success: true, data: f(value.data) } : (value as never)), isSuccess: (value) => value.success, + // eslint-disable-next-line no-restricted-syntax chain: (value, f) => (value.success ? f(value.data) : (value as never)), } @@ -503,3 +510,23 @@ describe('form', () => { }) }) }) + +describe('zod + exception', () => { + const newForm = makeNewForm(ZODSchema, IdentityValidation) + it('encodes', () => { + const form = newForm({ foo: Z.string() }, { foo: 'foo' }) + expect(form.get()).toEqual({ foo: 'foo' }) + form.views.foo.set('123') + expect(form.get()).toEqual({ foo: '123' }) + }) +}) + +describe('identity + identity', () => { + const newForm = makeNewForm(IdentitySchema, IdentityValidation) + it('encodes', () => { + const form = newForm({ foo: '' }, { foo: 'foo' }) + expect(form.get()).toEqual({ foo: 'foo' }) + form.views.foo.set('123') + expect(form.get()).toEqual({ foo: '123' }) + }) +}) diff --git a/packages/form/src/form.ts b/packages/form/src/form.ts index f78c1c0..3ed436a 100644 --- a/packages/form/src/form.ts +++ b/packages/form/src/form.ts @@ -1,110 +1,29 @@ -import { Atom, combine, newAtom, Property } from '@frp-ts/core' -import { HKT, Kind, URIS, URIS2 } from './hkt' +import { combine, newAtom, Property } from '@frp-ts/core' +import { HKT, URIS, URIS2 } from './hkt' import { DecodedObjectValue1, DecodedObjectValue2, DecodedObjectValueHKT, - DecodedValue1, - DecodedValue2, DecodedValueHKT, - EncodedValue2, + Form11, + Form21, + FormHKT, + FormStateHKT, + FormViewHKT, + FormViewsHKT, Schema11, Schema21, SchemaHKT, + StateItemHKT, UnknownObjectSchema1, UnknownObjectSchema2, - UnknownObjectSchemaHKT, Validation1, Validation1C, ValidationHKT, -} from './abstract/schema-f' -import { buildState } from './abstract/build-state' +} from './types' import { mapRecord, objectEntries, objectValues } from '@frp-ts/utils' -export interface FormViewHKT extends Atom { - readonly decoded: Property> - readonly isDirty: Property - readonly isDecoded: Property -} - -export interface FormView11 extends Atom { - readonly decoded: Property> - readonly isDirty: Property - readonly isDecoded: Property -} - -export interface FormView21 extends Atom { - readonly decoded: Property> - readonly isDirty: Property - readonly isDecoded: Property -} - -export type FormViewsHKT> = { - readonly [Field in keyof Schema]: FormViewHKT> -} - -export type FormViews11< - SchemaURI extends URIS, - ValidationURI extends URIS, - Schema extends UnknownObjectSchema1, -> = { - readonly [Field in keyof Schema]: FormView11> -} - -export type FormViews21< - SchemaURI extends URIS2, - ValidationURI extends URIS, - Schema extends UnknownObjectSchema2, -> = { - readonly [Field in keyof Schema]: FormView21< - ValidationURI, - DecodedValue2, - EncodedValue2 - > -} - -export interface FormHKT> - extends Property>> { - readonly reset: (value?: DecodedObjectValueHKT) => void - readonly commit: () => void - readonly views: FormViewsHKT - readonly isDirty: Property - readonly isDecoded: Property -} - -export interface Form11< - SchemaURI extends URIS, - ValidationURI extends URIS, - Schema extends UnknownObjectSchema1, -> extends Property>> { - readonly reset: (value?: DecodedObjectValue1) => void - readonly commit: () => void - readonly views: FormViews11 - readonly isDirty: Property - readonly isDecoded: Property -} - -export interface Form21< - SchemaURI extends URIS2, - ValidationURI extends URIS, - Schema extends UnknownObjectSchema2, -> extends Property>> { - readonly reset: (value?: DecodedObjectValue2) => void - readonly commit: () => void - readonly views: FormViews21 - readonly isDirty: Property - readonly isDecoded: Property -} - -interface StateItemHKT { - readonly encoded: Value - readonly decoded: HKT - readonly isDirty: boolean -} - -type FormStateHKT> = { - readonly [Field in keyof Schema]: StateItemHKT> -} +export { FormHKT, Form11, Form21, SchemaHKT, Schema11, Schema21, ValidationHKT, Validation1, Validation1C } export function makeNewForm( schemaF: Schema21, @@ -244,3 +163,23 @@ export function makeNewForm( } } } + +function buildState( + schemaF: SchemaHKT, + validationF: ValidationHKT, +): ( + schema: Record>, + initial: Record, +) => FormStateHKT>> { + return (schema, initial) => + mapRecord(schema, (field, name) => { + const value = initial[name] + const encoded = schemaF.encode(field, value) + const decoded = validationF.success(value) + return { + encoded, + decoded, + isDirty: false, + } + }) +} diff --git a/packages/form/src/hkt.ts b/packages/form/src/hkt.ts index 9d028ff..857cc6a 100644 --- a/packages/form/src/hkt.ts +++ b/packages/form/src/hkt.ts @@ -12,12 +12,15 @@ export interface HKT3 extends HKT2 { const URI_TO_KIND = Symbol('URI_TO_KIND') const URI_TO_KIND2 = Symbol('URI_TO_KIND2') const URI_TO_KIND3 = Symbol('URI_TO_KIND3') +// eslint-disable-next-line @typescript-eslint/no-unused-vars export interface URItoKind { readonly [URI_TO_KIND]: never } +// eslint-disable-next-line @typescript-eslint/no-unused-vars export interface URItoKind2 { readonly [URI_TO_KIND2]: never } +// eslint-disable-next-line @typescript-eslint/no-unused-vars export interface URItoKind3 { readonly [URI_TO_KIND3]: never } diff --git a/packages/form/src/index.ts b/packages/form/src/index.ts index 5d090fc..23ddab7 100644 --- a/packages/form/src/index.ts +++ b/packages/form/src/index.ts @@ -1,19 +1,14 @@ -export { FormView, FormViews, Form, newForm } from './form' -export * as form from './form' export { - numberFromStringSchema, - UnknownObjectSchema, - DecodedValue, - EncodedValue, - DecodedObjectValue, - EncodedObjectValue, - numberSchema, - objectSchema, - stringSchema, - ValueSchema, - arraySchema, - optionalSchema, - recordSchema, - booleanSchema, -} from './schema' -export * as schema from './schema' + makeNewForm, + Form11, + Form21, + FormHKT, + Schema21, + Schema11, + SchemaHKT, + ValidationHKT, + Validation1, + Validation1C, +} from './form' +export * as form from './form' +export * as hkt from './hkt' diff --git a/packages/form/src/abstract/schema-f.ts b/packages/form/src/types.ts similarity index 70% rename from packages/form/src/abstract/schema-f.ts rename to packages/form/src/types.ts index 6729253..807d6b7 100644 --- a/packages/form/src/abstract/schema-f.ts +++ b/packages/form/src/types.ts @@ -1,6 +1,5 @@ -import { HKT, HKT2, HKT3, Kind, Kind2, Kind3, URIS, URIS2, URIS3 } from '../hkt' +import { HKT, Kind, Kind2, URIS, URIS2 } from './hkt' import { Atom, Property } from '@frp-ts/core' -import { Either } from '@frp-ts/utils' export interface SchemaHKT { readonly URI: SchemaURI @@ -87,6 +86,8 @@ export type DecodedValue1> = Schema extends Kind2< SchemaURI, + // `any` is required for type inference from `Kin2` + // eslint-disable-next-line @typescript-eslint/no-explicit-any any, infer Decoded > @@ -108,6 +109,7 @@ export type EncodedValue1> = Schema extends Kind2< SchemaURI, infer Encoded, + // eslint-disable-next-line @typescript-eslint/no-explicit-any any > ? Encoded @@ -127,112 +129,85 @@ export type DecodedObjectValue2 } -export interface FormViewHKT extends Atom { - readonly decoded: Property> +export interface FormViewHKT extends Atom { + readonly decoded: Property> readonly isDirty: Property readonly isDecoded: Property } -export interface FormView extends Atom { - readonly decoded: Property> + +export interface FormView11 extends Atom { + readonly decoded: Property> readonly isDirty: Property readonly isDecoded: Property } -export type FormViewsHKT> = { - readonly [Field in keyof Schema]: FormViewHKT< - ValidationURI, - Failure, - DecodedValueHKT, - EncodedValueHKT - > -} - -export interface FormHKT> - extends Property>> { - readonly reset: (value?: DecodedObjectValueHKT) => void - readonly commit: () => void - readonly views: FormViewsHKT +export interface FormView21 extends Atom { + readonly decoded: Property> readonly isDirty: Property readonly isDecoded: Property } -export interface StateItemHKT { - readonly encoded: Encoded - readonly decoded: HKT - readonly isDirty: boolean -} -export interface StateItem1 { - readonly encoded: Encoded - readonly decoded: Kind - readonly isDirty: boolean -} -export interface StateItem2 { - readonly encoded: Encoded - readonly decoded: Kind2 - readonly isDirty: boolean -} -export interface StateItem { - readonly encoded: Encoded - readonly decoded: Kind2 - readonly isDirty: boolean -} - -export type FormStateHKT> = { - readonly [Field in keyof Schema]: StateItemHKT< - ValidationURI, - DecodedValueHKT, - EncodedValueHKT - > +export type FormViewsHKT> = { + readonly [Field in keyof Schema]: FormViewHKT> } - -export type FormState11< +export type FormViews11< SchemaURI extends URIS, ValidationURI extends URIS, Schema extends UnknownObjectSchema1, > = { - readonly [Field in keyof Schema]: StateItem1< - ValidationURI, - DecodedValue1, - EncodedValue1 - > + readonly [Field in keyof Schema]: FormView11> } - -export type FormState12< - SchemaURI extends URIS, - ValidationURI extends URIS2, - Failure, - Schema extends UnknownObjectSchema1, -> = { - readonly [Field in keyof Schema]: StateItem2< - ValidationURI, - Failure, - DecodedValue1, - EncodedValue1 - > -} - -export type FormState21< +export type FormViews21< SchemaURI extends URIS2, ValidationURI extends URIS, Schema extends UnknownObjectSchema2, > = { - readonly [Field in keyof Schema]: StateItem1< + readonly [Field in keyof Schema]: FormView21< ValidationURI, DecodedValue2, EncodedValue2 > } -export type FormState22< +export interface FormHKT> + extends Property>> { + readonly reset: (value?: DecodedObjectValueHKT) => void + readonly commit: () => void + readonly views: FormViewsHKT + readonly isDirty: Property + readonly isDecoded: Property +} + +export interface Form11< + SchemaURI extends URIS, + ValidationURI extends URIS, + Schema extends UnknownObjectSchema1, +> extends Property>> { + readonly reset: (value?: DecodedObjectValue1) => void + readonly commit: () => void + readonly views: FormViews11 + readonly isDirty: Property + readonly isDecoded: Property +} + +export interface Form21< SchemaURI extends URIS2, - ValidationURI extends URIS2, - Failure, + ValidationURI extends URIS, Schema extends UnknownObjectSchema2, -> = { - readonly [Field in keyof Schema]: StateItem2< - ValidationURI, - Failure, - DecodedValue2, - EncodedValue2 - > +> extends Property>> { + readonly reset: (value?: DecodedObjectValue2) => void + readonly commit: () => void + readonly views: FormViews21 + readonly isDirty: Property + readonly isDecoded: Property +} + +export interface StateItemHKT { + readonly encoded: Value + readonly decoded: HKT + readonly isDirty: boolean +} + +export type FormStateHKT> = { + readonly [Field in keyof Schema]: StateItemHKT> }