Skip to content

Commit

Permalink
fixed multi level objects
Browse files Browse the repository at this point in the history
  • Loading branch information
overthemike committed Sep 1, 2024
1 parent 71db130 commit ad1b1c2
Show file tree
Hide file tree
Showing 2 changed files with 195 additions and 212 deletions.
193 changes: 88 additions & 105 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,191 +1,174 @@
/* eslint-disable */
import { z, ZodType } from 'zod';
import { proxy as vproxy, useSnapshot as vsnap } from 'valtio';
import _ from 'lodash';
import { z, ZodType } from 'zod'
import { proxy as vproxy, useSnapshot as vsnap } from 'valtio'
import _ from 'lodash'

type ValtioProxy<T> = {
[P in keyof T]: T[P];
};
[P in keyof T]: T[P]
}

type SchemaConfig = {
parseAsync?: boolean;
safeParse?: boolean;
errorHandler?: (error: unknown) => void;
};
parseAsync?: boolean
safeParse?: boolean
errorHandler?: (error: unknown) => void
}

const defaultConfig = {
parseAsync: false,
safeParse: false,
errorHandler: (error: unknown) => console.error(error),
};
errorHandler: (error: unknown) => console.error(error)
}

export const vzGlobalConfig = {
safeParse: false,
errorHandler: (error: unknown) => console.error(error),
};
errorHandler: (error: unknown) => console.error(error)
}

const isObject = (x: unknown): x is object =>
typeof x === 'object' && x !== null;
typeof x === 'object' && x !== null

type MergedConfig = Required<SchemaConfig>;
type MergedConfig = Required<SchemaConfig>

type SchemaMeta = SchemaConfig & {
initialState: unknown;
};
initialState: unknown
}

type PropType = string | number | symbol;
const schemaMeta = new WeakMap<ZodType<any>, SchemaMeta>();
const pathList = new WeakMap<{}, PropType[]>();
type PropType = string | number | symbol
const schemaMeta = new WeakMap<ZodType<any>, SchemaMeta>()
const pathList = new WeakMap<{}, PropType[]>()

type SchemaReturn<T extends ZodType<any>> = {
proxy: {
(initialState: any, config?: SchemaConfig): ValtioProxy<z.infer<T>>;
};
};

function updateObjectAtPath(obj: any, path: PropType[], newValue: any) {
let stack = [...path];
let object = obj;

while (stack.length > 1) {
const key = stack.shift();
if (key === undefined) return;
if (!object[key] || typeof object[key] !== 'object') {
object[key] = {};
}
object = object[key];
(initialState: any, config?: SchemaConfig): ValtioProxy<z.infer<T>>
}

const lastKey = stack.shift();
if (lastKey !== undefined) object[lastKey] = newValue;
}

const valtioStoreSymbol = Symbol('valtioStore');
const valtioStoreSymbol = Symbol('valtioStore')

export const useSnapshot = (store: any) => {
return vsnap(store[valtioStoreSymbol]);
};
return vsnap(store[valtioStoreSymbol])
}

export const schema = <T extends ZodType<any>>(
zodSchema: T,
zodSchema: T
): SchemaReturn<T> => {
let valtioProxy: any;
const proxy = (
initialState: z.infer<T>,
config: SchemaConfig = {},
config: SchemaConfig = {}
): ValtioProxy<z.infer<T>> => {
if (!isObject(initialState)) {
throw new Error('object required');
throw new Error('object required')
}

const mergedConfig: MergedConfig = { ...defaultConfig, ...config };
const mergedConfig: MergedConfig = { ...defaultConfig, ...config }

const parseAsync = mergedConfig.parseAsync;
const safeParse = mergedConfig.safeParse;
const errorHandler = mergedConfig.errorHandler;
const parseAsync = mergedConfig.parseAsync
const safeParse = mergedConfig.safeParse
const errorHandler = mergedConfig.errorHandler

// before proxying, validate the initial state
if (parseAsync) {
zodSchema.parseAsync(initialState).catch((e) => {
throw e;
});
throw e
})
} else {
zodSchema.parse(initialState);
zodSchema.parse(initialState)
}

valtioProxy = vproxy(initialState);
const valtioProxy = vproxy(initialState)

const createProxy = (target: any, parentPath: PropType[] = []): any => {
if (!schemaMeta.has(zodSchema)) {
schemaMeta.set(zodSchema, {
safeParse,
parseAsync,
errorHandler,
initialState,
});
initialState
})
}

return new Proxy(target, {
// get(target, prop, receiver) {
// const value = Reflect.get(target, prop, receiver)
// if (isObject(value)) {
// const newPath = parentPath.concat(prop)
// pathList.set(value, newPath)
// return createProxy(value, newPath)
// } else {
// return value
// }
// },
get(target, prop, receiver) {
const value = Reflect.get(target, prop, receiver)
if (isObject(value)) {
const newPath = parentPath.concat(prop)
pathList.set(value, newPath)
return createProxy(value, newPath)
} else {
const pathToSet = [...(pathList.get(target) || []), prop]
return _.get(valtioProxy, pathToSet, value)
}
},
set(target, prop, value, receiver) {
const originalObject = schemaMeta.get(zodSchema)!
.initialState as z.infer<T>;
.initialState as z.infer<T>

const objectToValidate = JSON.parse(JSON.stringify(originalObject));
const path = (pathList.get(target) || []).concat(prop);
const objectToValidate = _.cloneDeep(originalObject)
const path = (pathList.get(target) || []).concat(prop)

updateObjectAtPath(objectToValidate, path, value);
_.set(objectToValidate, path, value)

const handleAsyncParse = async () => {
try {
const parsedValue = await zodSchema.parseAsync(objectToValidate);
_.set(valtioProxy, value, path);
Reflect.set(target, prop, value, receiver);
return true;
const parsedValue = await zodSchema.parseAsync(objectToValidate)
_.set(valtioProxy, value, path)
Reflect.set(target, prop, value, receiver)
return true
} catch (error) {
errorHandler(error);
errorHandler(error)
if (!safeParse) {
throw error;
throw error
}
return false;
return false
}
};
}

const handleSyncParse = () => {
try {
if (safeParse) {
const result = zodSchema.safeParse(objectToValidate);
const result = zodSchema.safeParse(objectToValidate)
if (result.success) {
valtioProxy[prop] = value;
Reflect.set(target, prop, value, receiver);
return true;
valtioProxy[prop] = value
Reflect.set(target, prop, value, receiver)
return true
} else {
errorHandler(result.error);
return false;
errorHandler(result.error)
return false
}
} else {
const parsedValue = zodSchema.parse(objectToValidate);
Reflect.set(target, prop, value, receiver);
valtioProxy[prop] = value;
return true;
const parsedValue = zodSchema.parse(objectToValidate)
Reflect.set(target, prop, value, receiver)
valtioProxy[prop] = value
return true
}
} catch (error) {
errorHandler(error);
errorHandler(error)
if (!safeParse) {
throw error;
throw error
}
return false;
return false
}
};
}

if (parseAsync) {
handleAsyncParse().catch((error) => {
errorHandler(error);
errorHandler(error)
if (!safeParse) {
throw error;
throw error
}
});
return true;
})
return true
} else {
return handleSyncParse();
return handleSyncParse()
}
},
});
};
}
})
}

const store = createProxy(valtioProxy);
store[valtioStoreSymbol] = valtioProxy;
const store = createProxy(valtioProxy)
store[valtioStoreSymbol] = valtioProxy

return store;
};
return { proxy };
};
return store
}
return { proxy }
}
Loading

0 comments on commit ad1b1c2

Please sign in to comment.