@croz/nrich-form-configuration-core
Version:
Contains core utilities related to the nrich-form-configuration module
302 lines (295 loc) • 11 kB
JavaScript
import {
__async,
__objRest,
__spreadProps,
__spreadValues,
__toESM,
require_uniqBy
} from "../chunk-IX4UG67P.mjs";
// src/zod/store/form-configuration-store.ts
import { create } from "zustand";
var useZodFormConfigurationStore = create((set) => ({
zodFormConfigurations: [],
formConfigurationLoaded: false,
set: (zodFormConfigurations) => set((state) => __spreadProps(__spreadValues({}, state), {
zodFormConfigurations
})),
add: (zodFormConfigurations) => set((state) => ({
zodFormConfigurations: [...state.zodFormConfigurations, zodFormConfigurations]
})),
remove: (zodFormConfigurations) => set((state) => ({
zodFormConfigurations: state.zodFormConfigurations.filter((c) => c !== zodFormConfigurations)
})),
setFormConfigurationLoaded: (formConfigurationLoaded) => set({ formConfigurationLoaded })
}));
// src/zod/hook/use-form-configuration.ts
var useZodFormConfigurationState = () => {
const formZodConfigurations = useZodFormConfigurationStore((state) => state.zodFormConfigurations);
const formConfigurationLoaded = useZodFormConfigurationStore((state) => state.formConfigurationLoaded);
const set = useZodFormConfigurationStore((state) => state.set);
const add = useZodFormConfigurationStore((state) => state.add);
const remove = useZodFormConfigurationStore((state) => state.remove);
const setFormConfigurationLoaded = useZodFormConfigurationStore((state) => state.setFormConfigurationLoaded);
return {
formZodConfigurations,
formConfigurationLoaded,
set,
add,
remove,
setFormConfigurationLoaded
};
};
var useZodFormConfiguration = (formId) => {
const { formZodConfigurations } = useZodFormConfigurationState();
const searchedFormConfiguration = formZodConfigurations.find((formZodConfiguration) => formZodConfiguration.formId === formId);
if (!searchedFormConfiguration) {
return void 0;
}
return searchedFormConfiguration.zodSchema;
};
// src/zod/component/FormConfigurationZodProvider.tsx
import React, { useEffect } from "react";
// src/zod/loader/fetch-form-configurations.ts
var import_uniqBy = __toESM(require_uniqBy());
// src/zod/converter/FormConfigurationValidationZodConverter.ts
import { z } from "zod";
var _FormConfigurationValidationZodConverter = class {
constructor(additionalConverters = []) {
this.additionalConverters = additionalConverters;
}
/**
* Converts {@link FormConfiguration} to Zod's schema using builtin and provided converters.
* @param formConfig Form configuration to convert
*/
convertFormConfigurationToZodSchema(formConfig) {
const shape = {};
formConfig.constrainedPropertyConfigurationList.forEach(
(property) => {
let fieldSchema = _FormConfigurationValidationZodConverter.getBaseSchema(property);
let isRequired = false;
let lastRequiredValidator;
property.validatorList.forEach(
(validator) => {
if (["NotNull", "NotBlank", "NotEmpty"].includes(validator.name)) {
isRequired = true;
lastRequiredValidator = validator;
}
fieldSchema = this.applyConverter(validator, fieldSchema);
}
);
if (!isRequired) {
fieldSchema = fieldSchema.nullish();
} else {
fieldSchema = fieldSchema.nullable().or(z.undefined()).refine(
(val) => val != null,
{ message: lastRequiredValidator.errorMessage }
);
}
_FormConfigurationValidationZodConverter.setNestedField(shape, property.path.split("."), fieldSchema);
}
);
return _FormConfigurationValidationZodConverter.buildZodObject(shape);
}
/**
* Determines the base Zod schema for a constrained property based on
* its declared JavaScript type.
*/
static getBaseSchema(property) {
switch (property.javascriptType.toLowerCase()) {
case "string":
return z.string();
case "number":
return z.number();
case "boolean":
return z.boolean();
case "object":
return z.record(z.string(), z.any());
case "array":
return z.array(z.any());
case "date":
return z.date();
default:
return z.unknown();
}
}
/**
* Assigns a Zod schema to a nested path inside a JS object that represents the shape of the final Zod object.
*/
static setNestedField(shape, path, schema) {
let current = shape;
for (let i = 0; i < path.length - 1; i += 1) {
const key = path[i];
if (!(key in current)) {
current[key] = {};
}
current = current[key];
}
current[path[path.length - 1]] = schema;
}
/**
* Recursively converts a nested shape object into a real ZodObject.
*/
static buildZodObject(shape) {
const zodShape = {};
Object.entries(shape).forEach(([key, value]) => {
if (value instanceof z.ZodType) {
zodShape[key] = value;
} else {
zodShape[key] = _FormConfigurationValidationZodConverter.buildZodObject(value);
}
});
return z.object(zodShape);
}
applyConverter(config, schema) {
const converter = this.resolveConverter(config);
if (converter) {
try {
return converter.convert(config, schema);
} catch (e) {
}
}
return schema;
}
resolveConverter(config) {
const allConverters = this.additionalConverters.concat(_FormConfigurationValidationZodConverter.DEFAULT_CONVERTERS_LIST);
return allConverters.find((additionalConverter) => additionalConverter.supports(config));
}
};
var FormConfigurationValidationZodConverter = _FormConfigurationValidationZodConverter;
FormConfigurationValidationZodConverter.DEFAULT_CONVERTERS_LIST = [
{
supports: (configuration) => ["NotNull", "NotBlank", "NotEmpty"].includes(configuration.name),
convert: (configuration, schema) => {
if (configuration.name === "NotBlank" && schema instanceof z.ZodString) {
return schema.refine(
(val) => val == null || typeof val === "string" && val.trim().length > 0,
{ message: configuration.errorMessage }
);
}
if (configuration.name === "NotEmpty") {
if (schema instanceof z.ZodString) {
return schema.refine(
(val) => val == null || typeof val === "string" && val.length > 0,
{ message: configuration.errorMessage }
);
}
if (schema instanceof z.ZodArray) {
return schema.refine(
(val) => val == null || val.length > 0,
{ message: configuration.errorMessage }
);
}
}
return schema;
}
},
{
supports: (configuration) => ["Size", "Length"].includes(configuration.name),
convert: (configuration, schema) => {
if (schema instanceof z.ZodString || schema instanceof z.ZodArray) {
let s = schema;
if (configuration.argumentMap.min != null)
s = s.min(configuration.argumentMap.min, { message: configuration.errorMessage });
if (configuration.argumentMap.max != null)
s = s.max(configuration.argumentMap.max, { message: configuration.errorMessage });
return s;
}
return schema;
}
},
{
supports: (configuration) => configuration.name === "Pattern",
convert: (configuration, schema) => {
if (!(schema instanceof z.ZodString)) {
return schema;
}
const patternStr = configuration.argumentMap.pattern;
if (typeof patternStr !== "string" || patternStr.trim() === "") {
return schema;
}
let regex;
try {
regex = new RegExp(patternStr);
} catch (e) {
return schema;
}
return schema.regex(regex, { message: configuration.errorMessage });
}
},
{
supports: (configuration) => ["Min", "Max"].includes(configuration.name),
convert: (configuration, schema) => {
if (!(schema instanceof z.ZodNumber) || configuration.argumentMap.value == null)
return schema;
const value = configuration.argumentMap.value;
return configuration.name === "Min" ? schema.min(value, { message: configuration.errorMessage }) : schema.max(value, { message: configuration.errorMessage });
}
},
{
supports: (configuration) => configuration.name === "InList",
convert: (config, schema) => {
const values = config.argumentMap.value;
if (Array.isArray(values)) {
return schema.refine(
(val) => values.includes(val),
{ message: config.errorMessage }
);
}
return schema;
}
},
{
supports: (configuration) => configuration.name === "Email",
convert: (configuration, schema) => {
if (!(schema instanceof z.ZodString)) {
return schema;
}
return z.email(configuration.errorMessage);
}
},
{
supports: () => true,
convert: (_, schema) => schema
}
];
// src/zod/loader/fetch-form-configurations.ts
var mergeZodFormConfigurationsWithoutDuplicates = (oldFormConfiguration, newFormConfiguration) => {
const mergedZodFormConfigurations = [...oldFormConfiguration, ...newFormConfiguration];
return (0, import_uniqBy.default)(mergedZodFormConfigurations, "formId");
};
var fetchZodFormConfigurations = (_0) => __async(void 0, [_0], function* ({ url, requestOptionsResolver, additionalValidatorConverters }) {
const formConfigurationValidationConverter = new FormConfigurationValidationZodConverter(additionalValidatorConverters);
const additionalOptions = (requestOptionsResolver == null ? void 0 : requestOptionsResolver()) || {};
const finalUrl = url || "/nrich/form/configuration";
const response = yield fetch(`${finalUrl}/fetch-all`, __spreadValues({
method: "POST"
}, additionalOptions));
const body = yield response.json();
const zodFormConfigurations = [];
if (response.ok) {
body.forEach((item) => {
zodFormConfigurations.push({
formId: item.formId,
zodSchema: formConfigurationValidationConverter.convertFormConfigurationToZodSchema(item)
});
});
useZodFormConfigurationStore.getState().set(mergeZodFormConfigurationsWithoutDuplicates(useZodFormConfigurationStore.getState().zodFormConfigurations, zodFormConfigurations));
useZodFormConfigurationStore.getState().setFormConfigurationLoaded(true);
}
return body;
});
// src/zod/component/FormConfigurationZodProvider.tsx
var FormConfigurationZodProvider = (_a) => {
var _b = _a, { children, loader } = _b, fetchProps = __objRest(_b, ["children", "loader"]);
useEffect(() => {
fetchZodFormConfigurations(fetchProps);
}, []);
const { formConfigurationLoaded } = useZodFormConfigurationState();
return /* @__PURE__ */ React.createElement("div", null, formConfigurationLoaded ? children : loader != null ? loader : null);
};
export {
FormConfigurationZodProvider,
useZodFormConfiguration,
useZodFormConfigurationState
};
//# sourceMappingURL=index.mjs.map