Last active
June 15, 2021 14:41
-
-
Save Talor-A/df50913f373f0d55c7a1a7fb22cf03c3 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* get all keys from T of type `string` (exclude `number` and `symbol`) | |
*/ | |
export type ObjectKeys<T> = Extract<keyof T, string> | |
export type ObjectValues<T> = T[ObjectKeys<T>] | |
/** | |
* Object.keys(), but assert that an array of T's keys is returned, not `string[]` | |
*/ | |
export const objectKeys = <T>(obj: T) => Object.keys(obj) as ObjectKeys<T>[] | |
/** | |
* assert that T has length >=2. | |
*/ | |
export type Tuplify<T extends any[]> = [T[number], T[number], ...T] |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import * as z from "zod" | |
import { JSONValue, MaybePromise, WriteTransaction } from "replicache/out/replicache" | |
import { objectKeys, ObjectKeys, ObjectValues, Tuplify } from "./helper.ts" | |
export interface ZodMutationSchema { | |
[key: string]: z.ZodObject<any> | |
} | |
/** | |
* for an object mapping [key: mutator name] -> validation schema, | |
* get the type of the functions that should be provided to replicache `mutators` key | |
*/ | |
export type MutatorFns<Schema extends ZodMutationSchema> = { | |
[K in ObjectKeys<Schema>]: ( | |
tx: WriteTransaction, | |
args: z.infer<Schema[K]> | |
) => MaybePromise<JSONValue | void> | |
} | |
/** | |
* For each mutator function, returns a function that will first check that | |
* the provided args pass validation and then call the mutator. | |
* | |
* @param schema the schema of [key: name] -> zod validator | |
* @param mutators functions taking tx and args and applying a transformation | |
* @returns an object of mutators that check their associated validator before applying a transformation | |
*/ | |
export const applyValidators = <S extends ZodMutationSchema>(schema: S, mutators: MutatorFns<S>) => | |
Object.fromEntries( | |
objectKeys(mutators).map((name) => { | |
const fn = mutators[name] as MutatorFns<S>[keyof MutatorFns<S>] | |
const validator = schema[name] as S[keyof S] | |
const validationFn: typeof fn = (tx, args) => { | |
const result = validator.parse(args) | |
fn(tx, result) | |
} | |
return [name, validationFn] as const | |
}) | |
) as MutatorFns<S> | |
type SerializedMutationValidator<K extends string, T extends z.ZodObject<any>> = z.ZodObject<{ | |
name: z.ZodLiteral<K> | |
id: z.ZodNumber | |
args: T | |
}> | |
export type SerializedMutations<Schema extends ZodMutationSchema> = { | |
[K in ObjectKeys<Schema>]: SerializedMutationValidator<K, Schema[K]> | |
} | |
export const toSerializedMutations = <Schema extends ZodMutationSchema>(schema: Schema) => { | |
return objectKeys(schema) | |
.map( | |
(name) => | |
[ | |
name, | |
z.object({ | |
name: z.literal(name), | |
id: z.number(), | |
args: schema[name]!, | |
}), | |
] as const | |
) | |
.reduce( | |
(acc, [name, z]) => ({ ...acc, [name]: z }), | |
{} as Record<ObjectKeys<Schema>, z.ZodObject<any>> | |
) as SerializedMutations<Schema> | |
} | |
/** | |
* | |
* @param schema our mutation schema. | |
* @returns | |
*/ | |
export const createPushRequestValidator = <Schema extends ZodMutationSchema>(schema: Schema) => { | |
const serialized = toSerializedMutations(schema) | |
const mutationsArr = Object.values(serialized) as ObjectValues<typeof serialized>[] | |
const pushRequest = z.object({ | |
clientID: z.string(), | |
// z.union expects at least 2 entries, so a plain array type won't suffice. | |
mutations: z.array(z.union(mutationsArr as Tuplify<ObjectValues<typeof serialized>[]>)), | |
}) | |
return pushRequest | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment