UNPKG

sveltekit-superforms

Version:

Making SvelteKit forms a pleasure to use!

148 lines (147 loc) 5.5 kB
import { traversePath } from './traversal.js'; import { fail as kitFail } from '@sveltejs/kit'; import {} from './adapters/adapters.js'; import { parseRequest } from './formData.js'; import { splitPath } from './stringPath.js'; import { mapErrors, mergeDefaults, replaceInvalidDefaults } from './errors.js'; /** * Validates a schema for data validation and usage in superForm. * @param data Data corresponding to a schema, or RequestEvent/FormData/URL. If falsy, the schema's default values will be used. * @param schema The schema to validate against. */ export async function superValidate(data, adapter, options) { if (data && 'superFormValidationLibrary' in data) { options = adapter; adapter = data; data = undefined; } const validator = adapter; const defaults = options?.defaults ?? validator.defaults; const jsonSchema = validator.jsonSchema; const parsed = await parseRequest(data, jsonSchema, options); const addErrors = options?.errors ?? (options?.strict ? true : !!parsed.data); // Merge with defaults in non-strict mode. const parsedData = options?.strict ? (parsed.data ?? {}) : mergeDefaults(parsed.data, defaults); let status; if (!!parsed.data || addErrors) { status = await /* @__PURE__ */ validator.validate(parsedData); } else { status = { success: false, issues: [] }; } const valid = status.success; const errors = valid || !addErrors ? {} : mapErrors(status.issues, validator.shape); // Final data should always have defaults, to ensure type safety //const dataWithDefaults = { ...defaults, ...(valid ? status.data : parsedData) }; const dataWithDefaults = valid ? status.data : replaceInvalidDefaults(options?.strict ? mergeDefaults(parsedData, defaults) : parsedData, defaults, jsonSchema, status.issues, options?.preprocessed); let outputData; if (jsonSchema.additionalProperties === false) { // Strip keys not belonging to schema outputData = {}; for (const key of Object.keys(jsonSchema.properties ?? {})) { if (key in dataWithDefaults) outputData[key] = dataWithDefaults[key]; } } else { outputData = dataWithDefaults; } const output = { id: parsed.id ?? options?.id ?? validator.id, valid, posted: parsed.posted, errors: errors, data: outputData }; if (!parsed.posted) { output.constraints = validator.constraints; if (Object.keys(validator.shape).length) { output.shape = validator.shape; } } return output; } ///////////////////////////////////////////////////////////////////// /** * Sends a message with a form, with an optional HTTP status code that will set * form.valid to false if status >= 400. A status lower than 400 cannot be sent. */ export function message(form, message, options) { if (options?.status && options.status >= 400) { form.valid = false; } form.message = message; const remove = options?.removeFiles !== false; const output = remove ? withFiles({ form }) : { form }; return form.valid ? output : kitFail(options?.status ?? 400, output); } export const setMessage = message; export function setError(form, path, error, options) { // Unify signatures if (error == undefined || (typeof error !== 'string' && !Array.isArray(error))) { options = error; error = path; path = ''; } if (options === undefined) options = {}; const errArr = Array.isArray(error) ? error : [error]; if (!form.errors) form.errors = {}; if (path === null || path === '') { if (!form.errors._errors) form.errors._errors = []; form.errors._errors = options.overwrite ? errArr : form.errors._errors.concat(errArr); } else { const realPath = splitPath(path); const leaf = traversePath(form.errors, realPath, ({ parent, key, value }) => { if (value === undefined) parent[key] = {}; return parent[key]; }); if (leaf) { leaf.parent[leaf.key] = Array.isArray(leaf.value) && !options.overwrite ? leaf.value.concat(errArr) : errArr; } } form.valid = false; const output = options.removeFiles === false ? { form } : withFiles({ form }); return kitFail(options.status ?? 400, output); } export function withFiles(obj) { if (typeof obj !== 'object') return obj; for (const key in obj) { const value = obj[key]; if (value instanceof File) delete obj[key]; else if (value && typeof value === 'object') withFiles(value); } return obj; } export const removeFiles = withFiles; export function fail(status, data) { function checkForm(data) { return !!data && typeof data === 'object' && 'valid' in data && 'data' in data && 'id' in data; } function checkObj(data) { if (data && typeof data === 'object') { for (const key in data) { const v = data[key]; if (checkForm(v)) { v.valid = false; removeFiles(v); } else if (v && typeof v === 'object') { checkObj(v); } } } return data; } return kitFail(status, checkObj(data)); }