Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Server Actions example leaks backend code #710

Open
benjavicente opened this issue May 17, 2024 · 1 comment
Open

Server Actions example leaks backend code #710

benjavicente opened this issue May 17, 2024 · 1 comment

Comments

@benjavicente
Copy link

Describe the bug

onServerValidate is shared between server and browser. Using the example given by the docs:

import { createFormFactory } from "@tanstack/react-form";

export const formFactory = createFormFactory({
  defaultValues: {
    firstName: "",
    age: 0,
  },
  onServerValidate({ value }) {
    if (value.age < 12) {
      return "Server validation: You must be at least 12 to sign up";
    }
  },
});

https://tanstack.com/form/latest/docs/framework/react/guides/ssr

Note that it is mentioned that

Much like the useForm hook, createFormFactory allows you to pass defaultValues and other configuration options. A key feature here is the ability to include a property called onServerValidate. This special validation, in contrast to client-side validations, is executed exclusively on the server when a server action is triggered.

If onServerValidate is only executed in the server, it should not be part of the JS client bundle.

Your minimal, reproducible example

https://github.com/benjavicente/tanstack-react-form-actions

Steps to reproduce

Clone the repository, build, start the server, and search for .age in JS delivered to the browser.

Expected behavior

It doesn't.

How often does this bug happen?

None

Screenshots or Videos

This is part of the JS received by the browser. Search .age<12.

;(self.webpackChunk_N_E=self.webpackChunk_N_E||[]).push([[931],{2703:function(t,e,i){Promise.resolve().then(i.bind(i,3800)),Promise.resolve().then(i.bind(i,9007))},8588:function(t,e,i){"use strict";Object.defineProperty(e,"$",{enumerable:!0,get:function(){return r}});let s=i(1264);function r(t){let{createServerReference:e}=i(2366);return e(t,s.callServer)}},2328:function(t,e,i){"use strict";t.exports=i(4986)},8397:function(t,e,i){"use strict";/** * @license React * use-sync-external-store-shim.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var s=i(4986),r="function"==typeof Object.is?Object.is:function(t,e){return t===e&&(0!==t||1/t==1/e)||t!=t&&e!=e},a=s.useState,n=s.useEffect,l=s.useLayoutEffect,o=s.useDebugValue;function u(t){var e=t.getSnapshot;t=t.value;try{var i=e();return!r(t,i)}catch(t){return!0}}var h="undefined"==typeof window||void 0===window.document||void 0===window.document.createElement?function(t,e){return e()}:function(t,e){var i=e(),s=a({inst:{value:i,getSnapshot:e}}),r=s[0].inst,h=s[1];return l(function(){r.value=i,r.getSnapshot=e,u(r)&&h({inst:r})},[t,i,e]),n(function(){return u(r)&&h({inst:r}),t(function(){u(r)&&h({inst:r})})},[t]),o(i),i};e.useSyncExternalStore=void 0!==s.useSyncExternalStore?s.useSyncExternalStore:h},4473:function(t,e,i){"use strict";/** * @license React * use-sync-external-store-shim/with-selector.production.min.js * * Copyright (c) Facebook, Inc. and its affiliates. * * This source code is licensed under the MIT license found in the * LICENSE file in the root directory of this source tree. */var s=i(4986),r=i(1656),a="function"==typeof Object.is?Object.is:function(t,e){return t===e&&(0!==t||1/t==1/e)||t!=t&&e!=e},n=r.useSyncExternalStore,l=s.useRef,o=s.useEffect,u=s.useMemo,h=s.useDebugValue;e.useSyncExternalStoreWithSelector=function(t,e,i,s,r){var d=l(null);if(null===d.current){var c={hasValue:!1,value:null};d.current=c}else c=d.current;var f=n(t,(d=u(function(){function t(t){if(!o){if(o=!0,n=t,t=s(t),void 0!==r&&c.hasValue){var e=c.value;if(r(e,t))return l=e}return l=t}if(e=l,a(n,t))return e;var i=s(t);return void 0!==r&&r(e,i)?e:(n=t,l=i)}var n,l,o=!1,u=void 0===i?null:i;return[function(){return t(e())},null===u?void 0:function(){return t(u())}]},[e,i,s,r]))[0],d[1]);return o(function(){c.hasValue=!0,c.value=f},[f]),h(f),f}},1656:function(t,e,i){"use strict";t.exports=i(8397)},2432:function(t,e,i){"use strict";t.exports=i(4473)},3800:function(t,e,i){"use strict";i.d(e,{ShowScriptContent:function(){return r}});var s=i(2984);function r(){return(0,s.jsx)("code",{ref:t=>{if(console.log("element",t),t)for(let e of Array.from(document.querySelectorAll("script")))e.src&&fetch(e.src).then(t=>t.text()).then(e=>{if(e.includes("Server validation:")){var i,s;t.textContent=e;let r="Server validation: You must be at least 12 to sign up",a=e.indexOf(r),n=t.childNodes[0],l=new Range;l.setStart(n,a),l.setEnd(n,a+r.length);let o=new Highlight(l);CSS.highlights.set("danger",o);let u=document.getSelection();null==u||u.setBaseAndExtent(n,a,n,a+r.length),null==u||null===(s=u.focusNode)||void 0===s||null===(i=s.parentElement)||void 0===i||i.scrollIntoView()}})}})}},9007:function(t,e,i){"use strict";let s;i.d(e,{Form:function(){return q}});var r,a=i(9886),n=i(2984),l=i(2328),o=i(2432);function u(t,e=t=>t){return(0,o.useSyncExternalStoreWithSelector)(t.subscribe,()=>t.state,()=>t.state,e,h)}function h(t,e){if(Object.is(t,e))return!0;if("object"!=typeof t||null===t||"object"!=typeof e||null===e)return!1;let i=Object.keys(t);if(i.length!==Object.keys(e).length)return!1;for(let s=0;s<i.length;s++)if(!Object.prototype.hasOwnProperty.call(e,i[s])||!Object.is(t[i[s]],e[i[s]]))return!1;return!0}class d{constructor(t,e){this.listeners=new Set,this._batching=!1,this._flushing=0,this.subscribe=t=>{var e,i;this.listeners.add(t);let s=null==(i=null==(e=this.options)?void 0:e.onSubscribe)?void 0:i.call(e,t,this);return()=>{this.listeners.delete(t),null==s||s()}},this.setState=t=>{var e,i,s;let r=this.state;this.state=(null==(e=this.options)?void 0:e.updateFn)?this.options.updateFn(r)(t):t(r),null==(s=null==(i=this.options)?void 0:i.onUpdate)||s.call(i),this._flush()},this._flush=()=>{if(this._batching)return;let t=++this._flushing;this.listeners.forEach(e=>{this._flushing===t&&e()})},this.batch=t=>{if(this._batching)return t();this._batching=!0,t(),this._batching=!1,this._flush()},this.state=t,this.options=e}}function c(t,e){return"function"==typeof t?t(e):t}function f(t,e){return V(e).reduce((t,e)=>{if(void 0!==t)return t[e]},t)}function v(t,e,i){let s=V(e);return function t(e){if(!s.length)return c(i,e);let r=s.shift();if("string"==typeof r)return"object"==typeof e?{...e,[r]:t(e[r])}:{[r]:t()};if(Array.isArray(e)&&void 0!==r){let i=e.slice(0,r);return[...i.length?i:Array(r),t(e[r]),...e.slice(r+1)]}return[...Array(r),t()]}(t)}let p=/^(\d*)$/gm,m=/\.(\d*)\./gm,g=/^(\d*)\./gm,b=/\.(\d*$)/gm,y=/\.{2,}/gm,S="__int__",M=`${S}$1`;function V(t){if("string"!=typeof t)throw Error("Path must be a string.");return t.replaceAll("[",".").replaceAll("]","").replace(p,M).replace(m,`.${M}.`).replace(g,`${M}.`).replace(b,`.${M}`).replace(y,".").split(".").map(t=>0===t.indexOf(S)?parseInt(t.substring(S.length),10):t)}function F(t,e){let{asyncDebounceMs:i}=e,{onChangeAsync:s,onBlurAsync:r,onSubmitAsync:a,onBlurAsyncDebounceMs:n,onChangeAsyncDebounceMs:l}=e.validators||{},o=i??0,u={cause:"change",validate:s,debounceMs:l??o},h={cause:"blur",validate:r,debounceMs:n??o},d=t=>({...t,debounceMs:0});switch(t){case"submit":return[d(u),d(h),{cause:"submit",validate:a,debounceMs:0}];case"blur":return[h];case"change":return[u];default:return[]}}function A(t,e){let{onChange:i,onBlur:s,onSubmit:r}=e.validators||{},a={cause:"change",validate:i},n={cause:"blur",validate:s},l={cause:"server",validate:()=>void 0};switch(t){case"submit":return[a,n,{cause:"submit",validate:r},l];case"server":return[l];case"blur":return[n,l];default:return[a,l]}}class w{constructor(t){this.options={},this.mount=()=>{this.getInfo().instance=this;let t=this.form.store.subscribe(()=>{this.store.batch(()=>{let t=this.getValue(),e=this.getMeta();t!==this.state.value&&this.store.setState(e=>({...e,value:t})),e!==this.state.meta&&this.store.setState(t=>({...t,meta:e}))})});this.update(this.options);let{onMount:e}=this.options.validators||{};if(e){let t=this.runValidator({validate:e,value:{value:this.state.value,fieldApi:this},type:"validate"});t&&this.setMeta(e=>({...e,errorMap:{...null==e?void 0:e.errorMap,onMount:t}}))}return()=>{let e=this.options.preserveValue;t(),e||this.form.deleteField(this.name)}},this.update=t=>{if(void 0===this.state.value){let e=f(t.form.options.defaultValues,t.name);void 0!==t.defaultValue?this.setValue(t.defaultValue):void 0!==e&&this.setValue(e)}void 0===this._getMeta()&&this.setMeta(this.state.meta),this.options=t},this.getValue=()=>this.form.getFieldValue(this.name),this.setValue=(t,e)=>{this.form.setFieldValue(this.name,t,e),this.validate("change")},this._getMeta=()=>this.form.getFieldMeta(this.name),this.getMeta=()=>this._getMeta()??{isValidating:!1,isTouched:!1,isDirty:!1,isPristine:!0,touchedErrors:[],errors:[],errorMap:{},...this.options.defaultMeta},this.setMeta=t=>this.form.setFieldMeta(this.name,t),this.getInfo=()=>this.form.getFieldInfo(this.name),this.pushValue=t=>this.form.pushFieldValue(this.name,t),this.insertValue=(t,e)=>this.form.insertFieldValue(this.name,t,e),this.removeValue=t=>this.form.removeFieldValue(this.name,t),this.swapValues=(t,e)=>this.form.swapFieldValues(this.name,t,e),this.getLinkedFields=t=>{let e=Object.values(this.form.fieldInfo),i=[];for(let s of e){if(!s.instance)continue;let{onChangeListenTo:e,onBlurListenTo:r}=s.instance.options.validators||{};"change"===t&&(null==e?void 0:e.includes(this.name))&&i.push(s.instance),"blur"===t&&(null==r?void 0:r.includes(this.name))&&i.push(s.instance)}return i},this.moveValue=(t,e)=>this.form.moveFieldValues(this.name,t,e),this.validateSync=t=>{let e=A(t,this.options),i=this.getLinkedFields(t).reduce((e,i)=>{let s=A(t,i.options);return s.forEach(t=>{t.field=i}),e.concat(s)},[]),s=!1;this.form.store.batch(()=>{let t=(t,e)=>{let i=$(t.runValidator({validate:e.validate,value:{value:t.getValue(),fieldApi:t},type:"validate"})),r=j(e.cause);t.state.meta.errorMap[r]!==i&&t.setMeta(t=>({...t,errorMap:{...t.errorMap,[j(e.cause)]:i}})),i&&(s=!0)};for(let i of e)i.validate&&t(this,i);for(let e of i)e.validate&&t(e.field,e)});let r=j("submit");return this.state.meta.errorMap[r]&&"submit"!==t&&!s&&this.setMeta(t=>({...t,errorMap:{...t.errorMap,[r]:void 0}})),{hasErrored:s}},this.validateAsync=async t=>{let e=F(t,this.options),i=this.getLinkedFields(t),s=i.reduce((e,i)=>{let s=F(t,i.options);return s.forEach(t=>{t.field=i}),e.concat(s)},[]);for(let t of(this.state.meta.isValidating||this.setMeta(t=>({...t,isValidating:!0})),i))t.setMeta(t=>({...t,isValidating:!0}));let r=[],a=[],n=(e,i,s)=>{let r=j(i.cause),a=e.getInfo().validationMetaMap[r];null==a||a.lastAbortController.abort();let n=new AbortController;this.getInfo().validationMetaMap[r]={lastAbortController:n},s.push(new Promise(async s=>{let r;try{r=await new Promise((t,s)=>{setTimeout(async()=>{if(n.signal.aborted)return t(void 0);try{t(await this.runValidator({validate:i.validate,value:{value:e.getValue(),fieldApi:e,signal:n.signal},type:"validateAsync"}))}catch(t){s(t)}},i.debounceMs)})}catch(t){r=t}let a=$(r);e.setMeta(e=>({...e,errorMap:{...null==e?void 0:e.errorMap,[j(t)]:a}})),s(a)}))};for(let t of e)t.validate&&n(this,t,r);for(let t of s)t.validate&&n(t.field,t,a);let l=[];for(let t of((r.length||a.length)&&(l=await Promise.all(r),await Promise.all(a)),this.setMeta(t=>({...t,isValidating:!1})),i))t.setMeta(t=>({...t,isValidating:!1}));return l.filter(Boolean)},this.validate=t=>{if(!this.state.meta.isTouched)return[];try{this.form.validate(t)}catch(t){}let{hasErrored:e}=this.validateSync(t);return e&&!this.options.asyncAlways?this.state.meta.errors:this.validateAsync(t)},this.handleChange=t=>{this.setValue(t,{touch:!0})},this.handleBlur=()=>{this.state.meta.isTouched||(this.setMeta(t=>({...t,isTouched:!0})),this.validate("change")),this.validate("blur")},this.form=t.form,this.name=t.name,void 0!==t.defaultValue&&this.form.setFieldValue(this.name,t.defaultValue),this.store=new d({value:this.getValue(),meta:this._getMeta()??{isValidating:!1,isTouched:!1,isDirty:!1,isPristine:!0,touchedErrors:[],errors:[],errorMap:{},...t.defaultMeta}},{onUpdate:()=>{let t=this.store.state;t.meta.errors=Object.values(t.meta.errorMap).filter(t=>void 0!==t),t.meta.touchedErrors=t.meta.isTouched?t.meta.errors:[],t.meta.isPristine=!t.meta.isDirty,this.prevState=t,this.state=t}}),this.state=this.store.state,this.prevState=this.state,this.options=t}runValidator(t){for(let e of[this.form.options.validatorAdapter,this.options.validatorAdapter])if(e&&"function"!=typeof t.validate)return e()[t.type](t.value,t.validate);return t.validate(t.value)}}function $(t){if(t)return"string"!=typeof t?"Invalid Form Values":t}function j(t){switch(t){case"submit":return"onSubmit";case"blur":return"onBlur";case"mount":return"onMount";case"server":return"onServer";default:return"onChange"}}let E="undefined"!=typeof window?l.useLayoutEffect:l.useEffect;function T(t){let[e]=(0,l.useState)(()=>{let e=new w({...t,form:t.form,name:t.name});return e.Field=O,e});return E(e.mount,[e]),E(()=>{e.update(t)}),u(e.store,"array"===t.mode?t=>[t.meta,Object.keys(t.value).length]:void 0),e}function O({children:t,...e}){let i=T(e);return(0,n.jsx)(n.Fragment,{children:c(t,i)})}function _(t){return{values:t.values??{},errors:t.errors??[],errorMap:t.errorMap??{},fieldMeta:t.fieldMeta??{},canSubmit:t.canSubmit??!0,isFieldsValid:t.isFieldsValid??!1,isFieldsValidating:t.isFieldsValidating??!1,isFormValid:t.isFormValid??!1,isFormValidating:t.isFormValidating??!1,isSubmitted:t.isSubmitted??!1,isSubmitting:t.isSubmitting??!1,isTouched:t.isTouched??!1,isPristine:t.isPristine??!0,isDirty:t.isDirty??!1,isValid:t.isValid??!1,isValidating:t.isValidating??!1,submissionAttempts:t.submissionAttempts??0,validationMetaMap:t.validationMetaMap??{onChange:void 0,onBlur:void 0,onSubmit:void 0,onMount:void 0,onServer:void 0}}}class P{constructor(t){var e;this.options={},this.fieldInfo={},this.prevTransformArray=[],this.mount=()=>{let{onMount:t}=this.options.validators||{};if(!t)return;let e=this.runValidator({validate:t,value:{value:this.state.values,formApi:this},type:"validate"});e&&this.store.setState(t=>({...t,errorMap:{...t.errorMap,onMount:e}}))},this.update=t=>{if(!t)return;let e=this.options;this.options=t,this.store.batch(()=>{let i=t.defaultValues&&t.defaultValues!==e.defaultValues&&!this.state.isTouched,s=t.defaultState!==e.defaultState&&!this.state.isTouched;this.store.setState(()=>_(Object.assign({},this.state,s?t.defaultState:{},i?{values:t.defaultValues}:{})))})},this.reset=()=>{let{fieldMeta:t}=this.state,e=this.resetFieldMeta(t);this.store.setState(()=>{var t;return _({...this.options.defaultState,values:this.options.defaultValues??(null==(t=this.options.defaultState)?void 0:t.values),fieldMeta:e})})},this.validateAllFields=async t=>{let e=[];return this.store.batch(()=>{Object.values(this.fieldInfo).forEach(i=>{if(!i.instance)return;let s=i.instance;e.push(Promise.resolve().then(()=>s.validate(t))),i.instance.state.meta.isTouched||i.instance.setMeta(t=>({...t,isTouched:!0}))})}),(await Promise.all(e)).flat()},this.validateSync=t=>{let e=A(t,this.options),i=!1;this.store.batch(()=>{for(let t of e){if(!t.validate)continue;let e=I(this.runValidator({validate:t.validate,value:{value:this.state.values,formApi:this},type:"validate"})),s=x(t.cause);this.state.errorMap[s]!==e&&this.store.setState(t=>({...t,errorMap:{...t.errorMap,[s]:e}})),e&&(i=!0)}});let s=x("submit");return this.state.errorMap[s]&&"submit"!==t&&!i&&this.store.setState(t=>({...t,errorMap:{...t.errorMap,[s]:void 0}})),{hasErrored:i}},this.validateAsync=async t=>{let e=F(t,this.options);this.state.isFormValidating||this.store.setState(t=>({...t,isFormValidating:!0}));let i=[];for(let s of e){if(!s.validate)continue;let e=x(s.cause),r=this.state.validationMetaMap[e];null==r||r.lastAbortController.abort();let a=new AbortController;this.state.validationMetaMap[e]={lastAbortController:a},i.push(new Promise(async e=>{let i;try{i=await new Promise((t,e)=>{setTimeout(async()=>{if(a.signal.aborted)return t(void 0);try{t(await this.runValidator({validate:s.validate,value:{value:this.state.values,formApi:this,signal:a.signal},type:"validateAsync"}))}catch(t){e(t)}},s.debounceMs)})}catch(t){i=t}let r=I(i);this.store.setState(e=>({...e,errorMap:{...e.errorMap,[x(t)]:r}})),e(r)}))}let s=[];return i.length&&(s=await Promise.all(i)),this.store.setState(t=>({...t,isFormValidating:!1})),s.filter(Boolean)},this.validate=t=>{let{hasErrored:e}=this.validateSync(t);return e&&!this.options.asyncAlways?this.state.errors:this.validateAsync(t)},this.handleSubmit=async()=>{var t,e,i,s,r,a;if(this.store.setState(t=>({...t,isSubmitted:!1,submissionAttempts:t.submissionAttempts+1})),!this.state.canSubmit)return;this.store.setState(t=>({...t,isSubmitting:!0}));let n=()=>{this.store.setState(t=>({...t,isSubmitting:!1}))};if(await this.validateAllFields("submit"),!this.state.isFieldsValid){n(),null==(e=(t=this.options).onSubmitInvalid)||e.call(t,{value:this.state.values,formApi:this});return}if(await this.validate("submit"),!this.state.isValid){n(),null==(s=(i=this.options).onSubmitInvalid)||s.call(i,{value:this.state.values,formApi:this});return}try{await (null==(a=(r=this.options).onSubmit)?void 0:a.call(r,{value:this.state.values,formApi:this})),this.store.batch(()=>{this.store.setState(t=>({...t,isSubmitted:!0})),n()})}catch(t){throw n(),t}},this.getFieldValue=t=>f(this.state.values,t),this.getFieldMeta=t=>this.state.fieldMeta[t],this.getFieldInfo=t=>{var e;return(e=this.fieldInfo)[t]||(e[t]={instance:null,validationMetaMap:{onChange:void 0,onBlur:void 0,onSubmit:void 0,onMount:void 0,onServer:void 0}})},this.setFieldMeta=(t,e)=>{this.store.setState(i=>({...i,fieldMeta:{...i.fieldMeta,[t]:c(e,i.fieldMeta[t])}}))},this.resetFieldMeta=t=>Object.keys(t).reduce((t,e)=>(t[e]={isValidating:!1,isTouched:!1,isDirty:!1,isPristine:!0,touchedErrors:[],errors:[],errorMap:{}},t),{}),this.setFieldValue=(t,e,i)=>{let s=null==i?void 0:i.touch;this.store.batch(()=>{s&&this.setFieldMeta(t,t=>({...t,isTouched:!0,isDirty:!0})),this.store.setState(i=>({...i,values:v(i.values,t,e)}))})},this.deleteField=t=>{this.store.setState(e=>{let i={...e};return i.values=function(t,e){let i=V(e);return function t(e){if(!e)return;if(1===i.length){let t=i[0];if(Array.isArray(e)&&"number"==typeof t)return e.filter((e,i)=>i!==t);let{[t]:s,...r}=e;return r}let s=i.shift();if("string"==typeof s&&"object"==typeof e)return{...e,[s]:t(e[s])};if("number"==typeof s&&Array.isArray(e)){if(s>=e.length)return e;let i=e.slice(0,s);return[...i.length?i:Array(s),t(e[s]),...e.slice(s+1)]}throw Error("It seems we have created an infinite loop in deleteBy. ")}(t)}(i.values,t),delete i.fieldMeta[t],i}),delete this.fieldInfo[t]},this.pushFieldValue=(t,e,i)=>this.setFieldValue(t,t=>[...Array.isArray(t)?t:[],e],i),this.insertFieldValue=(t,e,i,s)=>{this.setFieldValue(t,t=>t.map((t,s)=>s===e?i:t),s)},this.removeFieldValue=(t,e,i)=>{this.setFieldValue(t,t=>t.filter((t,i)=>i!==e),i)},this.swapFieldValues=(t,e,i)=>{this.setFieldValue(t,t=>{let s=t[e],r=t[i];return v(v(t,`${e}`,r),`${i}`,s)})},this.moveFieldValues=(t,e,i)=>{this.setFieldValue(t,t=>(t.splice(i,0,t.splice(e,1)[0]),t))},this.store=new d(_({...null==t?void 0:t.defaultState,values:(null==t?void 0:t.defaultValues)??(null==(e=null==t?void 0:t.defaultState)?void 0:e.values),isFormValid:!0}),{onUpdate:()=>{var t,e;let{state:i}=this.store,s=Object.values(i.fieldMeta),r=s.some(t=>null==t?void 0:t.isValidating),a=!s.some(t=>{var e;return(null==t?void 0:t.errorMap)&&!(Array.isArray(e=Object.values(t.errorMap).filter(Boolean))&&0===e.length)}),n=s.some(t=>null==t?void 0:t.isTouched),l=s.some(t=>null==t?void 0:t.isDirty),o=r||i.isFormValidating;i.errors=Object.values(i.errorMap).filter(t=>void 0!==t);let u=0===i.errors.length,h=a&&u,d=0===i.submissionAttempts&&!n||!o&&!i.isSubmitting&&h;i={...i,isFieldsValidating:r,isFieldsValid:a,isFormValid:u,isValid:h,canSubmit:d,isTouched:n,isPristine:!l,isDirty:l},this.state=i,this.store.state=this.state;let c=(null==(t=this.options.transform)?void 0:t.deps)??[];(c.length!==this.prevTransformArray.length||c.some((t,e)=>t!==this.prevTransformArray[e]))&&(null==(e=this.options.transform)||e.fn(this),this.store.state=this.state,this.prevTransformArray=c)}}),this.state=this.store.state,this.update(t||{})}runValidator(t){let e=this.options.validatorAdapter;return e&&"function"!=typeof t.validate?e()[t.type](t.value,t.validate):t.validate(t.value)}}function I(t){if(t)return"string"!=typeof t?"Invalid Form Values":t}function x(t){switch(t){case"submit":return"onSubmit";case"blur":return"onBlur";case"mount":return"onMount";case"server":return"onServer";default:return"onChange"}}var C=/^\d+$/u,D=/^\d{4}-(?:0[1-9]|1[0-2])-(?:[12]\d|0[1-9]|3[01])$/u,k=/^\d{4}-(?:0[1-9]|1[0-2])-(?:[12]\d|0[1-9]|3[01])T(?:0\d|1\d|2[0-3]):[0-5]\d$/u,B=/^(?:0\d|1\d|2[0-3]):[0-5]\d$/u,N=/^(?:0\d|1\d|2[0-3])(?::[0-5]\d){2}$/u,L=/^\d{4}-W(?:0[1-9]|[1-4]\d|5[0-3])$/u;function Z(t){if(!t)return null;if(D.test(t))return new Date(`${t}T00:00:00.000Z`);if(k.test(t))return new Date(`${t}:00.000Z`);if(L.test(t)){let[e,i]=t.split("-W"),s=new Date(`${e}-01-01T00:00:00.000Z`);return s.setUTCDate((+i-1)*7+1),s}return new Date(B.test(t)?`1970-01-01T${t}:00.000Z`:N.test(t)?`1970-01-01T${t}.000Z`:C.test(t)?+t:t)}function U(t,e,i){return t.reduce((t,i,s)=>t[i]=t[i]||("$"===e[s+1]?[]:{}),i)}function W(t,e){let i=[];if(t.includes(".$.")){let s=(t,r)=>{let[a,...n]=t.split(".$."),l=r?`${r}.${a}`:a,o=U(l.split("."),t.split("."),e);for(let t=0;t<o.length;t++){let e=`${l}.${t}`;n.length>1?s(n.join(".$."),e):i.push(`${e}.${n[0]}`)}};s(t)}else i.push(t);return i}let R={useForm:t=>(function(t){let[e]=(0,l.useState)(()=>{let e=new P(t);return e.Field=function(t){return(0,n.jsx)(O,{...t,form:e})},e.useField=t=>T({...t,form:e}),e.useStore=t=>u(e.store,t),e.Subscribe=t=>c(t.children,u(e.store,t.selector)),e});return E(e.mount,[]),e.useStore(t=>t.isSubmitting),E(()=>{e.update(t)}),e})(Object.assign({},r,t)),useField:T,Field:O,validateFormData:(s=r={defaultValues:{firstName:"",age:0},onServerValidate(t){let{value:e}=t;if(e.age<12)return"Server validation: You must be at least 12 to sign up"}},async(t,e)=>{var i;let{validatorAdapter:r,onServerValidate:a}=s||{},n=(i={value:function(t,e){let i={};for(let[s,r]of t.entries()){let t=s.replace(/.\d+./g,".$."),a=t.split(".");s.split(".").reduce((i,s,n,l)=>{if(n<l.length-1){if(i[s])return i[s];let t=n<l.length-2?"$"===a[n+1]:e?.arrays?.includes(a.slice(0,-1).join("."));return i[s]=t?[]:{}}(!e?.files?.includes(t)||r&&("string"==typeof r||r.size))&&(i[s]=function(t,e,i){if(t?.booleans?.includes(e))return"false"!==i&&"0"!==i;if("string"==typeof i){if(t?.dates?.includes(e))return Z(i);if(t?.numbers?.includes(e))return/^-?\d*(\.\d+)?$/.test(i)?parseFloat(i):Z(i).getTime()}return i}(e,t,r))},i)}if(e?.arrays)for(let t of e.arrays)for(let e of W(t,i)){let s=e.split("."),r=s[s.length-1],a=U(s.slice(0,-1),t.split("."),i);a[r]||(a[r]=[])}if(e?.booleans)for(let t of e.booleans)for(let e of W(t,i)){let s=e.split("."),r=s[s.length-1],a=U(s.slice(0,-1),t.split("."),i);!0!==a[r]&&(a[r]=!1)}return i}(t,e)},r&&"function"!=typeof a?r().validate(i,a):a(i));return{errorMap:{onServer:n},errors:n?[n]:[]}}),initialFormState:{errorMap:{onServer:void 0},errors:[]}};i(1264);var Y=(0,i(8588).$)("def8069cb35f2a7ce5c0b74936f11003541b4c77");function q(){let[t,e]=(0,a.useFormState)(Y,R.initialFormState),{useStore:i,Subscribe:s,handleSubmit:r,Field:n}=R.useForm({transform:{fn:e=>((function t(e,i){for(let s of new Set([...Object.keys(e),...Object.keys(i)]))if(Array.isArray(e[s])&&Array.isArray(i[s]))e[s]=[...e[s],...i[s]];else if("object"==typeof e[s]&&"object"==typeof i[s])t(e[s],i[s]);else{if(!(s in i)&&void 0===i[s])continue;e[s]=i[s]}return e})(e.state,t),e),deps:[t]}});return i(t=>t.errors),null}}},function(t){t.O(0,[645,563,744],function(){return t(t.s=2703)}),_N_E=t.O()}]);;

Platform

MacOS.

TanStack Form adapter

react-form

TanStack Form version

0.19.5

TypeScript version

No response

Additional context

I would mark the SSR with Next section in the docs with a warning. Also, the form factory doesn't feel like the right primitive to share code between server and client.

A server only layer could be useful to provide a type-safe way to parse, validate and server validate the form, to then respond with an updated form or another result 🤔

@crutchcorn
Copy link
Member

crutchcorn commented May 28, 2024

Oh, yikes, that's, uh, bad. Thank you for the report.

Could you make a PR adding a warning to our docs? Something akin to:

This feature is highly experimental and likely to change API as a result of backend code leaking to the frontend. We're currently investigating and will update this page as it comes.

As for form factory not being a good primitive to share code, what API would you alternatively propose? Keep in mind that it should/must infer the types of everything properly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants