@bolttech/form-engine-core 1.1.0-beta.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lite.esm.js ADDED
@@ -0,0 +1 @@
1
+ import{BehaviorSubject as e,Subject as t,Subscription as i,groupBy as s,mergeMap as a,debounceTime as n,filter as r,combineLatest as l,startWith as o,map as u,distinctUntilKeyChanged as d}from"rxjs";var h;function c(e,t,i,s){return new(i||(i=Promise))(function(a,n){function r(e){try{o(s.next(e))}catch(e){n(e)}}function l(e){try{o(s.throw(e))}catch(e){n(e)}}function o(e){var t;e.done?a(e.value):(t=e.value,t instanceof i?t:new i(function(e){e(t)})).then(r,l)}o((s=s.apply(e,t||[])).next())})}!function(e){e.ON_VALUE="value",e.ON_PROPS="props",e.ON_VISIBILITY="visibility",e.ON_API="api",e.ON_IVARS="iVars",e.ON_FIELDS="fields"}(h||(h={})),"function"==typeof SuppressedError&&SuppressedError;const v=1e3,f=/^\$\{(?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*\}$/,p=/\$\{((?:[^{}]|\{(?:[^{}]|\{[^{}]*\})*\})*)\}/g,m=/\s*(\|\||&&|!+)\s*/g,b=/^\|\||&&|!+$/,S=["fields","iVars","form"],y=["api","apiSchema","props","validations","visibilityConditions","resetValues"],g=!1;function V(e){const t=p,i=[];let s;for(;null!==(s=t.exec(e));)i.push(s[1]);const a=m,n=i.map(e=>e.split(a).filter(e=>!a.test(e))).flat().filter(e=>e.split(".").length>1);return{originScopeKeys:Array.from(new Set(n.map(e=>e.split(".")[0]))),originFieldKeys:Array.from(new Set(n.map(e=>e.split(".")[1]))),originPropertyKeys:Array.from(new Set(n.map(e=>e.split(".")[2])))}}function $(e,t){const i=[];if(Array.isArray(e))e.forEach((e,s)=>{i.push(...$(e,`${t?`${t}.`:""}${s}`))});else if("object"==typeof e)for(const s in e)Object.prototype.hasOwnProperty.call(e,s)&&i.push(...$(e[s],`${t?`${t}.`:""}${s}`));else if("string"==typeof e&&e.includes("${")){const s=(t||"").split(".");V(e).originScopeKeys.every(e=>S.includes(e))?i.push(Object.assign(Object.assign({originExpression:e},V(e)),{destinationKey:s[0],destinationProperty:s[1],destinationPath:s.slice(2)})):console.warn(`scope malformed on this expression: ${e}, ignoring..`)}return i}const j={},E={},O={};function I(e){Object.assign(j,e)}function x(e){Object.assign(E,e)}function _(e){Object.assign(O,e)}function k(e){if(null===e||"object"!=typeof e)return e;if(e instanceof Date)return new Date(e.getTime());if(Array.isArray(e))return e.map(e=>k(e));const t={};for(const i in e)Object.prototype.hasOwnProperty.call(e,i)&&(t[i]=k(e[i]));return t}function F(e,t,i){if(null==e)return i;const s=Array.isArray(t)?t:t.split(".");let a=e;for(let e=0;e<s.length;e++){if(null==a)return i;a=a[s[e]]}return void 0===a?i:a}function N(e,t,i){if(null==e||"object"!=typeof e)return e;const s=Array.isArray(t)?t:t.split(".");let a=e;for(let e=0;e<s.length-1;e++){const t=s[e],i=s[e+1];null!=a[t]&&"object"==typeof a[t]||(a[t]="number"==typeof i||/^\d+$/.test(String(i))?[]:{}),a=a[t]}return a[s[s.length-1]]=i,e}function P(e,t){if(e===t)return!0;if(null==e||null==t)return e===t;if(typeof e!=typeof t)return!1;if(e instanceof Date&&t instanceof Date)return e.getTime()===t.getTime();if("object"!=typeof e)return!1;const i=Array.isArray(e);if(i!==Array.isArray(t))return!1;if(i){if(e.length!==t.length)return!1;for(let i=0;i<e.length;i++)if(!P(e[i],t[i]))return!1;return!0}const s=Object.keys(e),a=Object.keys(t);if(s.length!==a.length)return!1;for(const i of s){if(!Object.prototype.hasOwnProperty.call(t,i))return!1;if(!P(e[i],t[i]))return!1}return!0}function w(e){return null==e}class T extends t{constructor(e){super(),this.isMounted=e}next(e){this.isMounted()&&super.next(e)}}class A extends e{constructor(e){super(e),this.defaultValue=e}next(e){this.closed||super.next(e)}get value(){return this.closed?this.defaultValue:super.value}}function R(e,t,i,s,a){return function(e){return"function"==typeof e}(i[s])?i[s](e,t,a):((e,t,i)=>!!t&&function(e,t,i){const s=[];return Object.keys(t).forEach(a=>{s.push(i[a](e,{[a]:t[a]}))}),s}(e,t,i).some(e=>e))(e,t[s],i)}class M{constructor({formIndex:e,schemaComponent:t,config:s,children:a,validateVisibility:n,resetValue:r,resetProperty:l,templateSubject$:o,fieldEventSubject$:u,dataSubject$:d,fieldValidNotification$:h,mountSubject$:c,mapper:f,formValuesStateSubject$:p,submitEvent:m,visibility:b,persistValue:S}){var y,V,$,j,E,O;this.valueSubscription$=new i,this.fieldStateSubscription$=new i,this.formIndex=e,this.originalSchema=k(t),this.config={defaultAPIdebounceTimeMS:Number(null==s?void 0:s.defaultAPIdebounceTimeMS)?Number(null==s?void 0:s.defaultAPIdebounceTimeMS):v,defaultStateRefreshTimeMS:Number(null==s?void 0:s.defaultStateRefreshTimeMS)?Number(null==s?void 0:s.defaultStateRefreshTimeMS):100,defaultLogVerbose:(null==s?void 0:s.defaultLogVerbose)?s.defaultLogVerbose:g},this.name=t.name,this.nameToSubmit=t.nameToSubmit,this.component=t.component,this.children=a,this.validations=k(t.validations),this.visibilityConditions=k(t.visibilityConditions),this.resetValues=k(t.resetValues),this.resetPropertyValues=k(t.resetPropertyValues),this.apiSchema=k(t.api),this.formatters=k(t.formatters),this.masks=k(t.masks),f.valueChangeEvent&&(this.valueChangeEvent=f.valueChangeEvent),(null===(y=f.events)||void 0===y?void 0:y.setValue)&&(this.valuePropName=f.events.setValue),this.mapper=f,this.validateVisibility=n,this.resetValue=r,this.resetProperty=l,this.submitEvent=m,this.templateSubject$=o,this.fieldEventSubject$=u,this.dataSubject$=d,this.fieldValidNotification$=h,this.mountSubject$=c,this.formValuesStateSubject$=p,this.getFormValues=()=>this.formValuesStateSubject$.value,this._props=M.filterProps(k(t.props||{})),this._adapterProps=JSON.stringify(t.props||{}),this._metadata="",this.errorsString="",this.errorsList=[],this._visibility="boolean"!=typeof b||b,this._api={default:{response:(null===(j=null===($=null===(V=this.apiSchema)||void 0===V?void 0:V.defaultConfig)||void 0===$?void 0:$.config)||void 0===j?void 0:j.fallbackValue)||"",status:null},named:(null===(E=this.apiSchema)||void 0===E?void 0:E.configs)&&Object.keys(null===(O=this.apiSchema)||void 0===O?void 0:O.configs).reduce((e,t)=>{var i,s;return e[t]={response:(null===(s=null===(i=this.apiSchema)||void 0===i?void 0:i.configs)||void 0===s?void 0:s[t].config.fallbackValue)||"",status:null},e},{}),apiState:{loading:!1}},this._errors={},this._mounted=!1,this.valid=!0,this.persistValue=S,this.initializeObservers()}initializeObservers(){this.valueSubject$&&!this.valueSubject$.closed||(this.valueSubject$=new T(()=>this.mounted)),this.errorSubject$&&!this.errorSubject$.closed||(this.errorSubject$=new T(()=>this.mounted)),this.visibilitySubject$&&!this.visibilitySubject$.closed||(this.visibilitySubject$=new T(()=>this.mounted)),this.propsSubject$&&!this.propsSubject$.closed||(this.propsSubject$=new T(()=>this.mounted)),this.fieldStateSubscription$&&!this.fieldStateSubscription$.closed||(this.fieldStateSubscription$=new i),this.apiEventQueueSubject$&&!this.apiEventQueueSubject$.closed||(this.apiEventQueueSubject$=new T(()=>this.mounted)),this.apiEventQueueSubject$.observed||this.apiEventQueueSubject$.pipe(s(({event:e})=>e),a(e=>e.pipe(n(this.config.defaultAPIdebounceTimeMS))),r(()=>this.apiEventQueueSubject$&&!this.apiEventQueueSubject$.closed)).subscribe(e=>{this.apiRequest(e)})}get adapterProps(){return this._adapterProps}set adapterProps(e){const t=JSON.stringify(e||{});t!==this.adapterProps&&(this.props=M.filterProps(e),this._adapterProps=t)}get props(){return this._props}set props(e){void 0===e||P(e,this.props)||(this._props=e,this.propsSubject$.next(this.props),this.templateSubject$.next({scope:"fields",key:this.name,event:"ON_PROPS"}))}static filterProps(e){return Array.isArray(e)?e.filter(e=>"string"!=typeof e||!e.includes("${")).map(e=>M.filterProps(e)):"object"==typeof e&&null!==e?e instanceof Date?e:Object.keys(e).reduce((t,i)=>{const s=e[i];return"string"==typeof s&&s.includes("${")||(t[i]=M.filterProps(e[i])),t},{}):e}get stateValue(){return this._stateValue}get metadata(){return this._metadata}get value(){return this._value}set value(e){var t,i,s;let a;if(this.valueChangeEvent)try{a=this.valueChangeEvent(e,{props:this.props})}catch(t){a=e}else a=e;null!=a&&("object"==typeof a&&"_value"in a&&"_metadata"in a?(this._value=this.formatValue(a._value),this._stateValue=(null===(t=this.mapper.events)||void 0===t?void 0:t.setValue)?{[this.mapper.events.setValue]:this.maskValue(this.formatValue(a._value))}:{},this._metadata=a._metadata):(this._value=this.formatValue(a),this._stateValue=(null===(i=this.mapper.events)||void 0===i?void 0:i.setValue)?{[null===(s=this.mapper.events)||void 0===s?void 0:s.setValue]:this.maskValue(this.formatValue(a))}:{},this.maskValue(this.formatValue(a)),this._metadata=a),this.stateValue&&this.valueSubject$.next(this.stateValue),this.templateSubject$.next({scope:"fields",key:this.name,event:"ON_VALUE"}))}get visibility(){return this._visibility}set visibility(e){void 0!==e&&e!==this.visibility&&(this._visibility=e,this.visibilitySubject$.next(this.visibility),this.templateSubject$.next({scope:"fields",key:this.name,event:"ON_VISIBILITY"}))}set valid(e){"boolean"!=typeof e&&this.valid===e||(this._valid=e,this.triggerFieldValidNotification())}get valid(){return this._valid}triggerFieldValidNotification(){this.fieldValidNotification$.closed||this.fieldValidNotification$.next({fieldTrigger:this.name})}get errors(){return this._errors}set errors(e){var t;void 0===e||P(e,this.errors)||(this._errors=e,this.errorsList=Object.values(this.errors).filter(e=>null!=e),this.errorsString=this.errorsList.join(", "),(null===(t=this.mapper.events)||void 0===t?void 0:t.setErrorMessage)&&this.errorSubject$.next({[this.mapper.events.setErrorMessage]:this.errorsString}),this.templateSubject$.next({scope:"fields",key:this.name,event:"ON_PROPS"}))}get api(){return this._api}set api(e){void 0!==e&&(this._api=e,this.templateSubject$.next({scope:"fields",key:this.name,event:"ON_API_RESPONSE"}),this.emitEvents({event:"ON_API_FIELD_RESPONSE"}))}notifyApiRequest(){this._api.apiState.loading=!0,this.templateSubject$.next({scope:"fields",key:this.name,event:"ON_API_REQUEST"}),this.emitEvents({event:"ON_API_FIELD_REQUEST"})}get mounted(){return this._mounted}set mounted(e){void 0!==e&&e!==this.mounted&&(this._mounted=e,this.initializeObservers(),this.mountSubject$.closed||this.mountSubject$.next({key:this.name,status:this.mounted}))}mountField({valueSubscription:e,propsSubscription:t}){this.mounted=!0,this.subscribeValue(e),this.subscribeState(t),this.valueSubject$.next(this.stateValue),this.propsSubject$.next(this.props),this.visibilitySubject$.next(this.visibility),this.fieldEventSubject$.next({event:"ON_FIELD_MOUNT",fieldName:this.name,fieldInstance:this})}emitValue(e){this.visibility&&this.mounted&&(this.value=e.value,this.dataSubject$.next({event:e.event,fieldIndex:this.name,formIndex:this.formIndex}),this.emitEvents({event:e.event}))}emitEvents({event:e}){if("ON_FORM_SUBMIT"===e)return this.submitEvent();this.validateVisibility({event:e,key:this.name}),this.setFieldValidity({event:e}),this.resetValue({event:e,key:this.name}),this.resetProperty({event:e,key:this.name}),this.apiEventQueueSubject$.next({event:e}),this.fieldEventSubject$.next({event:e,fieldName:this.name,fieldInstance:this})}setFieldValidity({event:e}){var t,i,s,a;if(!this.validations||!this.visibility)return this.errors={},void(this.valid=!0);let n=!0;const r={},l=null===(t=this.validations)||void 0===t?void 0:t.methods;if(l&&Object.keys(l).forEach(e=>{var t,i;const s=R(this.value,l,j,e,null===(t=this.formValuesStateSubject$)||void 0===t?void 0:t.value);n=!s&&n,s&&(null===(i=this.validations)||void 0===i?void 0:i.messages)?e in this.validations.messages?r[e]=this.validations.messages[e]:"default"in this.validations.messages&&(r[e]=this.validations.messages.default):delete r[e]}),this.valid=n,null===(s=null===(i=this.validations)||void 0===i?void 0:i.eventMessages)||void 0===s?void 0:s[e]){const t={};null===(a=this.validations.eventMessages[e])||void 0===a||a.forEach(e=>{e in r&&(t[e]=r[e])}),this.errors=t}else"ON_FIELD_VALIDATION"===e&&(this.errors=r)}formatValue(e){return this.formatters?Object.keys(this.formatters).reduce((e,t)=>{const i=E[t];return i?i(e,this.formatters):e},e):e}maskValue(e){return this.masks?Object.keys(this.masks).reduce((e,t)=>{const i=O[t];return i?i(e,this.masks):e},e):e}checkApiRequestValidations(e){let t=!0;const i=e.preConditions;return i&&Object.keys(i).forEach(e=>{var s;const a=R(this.value,i,j,e,null===(s=this.formValuesStateSubject$)||void 0===s?void 0:s.value);t=t&&!a}),e.blockRequestWhenInvalid&&(t=t&&this.valid),t}apiRequest(e){return c(this,arguments,void 0,function*({event:e}){var t,i,s,a,n,r,l,o,u;let d=!1;const h=e=>c(this,void 0,void 0,function*(){var t,i;try{const{status:s,response:a}=yield function(e,t,i,s,a){return c(this,void 0,void 0,function*(){if(a){const[e,i]=t.split("?"),s=new URLSearchParams(i);Object.keys(a).forEach(e=>{s.append(e,a[e])}),t=`${e}?${s.toString()}`}const n=yield fetch(t,{method:e,headers:i,body:s?JSON.stringify(s):void 0}),r=yield n.text();return{status:n.status,response:r}})}(e.method,e.url,e.headers,e.body,e.queryParams),n=null===(t=e.transform)||void 0===t?void 0:t.callback,r=n?n({payload:JSON.parse(String(a)),formValues:null===(i=this.formValuesStateSubject$)||void 0===i?void 0:i.value}):JSON.parse(String(a));return{response:e.resultPath?F(r,e.resultPath):r,status:s}}catch(t){return{response:w(null==e?void 0:e.fallbackValue)?"error":e.fallbackValue,status:500}}});if("ON_API_FIELD_RESPONSE"===this.api.apiState.lastEvent&&"ON_API_FIELD_RESPONSE"===e||!(null===(i=null===(t=this.apiSchema)||void 0===t?void 0:t.defaultConfig)||void 0===i?void 0:i.events.includes(e))&&(!(null===(s=this.apiSchema)||void 0===s?void 0:s.configs)||!Object.keys(null===(a=this.apiSchema)||void 0===a?void 0:a.configs).some(t=>{var i,s;return null===(s=null===(i=this.apiSchema)||void 0===i?void 0:i.configs)||void 0===s?void 0:s[t].events.includes(e)})))return;const v={default:Object.assign({},this.api.default),named:Object.assign({},this.api.named),apiState:Object.assign({},this.api.apiState)},f=null===(n=this.apiSchema.defaultConfig)||void 0===n?void 0:n.config;if(f&&(null===(l=null===(r=this.apiSchema)||void 0===r?void 0:r.defaultConfig)||void 0===l?void 0:l.events.includes(e))&&this.checkApiRequestValidations(f)){d||this.notifyApiRequest();const{response:e,status:t}=yield h(f);d=!0,v.default={response:e,status:t}}if((null===(o=this.apiSchema)||void 0===o?void 0:o.configs)&&Object.keys(null===(u=this.apiSchema)||void 0===u?void 0:u.configs).some(t=>{var i,s;return null===(s=null===(i=this.apiSchema)||void 0===i?void 0:i.configs)||void 0===s?void 0:s[t].events.includes(e)})&&this.apiSchema.configs){const t=yield Promise.all(Object.keys(this.apiSchema.configs).map(t=>c(this,void 0,void 0,function*(){var i,s,a,n;const r=null===(s=null===(i=this.apiSchema)||void 0===i?void 0:i.configs)||void 0===s?void 0:s[t].config;if(r&&(null===(n=null===(a=this.apiSchema)||void 0===a?void 0:a.configs)||void 0===n?void 0:n[t].events.includes(e))&&this.checkApiRequestValidations(r)){d||this.notifyApiRequest();const{response:e,status:i}=yield h(r);return d=!0,{name:t,result:{response:e,status:i}}}return null})));t.forEach(e=>{e&&v.named&&(v.named[e.name]=e.result)})}d&&(v.apiState.lastEvent=e,v.apiState.loading=!1,this.api=v)})}destroyField(){this.mounted=!1,this.valueSubscription$.unsubscribe(),this.visibilitySubject$.unsubscribe(),this.fieldStateSubscription$.unsubscribe(),this.propsSubject$.unsubscribe(),this.errorSubject$.unsubscribe(),this.apiEventQueueSubject$.unsubscribe(),this.dataSubject$.closed||this.dataSubject$.next({event:"ON_FIELD_UNMOUNT",fieldIndex:this.name,formIndex:this.formIndex}),this.fieldEventSubject$.closed||this.fieldEventSubject$.next({event:"ON_FIELD_UNMOUNT",fieldName:this.name,fieldInstance:this})}subscribeState(e){var t;this.fieldStateSubscription$=l({visibility:this.visibilitySubject$.pipe(o(this.visibility)),props:this.propsSubject$.pipe(o(this.props)),errors:this.errorSubject$.pipe(o(Object.assign({},(null===(t=this.mapper.events)||void 0===t?void 0:t.setErrorMessage)&&{[this.mapper.events.setErrorMessage]:this.errorsString})))}).pipe(n(this.config.defaultStateRefreshTimeMS)).subscribe({next:e})}subscribeValue(e){this.valueSubscription$=this.valueSubject$.subscribe({next:e})}}class L{constructor(e){var s,a,n,r,l,o,u,d,h,c,f;this.templateSubscription$=new i,this.mappers=new Map,this.queuedFieldVisibilityEvents=new Map,this.queuedFieldResetValuesEvents=new Map,this.queuedFieldResetPropertyEvents=new Map,this.queuedInitialValues=new Map,this._valid=!1,this.stopEventsOnSubmit=!1,this.submitted=!1,this.isolatedFormInstance=!1,this.index=e.index,this.schema=e.schema,this.fields=new Map,this.action=e.action||(null===(s=e.schema)||void 0===s?void 0:s.action),this.method=e.method||(null===(a=e.schema)||void 0===a?void 0:a.method),this.config={defaultAPIdebounceTimeMS:Number(null===(n=e.config)||void 0===n?void 0:n.defaultAPIdebounceTimeMS)?Number(null===(r=e.config)||void 0===r?void 0:r.defaultAPIdebounceTimeMS):v,defaultStateRefreshTimeMS:Number(null===(l=e.config)||void 0===l?void 0:l.defaultStateRefreshTimeMS)?Number(null===(o=e.config)||void 0===o?void 0:o.defaultStateRefreshTimeMS):100,defaultLogVerbose:(null===(u=e.config)||void 0===u?void 0:u.defaultLogVerbose)?e.config.defaultLogVerbose:g},null===(d=e.mappers)||void 0===d||d.map(e=>{this.mappers.set(e.componentName,e)}),e.submitSubject$&&e.dataSubject$&&e.formValidSubject$||(this.isolatedFormInstance=!0,this.config.defaultLogVerbose&&console.warn(`some formGroup events are not properly instanciated, any onData, onValid, onSubmit events managed by formGroup won't trigger on form: ${this.index}`)),this.schema&&L.checkIndexes(this.schema.components),this.templateSubject$=new t,this.fieldEventSubject$=new t,this.mountSubject$=new t,this.fieldValidNotification$=new t,this.submitSubject$=e.submitSubject$?e.submitSubject$:new t,this.dataSubject$=e.dataSubject$?e.dataSubject$:new t,this.formValidSubject$=e.formValidSubject$?e.formValidSubject$:new t,this.formValuesStateSubject$=new A({erroredFields:[],isValid:!0,metadata:[],values:[]}),this.getFormValues=()=>this.formValuesStateSubject$.value,this.subscribedTemplates=[],this.templateSubscription$=this.templateSubject$.subscribe(this.refreshTemplates.bind(this)),this.fieldValidNotification$.subscribe(()=>{this.validateForm()}),this.fieldEventSubject$.subscribe(()=>{this.formValuesStateSubject$.next(this.getFormState())}),this.dataSubject$.subscribe(()=>{this.formValuesStateSubject$.next(this.getFormState())}),this.mountSubject$.subscribe(this.mountActions.bind(this)),this.prefetchedData=null===(h=e.schema)||void 0===h?void 0:h.prefetchedData,this.initialValues=e.initialValues||(null===(c=e.schema)||void 0===c?void 0:c.initialValues),this.iVars=e.iVars||(null===(f=e.schema)||void 0===f?void 0:f.iVars)||{},this.stopEventsOnSubmit=(null==e?void 0:e.stopEventsOnSubmit)||!1,this.submitted=!1}generateFields(){this.schema&&this.serializeStructure(this.schema.components),this.fields.forEach(e=>{e.mountField({valueSubscription:()=>null,propsSubscription:()=>null})})}submitChecker(){return!(this.stopEventsOnSubmit&&this.submitted)}mountActions({key:e,status:t}){if(t){const t=this.fields.get(e);if(!t)return void(this.config.defaultLogVerbose&&console.warn(`field ${e} was mounted but since it's parent has some visibility condition the field was already removed`));this.subscribeTemplates(),this.refreshTemplates({scope:"fields",event:"ON_FIELDS"}),this.templateSubject$.next({scope:"iVars",event:"ON_IVARS"}),this.queuedInitialValues.has(e)||(t.valuePropName&&!t.value?t.emitValue({event:"ON_FIELD_MOUNT",value:""}):t.emitEvents({event:"ON_FIELD_MOUNT"})),this.checkFieldEventQueues(e)}}set initialValues(e){e&&Object.keys(e).forEach(t=>{const i=this.fields.get(t);i&&(null==i?void 0:i.visibility)&&(null==i?void 0:i.mounted)?i.emitValue({value:null==e?void 0:e[t],event:"ON_FIELD_MOUNT"}):this.queuedInitialValues.set(t,null==e?void 0:e[t])})}get iVars(){return this._iVars}set iVars(e){this._iVars=e,this.templateSubject$.next({scope:"iVars",event:"ON_IVARS"})}validateForm(){if(0===this.fields.size)return this.valid=!1;for(const[,e]of this.fields)if(!e.valid)return this.valid=!1;return this.valid=!0}get valid(){return this._valid}set valid(e){this._valid!==e&&(this._valid=e,this.templateSubject$.next({event:"ON_FORM",scope:"form"}),this.formValidSubject$.next({formIndex:this.index,valid:this.valid}))}subscribeTemplates(){this.subscribedTemplates=[],this.fields.forEach(({originalSchema:{component:e,props:t,name:i,validations:s,visibilityConditions:a,resetValues:n,resetPropertyValues:r,api:l}},o)=>{$({component:e,props:t,name:i,validations:s,visibilityConditions:a,resetValues:n,resetPropertyValues:r,apiSchema:l},o).forEach(e=>this.subscribedTemplates.push(e))})}getValue({scope:e,key:t,property:i,path:s}){switch(e){case"iVars":return F(this.iVars,[t,...i?[i]:[],...s]);case"form":return F(this,[t,...i?[i]:[],...s]);case"fields":{const e=this.fields.get(t);return e?"props"===i&&s[0]===e.valuePropName?e.value:s.length>0?F(e[i],s):e[i]:this.config.defaultLogVerbose&&console.warn(`failed to get value from ${t}`)}}}setValue({key:e,property:t,path:i,originKey:s,value:a}){const n=this.fields.get(e);if(n){if(i.length>0){if("props"===t&&i[0]===n.valuePropName&&e!==s)return void n.emitValue({event:"ON_FIELD_CHANGE",value:a});const r=n[t];let l;return Array.isArray(r)||"object"==typeof r&&!w(r)?(l=k(r),N(l,i,a),void(n[t]=l)):void(this.config.defaultLogVerbose&&console.warn(`invalid template property, skipping evaluation of ${n.name} with ${r}`))}n[t]=a}else this.config.defaultLogVerbose&&console.warn(`failed to update field ${e}`)}extractParams(e){const t=function(e){const t=e.split(".");return t.length>1?this.getValue({scope:t[0],key:t[1],property:t[2],path:t.slice(3)}):t[0]}.bind(this),i=[];let s;for(;!w(s=p.exec(e));)i.push(s[1]);return i.map(e=>e.split(m)).map(e=>e.length<=1?{parse:!1,value:t(e[0])}:{parse:!0,value:e.filter(Boolean).reduce((e,i)=>{if(i.match(b))return`${e}${i}`;let s;const a=t(i);let n;try{n=JSON.parse(a)}catch(e){n=a}switch(typeof n){case"string":s=`\`${n}\``;break;case"boolean":case"undefined":case"number":default:s=n;break;case"object":if(null===n){s=null;break}if(n instanceof Date){s=`new Date(\`${n}\`)`;break}s=JSON.stringify(n)}return`${e}${s}`},"")}).map(({parse:t,value:i})=>{try{return t?new Function(`return ${i}`)():i}catch(t){return this.config.defaultLogVerbose&&(console.warn(`unhandled parsing on ${e} returning`),console.warn(i)),i}})}replaceExpression(e,t){return e.replace(p,()=>String(t.shift())||"")}hasStringConcatenation(e){return!f.test(e)}refreshTemplates({key:e,event:t}){this.subscribedTemplates.forEach(({destinationKey:i,destinationPath:s,destinationProperty:a,originExpression:n,originFieldKeys:r})=>{if(!e||r.includes(e)){const r=this.extractParams(n);let l;l=this.hasStringConcatenation(n)?this.replaceExpression(n,[...r]):null==r?void 0:r[0],P(this.getValue({scope:"fields",key:i,property:a,path:s}),l)||this.setValue({key:i,property:a,path:s,originKey:e,value:l,event:t})}})}checkFieldEventQueues(e){var t;if(null===(t=this.fields.get(e))||void 0===t?void 0:t.mounted){if(this.queuedFieldVisibilityEvents.has(e)&&(this.setFieldVisibility(Object.assign({field:e},this.queuedFieldVisibilityEvents.get(e))),this.queuedFieldVisibilityEvents.delete(e)),this.queuedInitialValues.has(e)){const t=this.queuedInitialValues.get(e);this.queuedInitialValues.delete(e),this.initialValues={[e]:t}}this.queuedFieldResetValuesEvents.has(e)&&(this.setResetFieldValue(Object.assign({key:e},this.queuedFieldResetValuesEvents.get(e))),this.queuedFieldResetValuesEvents.delete(e)),this.queuedFieldResetPropertyEvents.has(e)&&(this.setResetPathValue(Object.assign({key:e},this.queuedFieldResetPropertyEvents.get(e))),this.queuedFieldResetPropertyEvents.delete(e))}}setFieldVisibility({field:e,hasError:t,showOnlyIfTrue:i}){const s=this.fields.get(e);if(s&&s.mounted){const a=s.visibility,n=i?t:!t;if(s.visibility=n,a===n)return;s.visibility?this.queuedInitialValues.has(e)?(null==s||s.emitValue({value:this.queuedInitialValues.get(e)||"",event:"ON_FIELD_MOUNT"}),this.queuedInitialValues.delete(e)):s.emitEvents({event:"ON_FIELD_MOUNT"}):(s.emitEvents({event:"ON_FIELD_UNMOUNT"}),s.persistValue&&this.queuedInitialValues.set(s.name,s.value),s.value="",s.valid=!0)}else this.queuedFieldVisibilityEvents.set(e,{hasError:t,showOnlyIfTrue:i})}validateVisibility({event:e,key:t}){const i=this.fields.get(t),s=null==i?void 0:i.visibilityConditions;s&&(null==s?void 0:s.some(t=>t.events.includes(e)))&&s.forEach(t=>{t.events.includes(e)&&Object.keys(t.validations).forEach(e=>{const s=R(i.value,t.validations,j,e,this.formValuesStateSubject$.value);Array.isArray(t.fields)?t.fields.forEach(e=>{this.setFieldVisibility({field:e,hasError:s,showOnlyIfTrue:!(!i.value||!t.showOnlyIfTrue)})}):t.fields&&this.setFieldVisibility({field:t.fields,hasError:s,showOnlyIfTrue:!(!i.value||!t.showOnlyIfTrue)})})})}setResetFieldValue({key:e,value:t}){const i=this.fields.get(e);i&&(null==i?void 0:i.mounted)?i.emitValue({value:t,event:"ON_FIELD_CLEARED"}):this.queuedFieldResetValuesEvents.set(e,{value:t})}resetValue({event:e,key:t}){const i=this.fields.get(t),s=null==i?void 0:i.resetValues;s&&(null==s?void 0:s.some(t=>t.events.includes(e)))&&s.forEach(t=>{if(!t.events.includes(e))return;const s=()=>{Array.isArray(t.fields)?t.fields.forEach((e,i)=>{const s=Array.isArray(t.resettledValue)?t.resettledValue[i]:t.resettledValue;this.setResetFieldValue({key:e,value:s})}):t.fields&&this.setResetFieldValue({key:t.fields,value:t.resettledValue})};if(!t.validations)return s();Object.keys(t.validations).forEach(e=>{R(i.value,t.validations,j,e,this.formValuesStateSubject$.value)||s()})})}setResetPathValue({key:e,property:t,path:i,value:s}){this.fields.get(e)?this.setValue({key:e,property:t,path:i.split("."),value:s,event:"ON_RESET"}):this.queuedFieldResetPropertyEvents.set(e,{property:t,path:i,value:s})}resetProperty({event:e,key:t}){const i=this.fields.get(t),s=null==i?void 0:i.resetPropertyValues;s&&(null==s?void 0:s.some(t=>t.events.includes(e)))&&s.forEach(t=>{if(t.events.includes(e)&&y.includes(t.property))return t.validations?void Object.keys(t.validations).forEach(e=>{R(i.value,t.validations,j,e,this.formValuesStateSubject$.value)||this.setResetPathValue({key:t.field,path:t.path,property:t.property,value:t.resettledValue})}):this.setResetPathValue({key:t.field,path:t.path,property:t.property,value:t.resettledValue})})}addField({fieldSchema:e,mapperElement:t,path:i}){var s,a,n,r,l;if(this.fields.has(e.name))throw new Error(`field name ${e.name} already defined`);const o=t||(null===(s=this.mappers)||void 0===s?void 0:s.get(e.component));if(!o)throw new Error(`mapper not found for ${e.component}, add it to the mappers configuration`);if(null===(a=o.events)||void 0===a?void 0:a.setValue){const t=null===(n=null==e?void 0:e.props)||void 0===n?void 0:n[null===(r=null==o?void 0:o.events)||void 0===r?void 0:r.setValue];void 0===t||this.queuedInitialValues.has(e.name)||"string"==typeof t&&t.includes("${")||this.queuedInitialValues.set(e.name,k(t))}if(this.fields.set(e.name,new M({formIndex:this.index,schemaComponent:e,mapper:o,children:e.children?e.children.map(e=>e.name):[],validateVisibility:this.validateVisibility.bind(this),resetValue:this.resetValue.bind(this),resetProperty:this.resetProperty.bind(this),formValuesStateSubject$:this.formValuesStateSubject$,templateSubject$:this.templateSubject$,fieldEventSubject$:this.fieldEventSubject$,dataSubject$:this.dataSubject$,fieldValidNotification$:this.fieldValidNotification$,mountSubject$:this.mountSubject$,config:this.config,submitEvent:this.submit.bind(this),visibility:e.visibility,persistValue:e.persistValue,path:i})),null===(l=this.prefetchedData)||void 0===l?void 0:l[e.name]){const t=this.fields.get(e.name);if(t){const i=this.prefetchedData[e.name],s=Object.assign({},t.api);void 0!==i.defaultResponse&&(s.default={response:i.defaultResponse,status:200}),i.namedResponses&&(s.named=s.named||{},Object.keys(i.namedResponses).forEach(e=>{s.named[e]={response:i.namedResponses[e],status:200}})),t._api=s}}}removeField({key:e}){var t;null===(t=this.fields.get(e))||void 0===t||t.destroyField(),this.fields.delete(e),this.subscribeTemplates(),this.templateSubject$.next({scope:"fields",key:e,event:"ON_FIELDS"}),this.validateForm()}serializeStructure(e,t){e&&e.forEach(e=>{var i;const s=this.fields.get(e.name);s?(s.children=(null===(i=null==e?void 0:e.children)||void 0===i?void 0:i.map(e=>e.name))||(null==s?void 0:s.children)||[],s.originalSchema=e,s.templateSubject$=this.templateSubject$):this.addField({fieldSchema:e,mapperElement:e.mapper,path:t}),e.children&&this.serializeStructure(e.children,`${t?`${t}.`:""}${e.name}`)})}refreshFields(e){const t=Array.from(this.fields.keys());this.serializeStructure(e);const i=L.checkIndexes(e);this.fields.forEach((e,t)=>{var s;i.includes(t)||(null===(s=this.fields.get(t))||void 0===s||s.destroyField(),this.fields.delete(t))}),this.subscribeTemplates(),this.fields.forEach((e,i)=>{var s;t.includes(i)||(null===(s=this.fields.get(i))||void 0===s||s.emitEvents({event:"ON_FIELD_MOUNT"}),this.checkFieldEventQueues(i))}),this.subscribedTemplates.forEach(e=>{e.originFieldKeys.forEach(e=>{this.templateSubject$.next({scope:"fields",key:e,event:"ON_FIELDS"})})})}getField({key:e}){return this.fields.get(e)}printValues(){console.table(this.formValuesStateSubject$.value.values)}getFormState(){const e={},t={},i=[];return this.fields.forEach((s,a)=>{"string"==typeof s.value&&0===s.value.length||null==s.value||(N(e,s.nameToSubmit||a,s.value),t[a]=s.metadata),s.valid||i.push(a)}),{values:e,metadata:t,erroredFields:i,isValid:this.valid}}subscribeFieldEvent({callback:e}){return this.fieldEventSubject$.pipe(r(()=>this.submitChecker()),s(e=>`${e.event}|${e.fieldName}`),a(e=>e.pipe(n(this.config.defaultStateRefreshTimeMS)))).subscribe({next:e})}subscribeOnMount(e){return this.mountSubject$.pipe(u(()=>this.formValuesStateSubject$.value)).subscribe({next:e})}subscribeData(e){return this.dataSubject$.pipe(r(({formIndex:e})=>this.index===e&&this.submitChecker()),s(e=>e.event),a(e=>e.pipe(n(this.config.defaultStateRefreshTimeMS))),u(({fieldIndex:e})=>({field:e,data:this.formValuesStateSubject$.value}))).subscribe({next:e})}subscribeOnSubmit(e){return this.submitSubject$.pipe(r(({formIndex:e})=>e===this.index&&this.submitChecker()),u(({values:e})=>e)).subscribe({next:e})}subscribeFormValidation(e){return this.formValidSubject$.pipe(r(({formIndex:e})=>this.index===e),u(()=>({formIndex:this.index,valid:this.valid})),d("valid"),o({formIndex:this.index,valid:this.valid})).subscribe({next:e})}submit(){this.fields.forEach(e=>{e.emitEvents({event:"ON_FIELD_VALIDATION"})}),this.valid&&(this.submitSubject$.next({formIndex:this.index,values:this.formValuesStateSubject$.value}),this.submitted=!0)}destroy(){this.templateSubject$.unsubscribe(),this.templateSubscription$.unsubscribe(),this.mountSubject$.unsubscribe(),this.fieldEventSubject$.unsubscribe(),this.fieldValidNotification$.unsubscribe(),this.formValuesStateSubject$.unsubscribe(),this.isolatedFormInstance&&(this.submitSubject$.unsubscribe(),this.dataSubject$.unsubscribe(),this.formValidSubject$.unsubscribe()),this.fields.forEach(e=>e.destroyField())}}L.checkIndexes=(e,t=[])=>{if(!e)return t;const i=(e,t)=>{for(let s=0;s<e.length;s++){const a=e[s];t.push(a.name),a.children&&i(a.children,t)}};i(e,t);const s=t.filter((e,i)=>t.indexOf(e)!==i);if(s.length>0)throw new Error(`duplicated indexes found on schema: ${JSON.stringify(s)}`);return t};class D{constructor(e){var i,s,a,n,r;this.destroy=()=>{this.forms.forEach(e=>e.destroy()),this.dataSubject$.unsubscribe(),this.formValidSubject$.unsubscribe(),this.submitSubject$.unsubscribe()},this.forms=new Map,this.config={defaultAPIdebounceTimeMS:Number(null===(i=null==e?void 0:e.config)||void 0===i?void 0:i.defaultAPIdebounceTimeMS)?Number(null===(s=null==e?void 0:e.config)||void 0===s?void 0:s.defaultAPIdebounceTimeMS):v,defaultStateRefreshTimeMS:Number(null===(a=null==e?void 0:e.config)||void 0===a?void 0:a.defaultStateRefreshTimeMS)?Number(null===(n=null==e?void 0:e.config)||void 0===n?void 0:n.defaultStateRefreshTimeMS):100,defaultLogVerbose:(null===(r=null==e?void 0:e.config)||void 0===r?void 0:r.defaultLogVerbose)?e.config.defaultLogVerbose:g},this.dataSubject$=new t,this.formValidSubject$=new t,this.submitSubject$=new t,this.mappers=null==e?void 0:e.mappers}createFormWithIndex({index:e,mappers:t}){this.addForm({key:e,params:{index:e,mappers:t,config:this.config}})}addForm({key:e,params:t}){this.checkIndexes({key:e});const i=new L(Object.assign(Object.assign({},t),{dataSubject$:this.dataSubject$,formValidSubject$:this.formValidSubject$,submitSubject$:this.submitSubject$}));i.config||(i.config=this.config),this.forms.set(e,i)}getForm({key:e}){return this.forms.get(e)}removeForm({key:e}){var t;null===(t=this.forms.get(e))||void 0===t||t.destroy(),this.forms.delete(e)}removeField({formIndex:e,fieldIndex:t}){const i=this.forms.get(e);null==i||i.removeField({key:t}),0===(null==i?void 0:i.fields.size)&&this.removeForm({key:e})}checkIndexes({key:e}){if(this.forms.has(e))throw new Error(`duplicate index ${e} on form group`)}printFormGroupInstance(){console.table(this.forms)}submitMultipleFormsByIndex(e,t){let i=!0,s={},a={},n=[];e.forEach(e=>{var t,r;null===(t=this.forms.get(e))||void 0===t||t.submit();const l=null===(r=this.forms.get(e))||void 0===r?void 0:r.formValuesStateSubject$.value,o="object"==typeof(null==l?void 0:l.metadata)&&null!==l.metadata?l.metadata:{};i=i&&((null==l?void 0:l.isValid)||!1),s=Object.assign(Object.assign({},s),(null==l?void 0:l.values)||{}),a=Object.assign(Object.assign({},a),o),n=[...n,...(null==l?void 0:l.erroredFields)||[]]}),i&&t&&t({erroredFields:n,isValid:i,values:s,metadata:a})}onDataSubscription({ids:e,callback:t}){return this.dataSubject$.pipe(r(({formIndex:t})=>e.includes(t)),s(({event:e,formIndex:t})=>`${e}.${t}`),a(e=>e.pipe(n(this.config.defaultStateRefreshTimeMS))),u(({fieldIndex:t,formIndex:i})=>e.reduce((e,s)=>{const a=this.forms.get(s);return a&&(e[s]={formId:i,formField:t,values:a.formValuesStateSubject$.value}),e},{}))).subscribe(t)}onValidSubscription({ids:e,callback:t}){return this.formValidSubject$.pipe(r(({formIndex:t})=>e.includes(t)),s(({formIndex:e})=>e),a(e=>e.pipe(n(0))),o({fieldTrigger:null}),u(()=>({groupValid:e.every(e=>{var t;return!this.forms.get(e)||(null===(t=this.forms.get(e))||void 0===t?void 0:t.valid)}),forms:e.reduce((e,t)=>{const i=this.forms.get(t);return i&&(e[t]=i.valid),e},{})}))).subscribe(t)}onSubmitSubscription({ids:e,callback:t}){return this.submitSubject$.pipe(r(({formIndex:t})=>e.includes(t)),u(()=>e.reduce((e,t)=>{const i=this.forms.get(t);return i&&(e[t]=i.formValuesStateSubject$.value),e},{}))).subscribe(t)}}export{L as FormCore,M as FormField,D as FormGroup,h as TMutationEnum,E as formatterRegistry,O as maskRegistry,x as registerFormatters,_ as registerMasks,I as registerValidations,j as validationRegistry};
package/package.json CHANGED
@@ -1,15 +1,46 @@
1
1
  {
2
2
  "name": "@bolttech/form-engine-core",
3
- "version": "1.1.0-beta.0",
3
+ "version": "1.1.0",
4
+ "sideEffects": false,
5
+ "exports": {
6
+ ".": {
7
+ "types": "./index.d.ts",
8
+ "import": "./index.esm.js",
9
+ "default": "./index.esm.js"
10
+ },
11
+ "./lite": {
12
+ "types": "./lite.d.ts",
13
+ "import": "./lite.esm.js",
14
+ "default": "./lite.esm.js"
15
+ },
16
+ "./credit-card": {
17
+ "types": "./credit-card.d.ts",
18
+ "import": "./credit-card.esm.js",
19
+ "default": "./credit-card.esm.js"
20
+ },
21
+ "./document": {
22
+ "types": "./document.d.ts",
23
+ "import": "./document.esm.js",
24
+ "default": "./document.esm.js"
25
+ },
26
+ "./currency": {
27
+ "types": "./currency.d.ts",
28
+ "import": "./currency.esm.js",
29
+ "default": "./currency.esm.js"
30
+ },
31
+ "./date": {
32
+ "types": "./date.d.ts",
33
+ "import": "./date.esm.js",
34
+ "default": "./date.esm.js"
35
+ }
36
+ },
37
+ "dependencies": {
38
+ "@gaignoux/currency": "^1.1.0",
39
+ "credit-card-type": "^10.0.0",
40
+ "rxjs": "7.8.2"
41
+ },
4
42
  "module": "./index.esm.js",
5
43
  "type": "module",
6
44
  "main": "./index.esm.js",
7
- "dependencies": {
8
- "@gaignoux/currency": "1.1.0",
9
- "credit-card-type": "10.0.1",
10
- "lodash": "4.17.21",
11
- "react": "18.2.0",
12
- "rxjs": "7.8.1"
13
- },
14
- "peerDependencies": {}
15
- }
45
+ "types": "./index.d.ts"
46
+ }
@@ -1,3 +1,4 @@
1
+ import { IFormField } from "../managers/field";
1
2
  declare const DEFAULT_API_DEBOUNCE_TIME = 1000;
2
3
  declare const DEFAULT_STATE_REFRESH_TIME = 100;
3
4
  declare const TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR: RegExp;
@@ -5,6 +6,6 @@ declare const TEMPLATE_REGEX_DELIMITATOR: RegExp;
5
6
  declare const TEMPLATE_REGEX_OPERATOR_SPLITTER: RegExp;
6
7
  declare const TEMPLATE_REGEX_OPERATOR_MATCHER: RegExp;
7
8
  declare const TEMPLATE_AVALIABLE_SCOPES: readonly ["fields", "iVars", "form"];
8
- declare const ALLOWED_RESET_PROPS_MUTATIONS: ("api" | "apiSchema" | "props" | "validations" | "visibilityConditions" | "resetValues")[];
9
+ declare const ALLOWED_RESET_PROPS_MUTATIONS: (keyof Pick<IFormField, "api" | "apiSchema" | "props" | "validations" | "visibilityConditions" | "resetValues">)[];
9
10
  declare const DEFAULT_LOG_VERBOSE = false;
10
11
  export { DEFAULT_API_DEBOUNCE_TIME, DEFAULT_STATE_REFRESH_TIME, DEFAULT_LOG_VERBOSE, TEMPLATE_REGEX_STRING_CONCATENATION_DETECTOR, TEMPLATE_REGEX_DELIMITATOR, TEMPLATE_REGEX_OPERATOR_SPLITTER, TEMPLATE_REGEX_OPERATOR_MATCHER, TEMPLATE_AVALIABLE_SCOPES, ALLOWED_RESET_PROPS_MUTATIONS, };
@@ -1,4 +1,4 @@
1
- import { Subject } from 'rxjs';
1
+ import { BehaviorSubject, Subject } from 'rxjs';
2
2
  /**
3
3
  * Custom RXJS Subject to gracefully handle errors on unsubscribed Subjects
4
4
  * that were unmounted due to adapter external handling such as visibility
@@ -8,4 +8,14 @@ declare class SafeSubject<T> extends Subject<T> {
8
8
  constructor(isMounted: () => boolean);
9
9
  next(value: T): void;
10
10
  }
11
- export { SafeSubject };
11
+ /**
12
+ * Custom RXJS BehaviourSubject to gracefully handle errors on unsubscribed Subjects
13
+ * since its fire and forget, no mount status needed to check if its available or not
14
+ */
15
+ declare class SafeBehaviourSubject<T> extends BehaviorSubject<T> {
16
+ defaultValue: T;
17
+ constructor(value: T);
18
+ next(value: T): void;
19
+ get value(): T;
20
+ }
21
+ export { SafeSubject, SafeBehaviourSubject };
@@ -1,22 +1,22 @@
1
- /// <reference types="node" />
2
1
  import { TApiResponsePayload } from '../types/schema';
3
2
  import { TSubscribedTemplates, TTemplateAvaliableScopes } from '../types/template';
4
- import { OutgoingHttpHeaders } from 'http2';
5
3
  /**
6
- * Makes an HTTP request using XMLHttpRequest.
4
+ * Makes an HTTP request using the Fetch API.
5
+ * Works in both browser and server (Node 18+) environments.
7
6
  *
8
7
  * @param {string} method - The HTTP method (GET, POST, PUT, DELETE, etc.).
9
8
  * @param {string} url - The URL to which the request is sent.
10
- * @param {OutgoingHttpHeaders} [headers] - Optional request headers.
9
+ * @param {Record<string, string>} [headers] - Optional request headers.
11
10
  * @param {Record<string,unknown>} [body] - Optional object body.
12
- * @returns {Promise<string>} A promise that resolves with the response text if the request is successful, otherwise rejects with an error message.
11
+ * @param {Record<string, string>} [queryParams] - Optional query parameters.
12
+ * @returns {Promise<TApiResponsePayload>} A promise that resolves with the response text and status.
13
13
  *
14
14
  * @example
15
15
  * ```typescript
16
16
  * const response = await makeRequest('GET', 'https://api.example.com/data');
17
17
  * ```
18
18
  */
19
- declare function makeRequest(method: string, url: string, headers?: OutgoingHttpHeaders, body?: Record<string, unknown>, queryParams?: Record<string, string>): Promise<TApiResponsePayload>;
19
+ declare function makeRequest(method: string, url: string, headers?: Record<string, string>, body?: Record<string, unknown>, queryParams?: Record<string, string>): Promise<TApiResponsePayload>;
20
20
  /**
21
21
  * Extracts keys enclosed in `${}` from a given expression.
22
22
  *
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Lightweight replacements for lodash functions used in form-engine-core.
3
+ * These cover the specific usage patterns in this codebase.
4
+ */
5
+ /**
6
+ * Deep clones a value. Handles objects, arrays, dates, and primitives.
7
+ * Equivalent to lodash/cloneDeep for the patterns used in this codebase.
8
+ */
9
+ export declare function cloneDeep<T>(value: T): T;
10
+ /**
11
+ * Gets the value at a path of an object. If the resolved value is undefined,
12
+ * the defaultValue is returned in its place.
13
+ * Equivalent to lodash/get.
14
+ */
15
+ export declare function get(obj: any, path: string | (string | number)[], defaultValue?: any): any;
16
+ /**
17
+ * Sets the value at a path of an object. If a portion of the path doesn't exist,
18
+ * it's created. Arrays are created for numeric keys, objects otherwise.
19
+ * Equivalent to lodash/set.
20
+ */
21
+ export declare function set(obj: any, path: string | (string | number)[], value: any): any;
22
+ /**
23
+ * Performs a deep comparison between two values to determine if they are equivalent.
24
+ * Equivalent to lodash/isEqual for the patterns used in this codebase.
25
+ */
26
+ export declare function isEqual(a: any, b: any): boolean;
27
+ /**
28
+ * Checks if value is null or undefined.
29
+ * Equivalent to lodash/isNil.
30
+ */
31
+ export declare function isNil(value: any): value is null | undefined;
32
+ /**
33
+ * Checks if value is classified as a Function object.
34
+ * Equivalent to lodash/isFunction.
35
+ */
36
+ export declare function isFunction(value: any): value is (...args: any[]) => any;
37
+ /**
38
+ * Checks if value is classified as a Number primitive or object.
39
+ * Equivalent to lodash/isNumber.
40
+ */
41
+ export declare function isNumber(value: any): value is number;
@@ -1,5 +1,6 @@
1
1
  import { TSchemaValidation, TValidationMethods } from '../types/schema';
2
2
  import { TValidationHandler } from '../types/utility';
3
+ import { TFormValues } from '../types/form';
3
4
  /**
4
5
  * @internal
5
6
  * Handles the validation of a given value based on specified validation methods and rules.
@@ -24,4 +25,4 @@ import { TValidationHandler } from '../types/utility';
24
25
  * const isValid = handleValidation(value, validations, methods, key);
25
26
  * console.log(isValid); // Output: true
26
27
  */
27
- export default function handleValidation(value: string | number | boolean | unknown, validations: TSchemaValidation, methods: TValidationHandler, key: keyof TValidationMethods): boolean;
28
+ export default function handleValidation(value: string | number | boolean | unknown, validations: TSchemaValidation, methods: TValidationHandler, key: keyof TValidationMethods, formValues: TFormValues<unknown>): boolean;
package/src/index.d.ts CHANGED
@@ -3,8 +3,13 @@ export * from './types/form';
3
3
  export * from './types/schema';
4
4
  export * from './types/template';
5
5
  export * from './types/mapper';
6
+ export * from './types/utility';
6
7
  export * from './interfaces/schema';
7
8
  export * from './interfaces/state';
8
9
  export * from './managers/form';
9
10
  export * from './managers/formGroup';
10
11
  export * from './managers/field';
12
+ export { registerValidations, registerFormatters, registerMasks, validationRegistry, formatterRegistry, maskRegistry } from './registry';
13
+ import './validations/handler';
14
+ import './formatters/handler';
15
+ import './masks/handler';
@@ -130,5 +130,32 @@ interface IFormSchema {
130
130
  iVars?: Record<string, unknown>;
131
131
  components?: IComponentSchema[];
132
132
  stopEventsOnSubmit?: boolean;
133
+ /** Pre-fetched API response data for SSR. Keys are field names, values contain the API responses to hydrate. */
134
+ prefetchedData?: Record<string, TPrefetchedFieldData>;
133
135
  }
134
- export { IFormSchema, IComponentSchema, IComponentSchemaAsFormField };
136
+ /**
137
+ * Represents pre-fetched API data for a single field.
138
+ * Used to hydrate API response caches during SSR so templates referencing API data resolve correctly.
139
+ *
140
+ * @property {unknown} [defaultResponse] - The pre-fetched response for the field's default API config.
141
+ * @property {Record<string, unknown>} [namedResponses] - Pre-fetched responses for named API configs.
142
+ *
143
+ * @example
144
+ * ```typescript
145
+ * const prefetchedData = {
146
+ * countryField: {
147
+ * defaultResponse: [{ id: 'BR', label: 'Brazil' }, { id: 'US', label: 'United States' }],
148
+ * },
149
+ * stateField: {
150
+ * namedResponses: { statesByCountry: [{ id: 'SP', label: 'São Paulo' }] },
151
+ * },
152
+ * };
153
+ * ```
154
+ */
155
+ interface TPrefetchedFieldData {
156
+ /** The pre-fetched response for the field's default API config. */
157
+ defaultResponse?: unknown;
158
+ /** Pre-fetched responses keyed by named API config name. */
159
+ namedResponses?: Record<string, unknown>;
160
+ }
161
+ export { IFormSchema, IComponentSchema, IComponentSchemaAsFormField, TPrefetchedFieldData };
package/src/lite.d.ts ADDED
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Lite entry point for form-engine-core.
3
+ *
4
+ * This entry point excludes credit-card and document validation modules,
5
+ * reducing bundle size for consumers who don't need them.
6
+ *
7
+ * To add credit card support:
8
+ * import '@bolttech/form-engine-core/credit-card';
9
+ *
10
+ * To add document validation support:
11
+ * import '@bolttech/form-engine-core/document';
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { FormCore, FormGroup } from '@bolttech/form-engine-core/lite';
16
+ * import '@bolttech/form-engine-core/credit-card'; // optional
17
+ * ```
18
+ */
19
+ export * from './types/event';
20
+ export * from './types/form';
21
+ export * from './types/schema';
22
+ export * from './types/template';
23
+ export * from './types/mapper';
24
+ export * from './types/utility';
25
+ export * from './interfaces/schema';
26
+ export * from './interfaces/state';
27
+ export * from './managers/form';
28
+ export * from './managers/formGroup';
29
+ export * from './managers/field';
30
+ export { registerValidations, registerFormatters, registerMasks, validationRegistry, formatterRegistry, maskRegistry } from './registry';
@@ -1,4 +1,4 @@
1
- import { Subject, Subscription } from 'rxjs';
1
+ import { Subject, Subscription, BehaviorSubject } from 'rxjs';
2
2
  import { TApiConfig, TApiEvent, TApiResponse, TErrorMessages, TFormatters, TMasks, TResetPathMethods, TResetValueMethods, TSchemaFormConfig, TValidations, TVisibility } from '../types/schema';
3
3
  import { IComponentSchema, IComponentSchemaAsFormField } from '../interfaces/schema';
4
4
  import { IState } from '../interfaces/state';
@@ -30,6 +30,7 @@ declare class FormField {
30
30
  errorsString: string;
31
31
  errorsList: string[];
32
32
  private _props;
33
+ private _adapterProps;
33
34
  private _value;
34
35
  private _stateValue;
35
36
  private _metadata;
@@ -55,6 +56,7 @@ declare class FormField {
55
56
  key: string;
56
57
  status: boolean;
57
58
  }>;
59
+ formValuesStateSubject$: BehaviorSubject<TFormValues<unknown>>;
58
60
  validateVisibility: (payload: {
59
61
  event: TEvents;
60
62
  key: string;
@@ -89,7 +91,7 @@ declare class FormField {
89
91
  * @param {TMapper<unknown>} options.mapper, - component generic mapper containing render parameters for adapters
90
92
  * @param {() => TFormValues<unknown>} options.getFormValues, - form instance function that builds onData parameter payload from fields
91
93
  */
92
- constructor({ formIndex, schemaComponent, config, path, children, validateVisibility, resetValue, resetProperty, templateSubject$, fieldEventSubject$, dataSubject$, fieldValidNotification$, mountSubject$, mapper, getFormValues, submitEvent, visibility, persistValue, }: {
94
+ constructor({ formIndex, schemaComponent, config, children, validateVisibility, resetValue, resetProperty, templateSubject$, fieldEventSubject$, dataSubject$, fieldValidNotification$, mountSubject$, mapper, formValuesStateSubject$, submitEvent, visibility, persistValue, }: {
93
95
  formIndex: string;
94
96
  schemaComponent: IComponentSchema;
95
97
  config?: TSchemaFormConfig;
@@ -116,8 +118,8 @@ declare class FormField {
116
118
  key: string;
117
119
  status: boolean;
118
120
  }>;
121
+ formValuesStateSubject$: BehaviorSubject<TFormValues<unknown>>;
119
122
  mapper: TMapper<unknown>;
120
- getFormValues: () => TFormValues<unknown>;
121
123
  visibility?: boolean;
122
124
  persistValue?: boolean;
123
125
  });
@@ -127,6 +129,18 @@ declare class FormField {
127
129
  * emissions to unsubscribed fields
128
130
  */
129
131
  initializeObservers(): void;
132
+ /**
133
+ * Retrieves the raw props sent from the adapter.
134
+ *
135
+ * @returns {string} - raw props from the adapter
136
+ */
137
+ get adapterProps(): string;
138
+ /**
139
+ * compares adapter props changes and emits the change if they effectively changed
140
+ * preventing an emission from the adapter of the same props that can overwrite other prop
141
+ * changes via templating
142
+ */
143
+ set adapterProps(props: Record<string, unknown>);
130
144
  /**
131
145
  * Retrieves the properties associated with the form field.
132
146
  *
@@ -1,11 +1,12 @@
1
1
  import { IFormField } from './field';
2
2
  import { Subject, Subscription } from 'rxjs';
3
- import { IComponentSchema, IComponentSchemaAsFormField, IFormSchema } from '../interfaces/schema';
3
+ import { IComponentSchema, IComponentSchemaAsFormField, IFormSchema, TPrefetchedFieldData } from '../interfaces/schema';
4
4
  import { TSchemaFormConfig } from '../types/schema';
5
5
  import { TSubscribedTemplates, TTemplateAvaliableScopes, TTemplateEvent } from '../types/template';
6
6
  import { TEvents, TFieldEvent, TFieldValidationPayload, TFormDataPayload, TFormSubmitPayload, TFormValidationPayload, TMutationEvents } from '../types/event';
7
7
  import { TFormEntry, TFormValues } from '../types/form';
8
8
  import { TMapper } from '../types/mapper';
9
+ import { SafeBehaviourSubject } from '../helpers/SafeSubject';
9
10
  /**
10
11
  * Represents the core logic for managing a form, including field management, validation, and submission.
11
12
  */
@@ -25,6 +26,7 @@ declare class FormCore {
25
26
  dataSubject$: Subject<TFormDataPayload>;
26
27
  formValidSubject$: Subject<TFormValidationPayload>;
27
28
  fieldValidNotification$: Subject<TFieldValidationPayload>;
29
+ formValuesStateSubject$: SafeBehaviourSubject<TFormValues<unknown>>;
28
30
  subscribedTemplates: TSubscribedTemplates[];
29
31
  action?: string;
30
32
  method?: string;
@@ -43,9 +45,12 @@ declare class FormCore {
43
45
  value: unknown;
44
46
  }>;
45
47
  queuedInitialValues: Map<string, unknown>;
48
+ prefetchedData?: Record<string, TPrefetchedFieldData>;
46
49
  _valid: boolean;
47
50
  stopEventsOnSubmit: boolean;
48
51
  submitted: boolean;
52
+ getFormValues: () => TFormValues<unknown>;
53
+ isolatedFormInstance: boolean;
49
54
  /**
50
55
  * Creates an instance of FormCore.
51
56
  *
@@ -302,7 +307,7 @@ declare class FormCore {
302
307
  *
303
308
  * @returns {TFormValues} The current form values.
304
309
  */
305
- getFormValues<T>(): TFormValues<T>;
310
+ getFormState<T>(): TFormValues<T>;
306
311
  /**
307
312
  * function to be called to events sent from the adapter
308
313
  *
@@ -0,0 +1,29 @@
1
+ import { TMasks } from '../types/schema';
2
+ /**
3
+ * Formats a numeric value as a currency string based on the provided mask configuration.
4
+ *
5
+ * @param value - The numeric value to be formatted.
6
+ * @param masks - An object containing the mask configuration.
7
+ * @returns The formatted currency string.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * import { currency } from './path/to/maskFunctions';
12
+ *
13
+ * const masks = {
14
+ * currency: {
15
+ * align: 'right',
16
+ * decimal: ',',
17
+ * precision: 2,
18
+ * prefix: 'USD',
19
+ * thousands: '.'
20
+ * }
21
+ * };
22
+ *
23
+ * const formattedValue = currency(12345.67, masks);
24
+ * console.log(formattedValue); // Output: '12.345,67 $'
25
+ *
26
+ * PS: To unCurrency this value, usage onlyFloatNumber formatter
27
+ * ```
28
+ */
29
+ export declare const currency: (value: string | number, masks: TMasks) => unknown;
@@ -1,2 +1,2 @@
1
1
  import { TMasks } from '../types/schema';
2
- export declare const masks: Record<keyof TMasks, (value: unknown, masks?: TMasks | null) => unknown>;
2
+ export declare const masks: Record<string, (value: unknown, masks?: TMasks | null) => unknown>;
@@ -14,63 +14,9 @@ import { TMasks } from '../types/schema';
14
14
  *
15
15
  * const maskedValue = replaceAll('12345', masks);
16
16
  * console.log(maskedValue); // Output: '*****'
17
- *
18
- * // Using from a JSON config
19
- * const config = {
20
- * value: 'hello',
21
- * masks: { replaceAll: '*' }
22
- * };
23
- *
24
- * const maskedValue = replaceAll(config.value, config.masks);
25
- * console.log(maskedValue); // Output: '*****'
26
17
  * ```
27
18
  */
28
19
  export declare const replaceAll: (value: string | number, masks: TMasks) => unknown;
29
- /**
30
- * Formats a numeric value as a currency string based on the provided mask configuration.
31
- *
32
- * @param value - The numeric value to be formatted.
33
- * @param masks - An object containing the mask configuration.
34
- * @returns The formatted currency string.
35
- *
36
- * @example
37
- * ```typescript
38
- * import { currency } from './path/to/maskFunctions';
39
- *
40
- * const masks = {
41
- * currency: {
42
- * align: 'right',
43
- * decimal: ',',
44
- * precision: 2,
45
- * prefix: 'USD',
46
- * thousands: '.'
47
- * }
48
- * };
49
- *
50
- * const formattedValue = currency(12345.67, masks);
51
- * console.log(formattedValue); // Output: '12.345,67 $'
52
- *
53
- * // Using from a JSON config
54
- * const config = {
55
- * value: 9876.54,
56
- * masks: {
57
- * currency: {
58
- * align: 'right',
59
- * decimal: ',',
60
- * precision: 2,
61
- * prefix: 'EUR',
62
- * thousands: '.'
63
- * }
64
- * }
65
- * };
66
- *
67
- * const formattedValue = currency(config.value, config.masks);
68
- * console.log(formattedValue); // Output: '9.876,54 €'
69
- *
70
- * PS: To unCurrency this value, usage onlyFloatNumber formatter
71
- * ```
72
- */
73
- export declare const currency: (value: string | number, masks: TMasks) => unknown;
74
20
  /**
75
21
  * Applies a custom mask to a string based on the provided mask configuration.
76
22
  *
@@ -86,14 +32,6 @@ export declare const currency: (value: string | number, masks: TMasks) => unknow
86
32
  *
87
33
  * const maskedValue = custom('123456', masks);
88
34
  * console.log(maskedValue); // Output: '12-34'
89
- *
90
- * // Using from a JSON config
91
- * const config = {
92
- * masks: { custom: '###-###' }
93
- * };
94
- *
95
- * const maskedValue = custom(config.value, config.masks);
96
- * console.log(maskedValue); // Output: '987-654'
97
35
  * ```
98
36
  */
99
37
  export declare const custom: (value: string, masks: TMasks) => string;
@@ -0,0 +1,4 @@
1
+ import { isCreditCard, isCreditCardAndLength, isCreditCodeMatch } from '../validations/creditCard';
2
+ import { gapsCreditCard } from '../formatters/creditCard';
3
+ import { secureCreditCard, card, cardDate, fein } from '../masks/creditCard';
4
+ export { isCreditCard, isCreditCardAndLength, isCreditCodeMatch, gapsCreditCard, secureCreditCard, card, cardDate, fein, };
@@ -0,0 +1,2 @@
1
+ import { currency } from '../masks/currency';
2
+ export { currency };
@@ -0,0 +1,2 @@
1
+ import { date, betweenDates, validDate } from '../validations/date';
2
+ export { date, betweenDates, validDate };
@@ -0,0 +1,2 @@
1
+ import document from '../validations/document';
2
+ export { document };
@@ -0,0 +1,20 @@
1
+ import { TValidationHandler } from './types/utility';
2
+ /**
3
+ * Global mutable registries for validations, formatters, and masks.
4
+ * Plugins (sub-path exports) can extend these at import time.
5
+ */
6
+ export declare const validationRegistry: TValidationHandler;
7
+ export declare const formatterRegistry: Record<string, (value: unknown, formatters?: any) => unknown>;
8
+ export declare const maskRegistry: Record<string, (value: unknown, masks?: any) => unknown>;
9
+ /**
10
+ * Registers additional validation methods into the global registry.
11
+ */
12
+ export declare function registerValidations(methods: TValidationHandler): void;
13
+ /**
14
+ * Registers additional formatter methods into the global registry.
15
+ */
16
+ export declare function registerFormatters(methods: Record<string, (value: unknown, formatters?: any) => unknown>): void;
17
+ /**
18
+ * Registers additional mask methods into the global registry.
19
+ */
20
+ export declare function registerMasks(methods: Record<string, (value: unknown, masks?: any) => unknown>): void;