@openmrs/esm-form-engine-lib 3.3.1-pre.2208 → 3.3.1-pre.2219

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.
@@ -1 +1 @@
1
- var _openmrs_esm_form_engine_lib;(()=>{"use strict";var e,r,t,n,o,i,a,l,s,u,f,d,p,c,h,m,v,g,b,y,w,_={78008:(e,r,t)=>{var n={"./start":()=>Promise.all([t.e(177),t.e(435),t.e(759),t.e(72),t.e(286),t.e(449)]).then((()=>()=>t(69449)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module "'+e+'" does not exist in container.')})),t.R=void 0,r),i=(e,r)=>{if(t.S){var n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>i})}},j={};function P(e){var r=j[e];if(void 0!==r)return r.exports;var t=j[e]={id:e,loaded:!1,exports:{}};return _[e].call(t.exports,t,t.exports,P),t.loaded=!0,t.exports}P.m=_,P.c=j,P.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return P.d(r,{a:r}),r},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,P.t=function(t,n){if(1&n&&(t=this(t)),8&n)return t;if("object"==typeof t&&t){if(4&n&&t.__esModule)return t;if(16&n&&"function"==typeof t.then)return t}var o=Object.create(null);P.r(o);var i={};e=e||[null,r({}),r([]),r(r)];for(var a=2&n&&t;"object"==typeof a&&!~e.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>t[e]));return i.default=()=>t,P.d(o,i),o},P.d=(e,r)=>{for(var t in r)P.o(r,t)&&!P.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},P.f={},P.e=e=>Promise.all(Object.keys(P.f).reduce(((r,t)=>(P.f[t](e,r),r)),[])),P.u=e=>e+".js",P.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),P.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t={},n="@openmrs/esm-form-engine-lib:",P.l=(e,r,o,i)=>{if(t[e])t[e].push(r);else{var a,l;if(void 0!==o)for(var s=document.getElementsByTagName("script"),u=0;u<s.length;u++){var f=s[u];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==n+o){a=f;break}}a||(l=!0,(a=document.createElement("script")).charset="utf-8",a.timeout=120,P.nc&&a.setAttribute("nonce",P.nc),a.setAttribute("data-webpack",n+o),a.src=e),t[e]=[r];var d=(r,n)=>{a.onerror=a.onload=null,clearTimeout(p);var o=t[e];if(delete t[e],a.parentNode&&a.parentNode.removeChild(a),o&&o.forEach((e=>e(n))),r)return r(n)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=d.bind(null,a.onerror),a.onload=d.bind(null,a.onload),l&&document.head.appendChild(a)}},P.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},P.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),P.j=719,(()=>{P.S={};var e={},r={};P.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];P.o(P.S,t)||(P.S[t]={});var i=P.S[t],a="@openmrs/esm-form-engine-lib",l=(e,r,t,n)=>{var o=i[e]=i[e]||{},l=o[r];(!l||!l.loaded&&(!n!=!l.eager?n:a>l.from))&&(o[r]={get:t,from:a,eager:!!n})},s=[];return"default"===t&&(l("@openmrs/esm-framework","8.0.1-pre.3511",(()=>Promise.all([P.e(177),P.e(747),P.e(435),P.e(72),P.e(184),P.e(286),P.e(796)]).then((()=>()=>P(16747))))),l("dayjs","1.11.13",(()=>P.e(353).then((()=>()=>P(74353))))),l("i18next","25.6.0",(()=>P.e(635).then((()=>()=>P(72635))))),l("react-i18next","16.0.0",(()=>Promise.all([P.e(255),P.e(72)]).then((()=>()=>P(77255))))),l("react","18.3.1",(()=>P.e(540).then((()=>()=>P(96540))))),l("swr/immutable","2.3.3",(()=>Promise.all([P.e(177),P.e(72),P.e(606)]).then((()=>()=>P(54225))))),l("swr/infinite","2.3.3",(()=>Promise.all([P.e(177),P.e(72),P.e(41)]).then((()=>()=>P(23041)))))),e[t]=s.length?Promise.all(s).then((()=>e[t]=1)):1}}})(),(()=>{var e;P.g.importScripts&&(e=P.g.location+"");var r=P.g.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),P.p=e})(),o=e=>{var r=e=>e.split(".").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},i=(e,r)=>{e=o(e),r=o(r);for(var t=0;;){if(t>=e.length)return t<r.length&&"u"!=(typeof r[t])[0];var n=e[t],i=(typeof n)[0];if(t>=r.length)return"u"==i;var a=r[t],l=(typeof a)[0];if(i!=l)return"o"==i&&"n"==l||"s"==l||"u"==i;if("o"!=i&&"u"!=i&&n!=a)return n<a;t++}},a=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,o=1;o<e.length;o++)n--,t+="u"==(typeof(l=e[o]))[0]?"-":(n>0?".":"")+(n=2,l);return t}var i=[];for(o=1;o<e.length;o++){var l=e[o];i.push(0===l?"not("+s()+")":1===l?"("+s()+" || "+s()+")":2===l?i.pop()+" "+i.pop():a(l))}return s();function s(){return i.pop().replace(/^\((.+)\)$/,"$1")}},l=(e,r)=>{if(0 in e){r=o(r);var t=e[0],n=t<0;n&&(t=-t-1);for(var i=0,a=1,s=!0;;a++,i++){var u,f,d=a<e.length?(typeof e[a])[0]:"";if(i>=r.length||"o"==(f=(typeof(u=r[i]))[0]))return!s||("u"==d?a>t&&!n:""==d!=n);if("u"==f){if(!s||"u"!=d)return!1}else if(s)if(d==f)if(a<=t){if(u!=e[a])return!1}else{if(n?u>e[a]:u<e[a])return!1;u!=e[a]&&(s=!1)}else if("s"!=d&&"n"!=d){if(n||a<=t)return!1;s=!1,a--}else{if(a<=t||f<d!=n)return!1;s=!1}else"s"!=d&&"n"!=d&&(s=!1,a--)}}var p=[],c=p.pop.bind(p);for(i=1;i<e.length;i++){var h=e[i];p.push(1==h?c()|c():2==h?c()&c():h?l(h,r):!c())}return!!c()},s=(e,r)=>e&&P.o(e,r),u=e=>(e.loaded=1,e.get()),f=e=>Object.keys(e).reduce(((r,t)=>(e[t].eager&&(r[t]=e[t]),r)),{}),d=(e,r,t)=>{var n=t?f(e[r]):e[r];return Object.keys(n).reduce(((e,r)=>!e||!n[e].loaded&&i(e,r)?r:e),0)},p=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+a(n)+")",c=e=>{throw new Error(e)},h=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},m=(e,r,t)=>t?t():((e,r)=>c("Shared module "+r+" doesn't exist in shared scope "+e))(e,r),v=(e=>function(r,t,n,o,i){var a=P.I(r);return a&&a.then&&!n?a.then(e.bind(e,r,P.S[r],t,!1,o,i)):e(r,P.S[r],t,n,o,i)})(((e,r,t,n,o,i)=>{if(!s(r,t))return m(e,t,i);var a=d(r,t,n);return l(o,a)||h(p(r,t,a,o)),u(r[t][a])})),g={},b={16072:()=>v("default","react",!1,[1,18],(()=>P.e(540).then((()=>()=>P(96540))))),56339:()=>v("default","swr/infinite",!1,[1,2],(()=>P.e(422).then((()=>()=>P(23041))))),92232:()=>v("default","i18next",!1,[1,25],(()=>P.e(635).then((()=>()=>P(72635))))),2076:()=>v("default","react-i18next",!1,[1,16],(()=>P.e(255).then((()=>()=>P(77255))))),15847:()=>v("default","@openmrs/esm-framework",!1,[1,8],(()=>Promise.all([P.e(177),P.e(747),P.e(184)]).then((()=>()=>P(16747))))),44209:()=>v("default","swr/immutable",!1,[1,2],(()=>Promise.all([P.e(177),P.e(225)]).then((()=>()=>P(54225))))),70231:()=>v("default","dayjs",!1,[1,1],(()=>P.e(353).then((()=>()=>P(74353)))))},y={72:[16072],184:[56339,92232],286:[2076,15847],449:[44209,70231],796:[44209,70231]},w={},P.f.consumes=(e,r)=>{P.o(y,e)&&y[e].forEach((e=>{if(P.o(g,e))return r.push(g[e]);if(!w[e]){var t=r=>{g[e]=0,P.m[e]=t=>{delete P.c[e],t.exports=r()}};w[e]=!0;var n=r=>{delete g[e],P.m[e]=t=>{throw delete P.c[e],r}};try{var o=b[e]();o.then?r.push(g[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}}))},(()=>{var e={719:0};P.f.j=(r,t)=>{var n=P.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(184|286|72)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var i=P.p+P.u(r),a=new Error;P.l(i,(t=>{if(P.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;a.message="Loading chunk "+r+" failed.\n("+o+": "+i+")",a.name="ChunkLoadError",a.type=o,a.request=i,n[1](a)}}),"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[i,a,l]=t,s=0;if(i.some((r=>0!==e[r]))){for(n in a)P.o(a,n)&&(P.m[n]=a[n]);l&&l(P)}for(r&&r(t);s<i.length;s++)o=i[s],P.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=globalThis.webpackChunk_openmrs_esm_form_engine_lib=globalThis.webpackChunk_openmrs_esm_form_engine_lib||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),P.nc=void 0;var S=P(78008);_openmrs_esm_form_engine_lib=S})();
1
+ var _openmrs_esm_form_engine_lib;(()=>{"use strict";var e,r,t,n,o,i,a,l,s,u,f,d,p,c,h,m,v,g,b,y,w,_={78008:(e,r,t)=>{var n={"./start":()=>Promise.all([t.e(177),t.e(435),t.e(926),t.e(72),t.e(286),t.e(449)]).then((()=>()=>t(69449)))},o=(e,r)=>(t.R=r,r=t.o(n,e)?n[e]():Promise.resolve().then((()=>{throw new Error('Module "'+e+'" does not exist in container.')})),t.R=void 0,r),i=(e,r)=>{if(t.S){var n="default",o=t.S[n];if(o&&o!==e)throw new Error("Container initialization failed as it has already been initialized with a different share scope");return t.S[n]=e,t.I(n,r)}};t.d(r,{get:()=>o,init:()=>i})}},j={};function P(e){var r=j[e];if(void 0!==r)return r.exports;var t=j[e]={id:e,loaded:!1,exports:{}};return _[e].call(t.exports,t,t.exports,P),t.loaded=!0,t.exports}P.m=_,P.c=j,P.n=e=>{var r=e&&e.__esModule?()=>e.default:()=>e;return P.d(r,{a:r}),r},r=Object.getPrototypeOf?e=>Object.getPrototypeOf(e):e=>e.__proto__,P.t=function(t,n){if(1&n&&(t=this(t)),8&n)return t;if("object"==typeof t&&t){if(4&n&&t.__esModule)return t;if(16&n&&"function"==typeof t.then)return t}var o=Object.create(null);P.r(o);var i={};e=e||[null,r({}),r([]),r(r)];for(var a=2&n&&t;"object"==typeof a&&!~e.indexOf(a);a=r(a))Object.getOwnPropertyNames(a).forEach((e=>i[e]=()=>t[e]));return i.default=()=>t,P.d(o,i),o},P.d=(e,r)=>{for(var t in r)P.o(r,t)&&!P.o(e,t)&&Object.defineProperty(e,t,{enumerable:!0,get:r[t]})},P.f={},P.e=e=>Promise.all(Object.keys(P.f).reduce(((r,t)=>(P.f[t](e,r),r)),[])),P.u=e=>e+".js",P.g=function(){if("object"==typeof globalThis)return globalThis;try{return this||new Function("return this")()}catch(e){if("object"==typeof window)return window}}(),P.o=(e,r)=>Object.prototype.hasOwnProperty.call(e,r),t={},n="@openmrs/esm-form-engine-lib:",P.l=(e,r,o,i)=>{if(t[e])t[e].push(r);else{var a,l;if(void 0!==o)for(var s=document.getElementsByTagName("script"),u=0;u<s.length;u++){var f=s[u];if(f.getAttribute("src")==e||f.getAttribute("data-webpack")==n+o){a=f;break}}a||(l=!0,(a=document.createElement("script")).charset="utf-8",a.timeout=120,P.nc&&a.setAttribute("nonce",P.nc),a.setAttribute("data-webpack",n+o),a.src=e),t[e]=[r];var d=(r,n)=>{a.onerror=a.onload=null,clearTimeout(p);var o=t[e];if(delete t[e],a.parentNode&&a.parentNode.removeChild(a),o&&o.forEach((e=>e(n))),r)return r(n)},p=setTimeout(d.bind(null,void 0,{type:"timeout",target:a}),12e4);a.onerror=d.bind(null,a.onerror),a.onload=d.bind(null,a.onload),l&&document.head.appendChild(a)}},P.r=e=>{"undefined"!=typeof Symbol&&Symbol.toStringTag&&Object.defineProperty(e,Symbol.toStringTag,{value:"Module"}),Object.defineProperty(e,"__esModule",{value:!0})},P.nmd=e=>(e.paths=[],e.children||(e.children=[]),e),P.j=719,(()=>{P.S={};var e={},r={};P.I=(t,n)=>{n||(n=[]);var o=r[t];if(o||(o=r[t]={}),!(n.indexOf(o)>=0)){if(n.push(o),e[t])return e[t];P.o(P.S,t)||(P.S[t]={});var i=P.S[t],a="@openmrs/esm-form-engine-lib",l=(e,r,t,n)=>{var o=i[e]=i[e]||{},l=o[r];(!l||!l.loaded&&(!n!=!l.eager?n:a>l.from))&&(o[r]={get:t,from:a,eager:!!n})},s=[];return"default"===t&&(l("@openmrs/esm-framework","8.0.1-pre.3511",(()=>Promise.all([P.e(177),P.e(747),P.e(435),P.e(72),P.e(184),P.e(286),P.e(796)]).then((()=>()=>P(16747))))),l("dayjs","1.11.13",(()=>P.e(353).then((()=>()=>P(74353))))),l("i18next","25.6.0",(()=>P.e(635).then((()=>()=>P(72635))))),l("react-i18next","16.0.0",(()=>Promise.all([P.e(255),P.e(72)]).then((()=>()=>P(77255))))),l("react","18.3.1",(()=>P.e(540).then((()=>()=>P(96540))))),l("swr/immutable","2.3.3",(()=>Promise.all([P.e(177),P.e(72),P.e(606)]).then((()=>()=>P(54225))))),l("swr/infinite","2.3.3",(()=>Promise.all([P.e(177),P.e(72),P.e(41)]).then((()=>()=>P(23041)))))),e[t]=s.length?Promise.all(s).then((()=>e[t]=1)):1}}})(),(()=>{var e;P.g.importScripts&&(e=P.g.location+"");var r=P.g.document;if(!e&&r&&(r.currentScript&&"SCRIPT"===r.currentScript.tagName.toUpperCase()&&(e=r.currentScript.src),!e)){var t=r.getElementsByTagName("script");if(t.length)for(var n=t.length-1;n>-1&&(!e||!/^http(s?):/.test(e));)e=t[n--].src}if(!e)throw new Error("Automatic publicPath is not supported in this browser");e=e.replace(/^blob:/,"").replace(/#.*$/,"").replace(/\?.*$/,"").replace(/\/[^\/]+$/,"/"),P.p=e})(),o=e=>{var r=e=>e.split(".").map((e=>+e==e?+e:e)),t=/^([^-+]+)?(?:-([^+]+))?(?:\+(.+))?$/.exec(e),n=t[1]?r(t[1]):[];return t[2]&&(n.length++,n.push.apply(n,r(t[2]))),t[3]&&(n.push([]),n.push.apply(n,r(t[3]))),n},i=(e,r)=>{e=o(e),r=o(r);for(var t=0;;){if(t>=e.length)return t<r.length&&"u"!=(typeof r[t])[0];var n=e[t],i=(typeof n)[0];if(t>=r.length)return"u"==i;var a=r[t],l=(typeof a)[0];if(i!=l)return"o"==i&&"n"==l||"s"==l||"u"==i;if("o"!=i&&"u"!=i&&n!=a)return n<a;t++}},a=e=>{var r=e[0],t="";if(1===e.length)return"*";if(r+.5){t+=0==r?">=":-1==r?"<":1==r?"^":2==r?"~":r>0?"=":"!=";for(var n=1,o=1;o<e.length;o++)n--,t+="u"==(typeof(l=e[o]))[0]?"-":(n>0?".":"")+(n=2,l);return t}var i=[];for(o=1;o<e.length;o++){var l=e[o];i.push(0===l?"not("+s()+")":1===l?"("+s()+" || "+s()+")":2===l?i.pop()+" "+i.pop():a(l))}return s();function s(){return i.pop().replace(/^\((.+)\)$/,"$1")}},l=(e,r)=>{if(0 in e){r=o(r);var t=e[0],n=t<0;n&&(t=-t-1);for(var i=0,a=1,s=!0;;a++,i++){var u,f,d=a<e.length?(typeof e[a])[0]:"";if(i>=r.length||"o"==(f=(typeof(u=r[i]))[0]))return!s||("u"==d?a>t&&!n:""==d!=n);if("u"==f){if(!s||"u"!=d)return!1}else if(s)if(d==f)if(a<=t){if(u!=e[a])return!1}else{if(n?u>e[a]:u<e[a])return!1;u!=e[a]&&(s=!1)}else if("s"!=d&&"n"!=d){if(n||a<=t)return!1;s=!1,a--}else{if(a<=t||f<d!=n)return!1;s=!1}else"s"!=d&&"n"!=d&&(s=!1,a--)}}var p=[],c=p.pop.bind(p);for(i=1;i<e.length;i++){var h=e[i];p.push(1==h?c()|c():2==h?c()&c():h?l(h,r):!c())}return!!c()},s=(e,r)=>e&&P.o(e,r),u=e=>(e.loaded=1,e.get()),f=e=>Object.keys(e).reduce(((r,t)=>(e[t].eager&&(r[t]=e[t]),r)),{}),d=(e,r,t)=>{var n=t?f(e[r]):e[r];return Object.keys(n).reduce(((e,r)=>!e||!n[e].loaded&&i(e,r)?r:e),0)},p=(e,r,t,n)=>"Unsatisfied version "+t+" from "+(t&&e[r][t].from)+" of shared singleton module "+r+" (required "+a(n)+")",c=e=>{throw new Error(e)},h=e=>{"undefined"!=typeof console&&console.warn&&console.warn(e)},m=(e,r,t)=>t?t():((e,r)=>c("Shared module "+r+" doesn't exist in shared scope "+e))(e,r),v=(e=>function(r,t,n,o,i){var a=P.I(r);return a&&a.then&&!n?a.then(e.bind(e,r,P.S[r],t,!1,o,i)):e(r,P.S[r],t,n,o,i)})(((e,r,t,n,o,i)=>{if(!s(r,t))return m(e,t,i);var a=d(r,t,n);return l(o,a)||h(p(r,t,a,o)),u(r[t][a])})),g={},b={16072:()=>v("default","react",!1,[1,18],(()=>P.e(540).then((()=>()=>P(96540))))),56339:()=>v("default","swr/infinite",!1,[1,2],(()=>P.e(422).then((()=>()=>P(23041))))),92232:()=>v("default","i18next",!1,[1,25],(()=>P.e(635).then((()=>()=>P(72635))))),2076:()=>v("default","react-i18next",!1,[1,16],(()=>P.e(255).then((()=>()=>P(77255))))),15847:()=>v("default","@openmrs/esm-framework",!1,[1,8],(()=>Promise.all([P.e(177),P.e(747),P.e(184)]).then((()=>()=>P(16747))))),44209:()=>v("default","swr/immutable",!1,[1,2],(()=>Promise.all([P.e(177),P.e(225)]).then((()=>()=>P(54225))))),70231:()=>v("default","dayjs",!1,[1,1],(()=>P.e(353).then((()=>()=>P(74353)))))},y={72:[16072],184:[56339,92232],286:[2076,15847],449:[44209,70231],796:[44209,70231]},w={},P.f.consumes=(e,r)=>{P.o(y,e)&&y[e].forEach((e=>{if(P.o(g,e))return r.push(g[e]);if(!w[e]){var t=r=>{g[e]=0,P.m[e]=t=>{delete P.c[e],t.exports=r()}};w[e]=!0;var n=r=>{delete g[e],P.m[e]=t=>{throw delete P.c[e],r}};try{var o=b[e]();o.then?r.push(g[e]=o.then(t).catch(n)):t(o)}catch(e){n(e)}}}))},(()=>{var e={719:0};P.f.j=(r,t)=>{var n=P.o(e,r)?e[r]:void 0;if(0!==n)if(n)t.push(n[2]);else if(/^(184|286|72)$/.test(r))e[r]=0;else{var o=new Promise(((t,o)=>n=e[r]=[t,o]));t.push(n[2]=o);var i=P.p+P.u(r),a=new Error;P.l(i,(t=>{if(P.o(e,r)&&(0!==(n=e[r])&&(e[r]=void 0),n)){var o=t&&("load"===t.type?"missing":t.type),i=t&&t.target&&t.target.src;a.message="Loading chunk "+r+" failed.\n("+o+": "+i+")",a.name="ChunkLoadError",a.type=o,a.request=i,n[1](a)}}),"chunk-"+r,r)}};var r=(r,t)=>{var n,o,[i,a,l]=t,s=0;if(i.some((r=>0!==e[r]))){for(n in a)P.o(a,n)&&(P.m[n]=a[n]);l&&l(P)}for(r&&r(t);s<i.length;s++)o=i[s],P.o(e,o)&&e[o]&&e[o][0](),e[o]=0},t=globalThis.webpackChunk_openmrs_esm_form_engine_lib=globalThis.webpackChunk_openmrs_esm_form_engine_lib||[];t.forEach(r.bind(null,0)),t.push=r.bind(null,t.push.bind(t))})(),P.nc=void 0;var S=P(78008);_openmrs_esm_form_engine_lib=S})();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openmrs/esm-form-engine-lib",
3
- "version": "3.3.1-pre.2208",
3
+ "version": "3.3.1-pre.2219",
4
4
  "description": "React Form Engine for O3",
5
5
  "browser": "dist/openmrs-esm-form-engine-lib.js",
6
6
  "main": "src/index.ts",
@@ -31,11 +31,15 @@ const FormProcessorFactory = ({
31
31
 
32
32
  const processor = useMemo(() => {
33
33
  const ProcessorClass = formProcessors[formJson.processor];
34
+ let processorInstance;
34
35
  if (ProcessorClass) {
35
- return new ProcessorClass(formJson);
36
+ processorInstance = new ProcessorClass(formJson);
37
+ } else {
38
+ console.error(`Form processor ${formJson.processor} not found, defaulting to EncounterFormProcessor`);
39
+ processorInstance = new EncounterFormProcessor(formJson);
36
40
  }
37
- console.error(`Form processor ${formJson.processor} not found, defaulting to EncounterFormProcessor`);
38
- return new EncounterFormProcessor(formJson);
41
+ processorInstance.prepareFormSchema(formJson);
42
+ return processorInstance;
39
43
  }, [formProcessors, formJson.processor]);
40
44
 
41
45
  const [processorContext, setProcessorContext] = useState<FormProcessorContextProps>({
@@ -0,0 +1,225 @@
1
+ import { EncounterFormProcessor } from './encounter-form-processor';
2
+ import { type FormSchema } from '../../types';
3
+
4
+ describe('EncounterFormProcessor', () => {
5
+ describe('prepareFormSchema - validateCalculateExpressions', () => {
6
+ let processor: EncounterFormProcessor;
7
+ let consoleSpy: jest.SpyInstance;
8
+
9
+ beforeEach(() => {
10
+ processor = new EncounterFormProcessor(null);
11
+ consoleSpy = jest.spyOn(console, 'error').mockImplementation();
12
+ });
13
+
14
+ afterEach(() => {
15
+ consoleSpy.mockRestore();
16
+ });
17
+
18
+ it('should warn when a calculateExpression contains a quoted string that matches a field ID', () => {
19
+ const schema: FormSchema = {
20
+ name: 'Test Form',
21
+ pages: [
22
+ {
23
+ label: 'Page 1',
24
+ sections: [
25
+ {
26
+ label: 'Section 1',
27
+ isExpanded: 'true',
28
+ questions: [
29
+ {
30
+ label: 'Last Menstrual Period',
31
+ type: 'obs',
32
+ id: 'lmp',
33
+ questionOptions: {
34
+ rendering: 'date',
35
+ concept: 'test-concept',
36
+ },
37
+ },
38
+ {
39
+ label: 'Expected Date of Delivery',
40
+ type: 'obs',
41
+ id: 'edd',
42
+ questionOptions: {
43
+ rendering: 'date',
44
+ concept: 'test-concept',
45
+ calculate: {
46
+ calculateExpression: "calcEDD('lmp')",
47
+ },
48
+ },
49
+ },
50
+ ],
51
+ },
52
+ ],
53
+ },
54
+ ],
55
+ processor: 'EncounterFormProcessor',
56
+ encounterType: 'test-encounter-type',
57
+ referencedForms: [],
58
+ uuid: 'test-form-uuid',
59
+ } as unknown as FormSchema;
60
+
61
+ processor.prepareFormSchema(schema);
62
+
63
+ expect(consoleSpy).toHaveBeenCalledWith(
64
+ expect.stringContaining("incorrectly quotes the field ID 'lmp' as a string"),
65
+ );
66
+ });
67
+
68
+ it('should not warn when a calculateExpression uses bare variable references', () => {
69
+ const schema: FormSchema = {
70
+ name: 'Test Form',
71
+ pages: [
72
+ {
73
+ label: 'Page 1',
74
+ sections: [
75
+ {
76
+ label: 'Section 1',
77
+ isExpanded: 'true',
78
+ questions: [
79
+ {
80
+ label: 'Last Menstrual Period',
81
+ type: 'obs',
82
+ id: 'lmp',
83
+ questionOptions: {
84
+ rendering: 'date',
85
+ concept: 'test-concept',
86
+ },
87
+ },
88
+ {
89
+ label: 'Expected Date of Delivery',
90
+ type: 'obs',
91
+ id: 'edd',
92
+ questionOptions: {
93
+ rendering: 'date',
94
+ concept: 'test-concept',
95
+ calculate: {
96
+ calculateExpression: 'calcEDD(lmp)',
97
+ },
98
+ },
99
+ },
100
+ ],
101
+ },
102
+ ],
103
+ },
104
+ ],
105
+ processor: 'EncounterFormProcessor',
106
+ encounterType: 'test-encounter-type',
107
+ referencedForms: [],
108
+ uuid: 'test-form-uuid',
109
+ } as unknown as FormSchema;
110
+
111
+ processor.prepareFormSchema(schema);
112
+
113
+ expect(consoleSpy).not.toHaveBeenCalled();
114
+ });
115
+
116
+ it('should not warn when a quoted string does not match any field ID', () => {
117
+ const schema: FormSchema = {
118
+ name: 'Test Form',
119
+ pages: [
120
+ {
121
+ label: 'Page 1',
122
+ sections: [
123
+ {
124
+ label: 'Section 1',
125
+ isExpanded: 'true',
126
+ questions: [
127
+ {
128
+ label: 'Duration',
129
+ type: 'obs',
130
+ id: 'duration',
131
+ questionOptions: {
132
+ rendering: 'number',
133
+ concept: 'test-concept',
134
+ calculate: {
135
+ calculateExpression: "calcTimeDifference(onsetDate, 'd')",
136
+ },
137
+ },
138
+ },
139
+ ],
140
+ },
141
+ ],
142
+ },
143
+ ],
144
+ processor: 'EncounterFormProcessor',
145
+ encounterType: 'test-encounter-type',
146
+ referencedForms: [],
147
+ uuid: 'test-form-uuid',
148
+ } as unknown as FormSchema;
149
+
150
+ processor.prepareFormSchema(schema);
151
+
152
+ // 'd' is not a field ID, so no warning should be issued
153
+ expect(consoleSpy).not.toHaveBeenCalled();
154
+ });
155
+
156
+ it('should warn for nested questions in obsGroups', () => {
157
+ const schema: FormSchema = {
158
+ name: 'Test Form',
159
+ pages: [
160
+ {
161
+ label: 'Page 1',
162
+ sections: [
163
+ {
164
+ label: 'Section 1',
165
+ isExpanded: 'true',
166
+ questions: [
167
+ {
168
+ label: 'Height',
169
+ type: 'obs',
170
+ id: 'height',
171
+ questionOptions: {
172
+ rendering: 'number',
173
+ concept: 'test-concept',
174
+ },
175
+ },
176
+ {
177
+ label: 'Weight',
178
+ type: 'obs',
179
+ id: 'weight',
180
+ questionOptions: {
181
+ rendering: 'number',
182
+ concept: 'test-concept',
183
+ },
184
+ },
185
+ {
186
+ label: 'Vitals Group',
187
+ type: 'obsGroup',
188
+ id: 'vitalsGroup',
189
+ questionOptions: {
190
+ rendering: 'group',
191
+ concept: 'test-concept',
192
+ },
193
+ questions: [
194
+ {
195
+ label: 'BMI',
196
+ type: 'obs',
197
+ id: 'bmi',
198
+ questionOptions: {
199
+ rendering: 'number',
200
+ concept: 'test-concept',
201
+ calculate: {
202
+ calculateExpression: "calcBMI('height', 'weight')",
203
+ },
204
+ },
205
+ },
206
+ ],
207
+ },
208
+ ],
209
+ },
210
+ ],
211
+ },
212
+ ],
213
+ processor: 'EncounterFormProcessor',
214
+ encounterType: 'test-encounter-type',
215
+ referencedForms: [],
216
+ uuid: 'test-form-uuid',
217
+ } as unknown as FormSchema;
218
+
219
+ processor.prepareFormSchema(schema);
220
+
221
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("incorrectly quotes the field ID 'height' as a string"));
222
+ expect(consoleSpy).toHaveBeenCalledWith(expect.stringContaining("incorrectly quotes the field ID 'weight' as a string"));
223
+ });
224
+ });
225
+ });
@@ -81,6 +81,8 @@ const contextInitializableTypes = [
81
81
 
82
82
  export class EncounterFormProcessor extends FormProcessor {
83
83
  prepareFormSchema(schema: FormSchema) {
84
+ const allFieldIds = new Set<string>();
85
+
84
86
  schema.pages.forEach((page) => {
85
87
  page.sections.forEach((section) => {
86
88
  section.questions.forEach((question) => {
@@ -90,6 +92,11 @@ export class EncounterFormProcessor extends FormProcessor {
90
92
  });
91
93
 
92
94
  function prepareFormField(field: FormField, section: FormSection, page: FormPage, schema: FormSchema) {
95
+ // Collect field ID
96
+ if (field.id) {
97
+ allFieldIds.add(field.id);
98
+ }
99
+
93
100
  // inherit inlineRendering and readonly from parent section and page if not set
94
101
  field.inlineRendering =
95
102
  field.inlineRendering ?? section.inlineRendering ?? page.inlineRendering ?? schema.inlineRendering;
@@ -106,6 +113,9 @@ export class EncounterFormProcessor extends FormProcessor {
106
113
  }
107
114
  }
108
115
 
116
+ // Validate calculate expressions for common mistakes
117
+ validateCalculateExpressions(schema, allFieldIds);
118
+
109
119
  return schema;
110
120
  }
111
121
 
@@ -368,3 +378,43 @@ async function evaluateCalculateExpression(
368
378
  values[field.id] = value;
369
379
  }
370
380
  }
381
+
382
+ /**
383
+ * Validates calculate expressions to warn about common mistakes.
384
+ * Specifically, checks if string literals in expressions match field IDs,
385
+ * which usually indicates the user should use bare variable references instead.
386
+ *
387
+ * For example: calcEDD('lmp') should be calcEDD(lmp)
388
+ */
389
+ function validateCalculateExpressions(schema: FormSchema, allFieldIds: Set<string>) {
390
+ const stringLiteralPattern = /(['"])([a-zA-Z_][a-zA-Z0-9_]*)\1/g;
391
+
392
+ function checkExpression(expression: string, fieldId: string) {
393
+ for (const match of expression.matchAll(stringLiteralPattern)) {
394
+ const quotedValue = match[2];
395
+ if (allFieldIds.has(quotedValue)) {
396
+ console.error(
397
+ `The calculateExpression for the field '${fieldId}' incorrectly quotes the field ID '${quotedValue}' as a string. ` +
398
+ `Field IDs must be referenced as variables without quotes to access their values. ` +
399
+ `Remove the quotes: use ${quotedValue} instead of '${quotedValue}'.`,
400
+ );
401
+ }
402
+ }
403
+ }
404
+
405
+ function processField(field: FormField) {
406
+ if (field.questionOptions?.calculate?.calculateExpression) {
407
+ checkExpression(field.questionOptions.calculate.calculateExpression, field.id);
408
+ }
409
+ // Process nested questions (for obsGroups)
410
+ if (field.questions) {
411
+ field.questions.forEach(processField);
412
+ }
413
+ }
414
+
415
+ schema.pages.forEach((page) => {
416
+ page.sections.forEach((section) => {
417
+ section.questions.forEach(processField);
418
+ });
419
+ });
420
+ }
@@ -104,6 +104,68 @@ describe('CommonExpressionHelpers', () => {
104
104
  });
105
105
  });
106
106
 
107
+ describe('isDateAfterSimple', () => {
108
+ it('should return true if the left date is after the right date', () => {
109
+ const left = new Date('2021-12-31');
110
+ const right = '2021-01-01';
111
+ expect(helpers.isDateAfterSimple(left, right)).toBe(true);
112
+ });
113
+
114
+ it('should return false if the left date is not after the right date', () => {
115
+ const left = new Date('2021-01-01');
116
+ const right = '2021-12-31';
117
+ expect(helpers.isDateAfterSimple(left, right)).toBe(false);
118
+ });
119
+
120
+ it('should accept a Date object as the right parameter', () => {
121
+ const left = new Date('2021-12-31');
122
+ const right = new Date('2021-01-01');
123
+ expect(helpers.isDateAfterSimple(left, right)).toBe(true);
124
+ });
125
+
126
+ it('should use custom format when provided', () => {
127
+ const left = new Date('2021-12-31');
128
+ const right = '31/01/2021';
129
+ expect(helpers.isDateAfterSimple(left, right, 'DD/MM/YYYY')).toBe(true);
130
+ });
131
+ });
132
+
133
+ describe('addWeeksToDate', () => {
134
+ it('should add weeks to a date correctly', () => {
135
+ const date = new Date('2021-01-01');
136
+ const result = helpers.addWeeksToDate(date, 2);
137
+ expect(result).toEqual(new Date('2021-01-15'));
138
+ });
139
+
140
+ it('should not mutate the original date', () => {
141
+ const date = new Date('2021-01-01');
142
+ const originalTime = date.getTime();
143
+ helpers.addWeeksToDate(date, 2);
144
+ expect(date.getTime()).toBe(originalTime);
145
+ });
146
+ });
147
+
148
+ describe('addDaysToDate', () => {
149
+ it('should add days to a date correctly', () => {
150
+ const date = new Date('2021-01-01');
151
+ const result = helpers.addDaysToDate(date, 10);
152
+ expect(result).toEqual(new Date('2021-01-11'));
153
+ });
154
+
155
+ it('should not mutate the original date', () => {
156
+ const date = new Date('2021-01-01');
157
+ const originalTime = date.getTime();
158
+ helpers.addDaysToDate(date, 10);
159
+ expect(date.getTime()).toBe(originalTime);
160
+ });
161
+
162
+ it('should handle negative days', () => {
163
+ const date = new Date('2021-01-15');
164
+ const result = helpers.addDaysToDate(date, -5);
165
+ expect(result).toEqual(new Date('2021-01-10'));
166
+ });
167
+ });
168
+
107
169
  describe('useFieldValue', () => {
108
170
  it('should return the field value if the key exists', () => {
109
171
  helpers.allFieldValues = { question1: 'value1' };
@@ -155,6 +217,23 @@ describe('CommonExpressionHelpers', () => {
155
217
  });
156
218
  });
157
219
 
220
+ describe('calcBSA', () => {
221
+ it('should return the correct BSA value using Mosteller formula', () => {
222
+ // BSA = sqrt((height * weight) / 3600)
223
+ // For height=180cm, weight=75kg: sqrt((180 * 75) / 3600) = sqrt(3.75) ≈ 1.94
224
+ const height = 180;
225
+ const weight = 75;
226
+ expect(helpers.calcBSA(height, weight)).toBeCloseTo(1.94, 2);
227
+ });
228
+
229
+ it('should return null if height or weight is not provided', () => {
230
+ expect(helpers.calcBSA(null, 75)).toBe(null);
231
+ expect(helpers.calcBSA(180, null)).toBe(null);
232
+ expect(helpers.calcBSA(0, 75)).toBe(null);
233
+ expect(helpers.calcBSA(180, 0)).toBe(null);
234
+ });
235
+ });
236
+
158
237
  describe('calcEDD', () => {
159
238
  it('should return the expected date of delivery', () => {
160
239
  const lmp = new Date('2021-01-01');
@@ -349,8 +428,8 @@ describe('CommonExpressionHelpers', () => {
349
428
  expect(helpers.calcTimeDifference(obsDate, 'y')).toBe(1);
350
429
  });
351
430
 
352
- it('should return "0" if obsDate is not provided', () => {
353
- expect(helpers.calcTimeDifference(null, 'd')).toBe('0');
431
+ it('should return 0 if obsDate is not provided', () => {
432
+ expect(helpers.calcTimeDifference(null, 'd')).toBe(0);
354
433
  });
355
434
  });
356
435
 
@@ -1,6 +1,6 @@
1
1
  import dayjs from 'dayjs';
2
- import duration from 'dayjs/plugin/duration';
3
- dayjs.extend(duration);
2
+ import customParseFormat from 'dayjs/plugin/customParseFormat';
3
+ dayjs.extend(customParseFormat);
4
4
  import findIndex from 'lodash/findIndex';
5
5
  import filter from 'lodash/filter';
6
6
  import first from 'lodash/first';
@@ -28,65 +28,168 @@ export class CommonExpressionHelpers {
28
28
  this.patient = patient;
29
29
  }
30
30
 
31
+ /**
32
+ * Shared helper for Z-score calculations. Finds the standard deviation (SD) value
33
+ * by comparing a measurement against WHO growth reference data.
34
+ * @param refSectionObject - Reference data object with SD columns (e.g., '-3SD', '-2SD', etc.)
35
+ * @param measurementValue - The patient's measurement to compare against reference values
36
+ * @returns The SD score as a string (e.g., '-2', '0', '1') or null if no reference data
37
+ */
38
+ private calculateZScoreFromRef = (
39
+ refSectionObject: Record<string, any> | undefined,
40
+ measurementValue: number,
41
+ ): string | null => {
42
+ if (!refSectionObject) {
43
+ console.warn('Z-score calculation: No reference data object provided');
44
+ return null;
45
+ }
46
+
47
+ const refObjectKeys = Object.keys(refSectionObject);
48
+ const refObjectValues = refObjectKeys.map((key) => refSectionObject[key]);
49
+ const minimumValue = refObjectValues[1];
50
+ const minReferencePoint: number[] = [];
51
+
52
+ if (measurementValue < minimumValue) {
53
+ minReferencePoint.push(minimumValue);
54
+ } else {
55
+ forEach(refObjectValues, (value) => {
56
+ if (value <= measurementValue) {
57
+ minReferencePoint.push(value);
58
+ }
59
+ });
60
+ }
61
+
62
+ const lastReferenceValue = last(minReferencePoint);
63
+ const lastValueIndex = findIndex(refObjectValues, (o) => o === lastReferenceValue);
64
+ const SDValue = refObjectKeys[lastValueIndex];
65
+ let formattedSDValue = SDValue?.replace('SD', '');
66
+
67
+ if (formattedSDValue?.includes('neg')) {
68
+ formattedSDValue = '-' + formattedSDValue.substring(0, 1);
69
+ }
70
+
71
+ if (formattedSDValue === 'S' || formattedSDValue === 'L' || formattedSDValue === 'M' || formattedSDValue === '-5') {
72
+ formattedSDValue = '-4';
73
+ }
74
+
75
+ return formattedSDValue ?? null;
76
+ };
77
+
78
+ /**
79
+ * Returns the current date and time.
80
+ * @returns A new Date object representing the current moment
81
+ */
31
82
  today = () => {
32
83
  return new Date();
33
84
  };
34
85
 
86
+ /**
87
+ * Checks if a collection contains a specific value.
88
+ * @param collection - The array to search in
89
+ * @param value - The value to search for
90
+ * @returns true if the collection contains the value, false otherwise
91
+ */
35
92
  includes = <T = any>(collection: T[], value: T) => {
36
93
  return collection?.includes(value);
37
94
  };
38
95
 
39
- isDateBefore = (left: Date, right: string | Date, format?: string) => {
40
- let otherDate: any = right;
41
- if (typeof right == 'string') {
42
- otherDate = format ? dayjs(right, format, true).toDate() : dayjs(right, 'YYYY-MM-DD', true).toDate();
43
- }
96
+ /**
97
+ * Checks if the left date is before the right date.
98
+ * @param left - The date to check
99
+ * @param right - The date to compare against (can be a Date object or string)
100
+ * @param format - Optional format string for parsing right date (defaults to 'YYYY-MM-DD')
101
+ * @returns true if left is before right
102
+ */
103
+ isDateBefore = (left: Date, right: string | Date, format?: string): boolean => {
104
+ const otherDate: Date = right instanceof Date ? right : (format ? dayjs(right, format, true).toDate() : dayjs(right, 'YYYY-MM-DD', true).toDate());
44
105
  return left?.getTime() < otherDate.getTime();
45
106
  };
46
107
 
47
- isDateAfter = (selectedDate: Date, baseDate: Date, duration: number, timePeriod: string) => {
48
- let calculatedDate = new Date(0);
49
- selectedDate = dayjs(selectedDate, 'YYYY-MM-DD', true).toDate();
50
- baseDate = dayjs(baseDate, 'YYYY-MM-DD', true).toDate();
108
+ /**
109
+ * Checks if selectedDate is on or after baseDate plus a duration offset.
110
+ * @param selectedDate - The date to check
111
+ * @param baseDate - The base date to add the duration to
112
+ * @param duration - The number of time units to add to baseDate
113
+ * @param timePeriod - The time unit: 'days', 'weeks', 'months', or 'years'
114
+ * @returns true if selectedDate >= (baseDate + duration)
115
+ */
116
+ isDateAfter = (selectedDate: Date, baseDate: Date, duration: number, timePeriod: 'days' | 'weeks' | 'months' | 'years'): boolean => {
117
+ const parsedBaseDate = dayjs(baseDate);
51
118
 
119
+ let calculatedDate: Date;
52
120
  switch (timePeriod) {
53
121
  case 'months':
54
- calculatedDate = new Date(baseDate.setMonth(baseDate.getMonth() + duration));
122
+ calculatedDate = parsedBaseDate.add(duration, 'month').toDate();
55
123
  break;
56
124
  case 'weeks':
57
- calculatedDate = this.addWeeksToDate(baseDate, duration);
125
+ calculatedDate = parsedBaseDate.add(duration, 'week').toDate();
58
126
  break;
59
127
  case 'days':
60
- calculatedDate = this.addDaysToDate(baseDate, duration);
128
+ calculatedDate = parsedBaseDate.add(duration, 'day').toDate();
61
129
  break;
62
130
  case 'years':
63
- calculatedDate = new Date(baseDate.setFullYear(baseDate.getFullYear() + duration));
131
+ calculatedDate = parsedBaseDate.add(duration, 'year').toDate();
64
132
  break;
65
133
  default:
66
- break;
134
+ calculatedDate = new Date(0);
67
135
  }
68
136
  return selectedDate.getTime() >= calculatedDate.getTime();
69
137
  };
70
138
 
71
- addWeeksToDate = (date: Date, weeks: number) => {
72
- date.setDate(date.getDate() + 7 * weeks);
73
-
74
- return date;
139
+ /**
140
+ * Adds weeks to a date without mutating the original.
141
+ * @param date - The starting date
142
+ * @param weeks - Number of weeks to add
143
+ * @returns A new Date object with the weeks added
144
+ */
145
+ addWeeksToDate = (date: Date, weeks: number): Date => {
146
+ return dayjs(date).add(weeks, 'week').toDate();
75
147
  };
76
148
 
149
+ /**
150
+ * Adds days to a date without mutating the original.
151
+ * @param date - The starting date
152
+ * @param days - Number of days to add
153
+ * @returns A new Date object with the days added
154
+ */
77
155
  addDaysToDate = (date: Date, days: number): Date => {
78
156
  return dayjs(date).add(days, 'day').toDate();
79
157
  };
80
158
 
159
+ /**
160
+ * Simple date comparison - checks if left date is strictly after right date.
161
+ * Mirrors the API of isDateBefore for consistency.
162
+ * @param left - The date to check
163
+ * @param right - The date to compare against (string or Date)
164
+ * @param format - Optional format string for parsing right date (defaults to 'YYYY-MM-DD')
165
+ * @returns true if left is after right
166
+ */
167
+ isDateAfterSimple = (left: Date, right: string | Date, format?: string): boolean => {
168
+ const otherDate: Date = right instanceof Date ? right : (format ? dayjs(right, format, true).toDate() : dayjs(right, 'YYYY-MM-DD', true).toDate());
169
+ return left?.getTime() > otherDate.getTime();
170
+ };
171
+
172
+ /**
173
+ * Retrieves the current value of another form field and registers a dependency.
174
+ * When the referenced field changes, expressions using this helper will be re-evaluated.
175
+ * @param questionId - The ID of the field to get the value from
176
+ * @returns The field's current value, or null if not found/set
177
+ */
81
178
  useFieldValue = (questionId: string) => {
82
179
  const targetField = this.allFields.find((field) => field.id === questionId);
83
180
  if (targetField) {
84
- // track field dependency
85
181
  registerDependency(this.node, targetField);
86
182
  }
87
183
  return this.allFieldValues[questionId] ?? null;
88
184
  };
89
185
 
186
+ /**
187
+ * Tests if a value does NOT match a regular expression pattern.
188
+ * Returns true for empty/null/undefined values (treated as non-matching).
189
+ * @param regexString - The regular expression pattern to test against
190
+ * @param val - The value to test
191
+ * @returns true if the value does not match the pattern or is empty/null/undefined
192
+ */
90
193
  doesNotMatchExpression = (regexString: string, val: string | null | undefined): boolean => {
91
194
  if (!val || ['undefined', 'null', ''].includes(val.toString())) {
92
195
  return true;
@@ -96,27 +199,41 @@ export class CommonExpressionHelpers {
96
199
  return !pattern.test(val);
97
200
  };
98
201
 
202
+ /**
203
+ * Calculates Body Mass Index (BMI) from height and weight.
204
+ * Formula: weight (kg) / height (m)²
205
+ * @param height - Height in centimeters
206
+ * @param weight - Weight in kilograms
207
+ * @returns BMI rounded to 1 decimal place, or null if inputs are missing
208
+ */
99
209
  calcBMI = (height: number, weight: number) => {
100
- let r: string;
101
- if (height && weight) {
102
- r = (weight / (((height / 100) * height) / 100)).toFixed(1);
210
+ if (!height || !weight) {
211
+ return null;
103
212
  }
104
- return r ? parseFloat(r) : null;
213
+ const heightInMeters = height / 100;
214
+ const bmi = (weight / (heightInMeters * heightInMeters)).toFixed(1);
215
+ return parseFloat(bmi);
105
216
  };
106
217
 
107
218
  /**
108
- * Expected date of delivery
109
- * @param lmpQuestionId
110
- * @returns
219
+ * Calculates the Expected Date of Delivery (EDD) from the last menstrual period.
220
+ * Uses Naegele's rule: LMP + 280 days (40 weeks).
221
+ * @param lmp - Last menstrual period date
222
+ * @returns Expected delivery date, or null if lmp is not provided
111
223
  */
112
- calcEDD = (lmp: Date) => {
113
- let resultEdd = {};
114
- if (lmp) {
115
- resultEdd = new Date(lmp.getTime() + 280 * 24 * 60 * 60 * 1000);
224
+ calcEDD = (lmp: Date): Date | null => {
225
+ if (!lmp) {
226
+ return null;
116
227
  }
117
- return lmp ? resultEdd : null;
228
+ return new Date(lmp.getTime() + 280 * 24 * 60 * 60 * 1000);
118
229
  };
119
230
 
231
+ /**
232
+ * Calculates the number of complete months a patient has been on ART.
233
+ * @param artStartDate - The date when ART treatment started
234
+ * @returns Number of months on ART, 0 if less than 30 days, or null if no start date
235
+ * @throws Error if artStartDate is not a valid Date object
236
+ */
120
237
  calcMonthsOnART = (artStartDate: Date) => {
121
238
  if (artStartDate == null) {
122
239
  return null;
@@ -136,6 +253,18 @@ export class CommonExpressionHelpers {
136
253
  return dayjs(today).diff(artStartDate, 'month');
137
254
  };
138
255
 
256
+ /**
257
+ * Determines viral load suppression status based on the viral load count.
258
+ *
259
+ * WARNING: This function returns hardcoded concept UUIDs that are specific to certain
260
+ * OpenMRS implementations. These UUIDs may not exist or may differ in your system.
261
+ * Consider using form-level configuration or concept mappings instead.
262
+ *
263
+ * @param viralLoadCount - The viral load count (copies/mL)
264
+ * @returns Concept UUID based on suppression threshold (>50 copies/mL), or null if no count
265
+ * @deprecated Consider implementing viral load status logic in form expressions with
266
+ * configurable concept UUIDs instead of using this hardcoded helper.
267
+ */
139
268
  calcViralLoadStatus = (viralLoadCount: number) => {
140
269
  let resultViralLoadStatus: string;
141
270
  if (viralLoadCount) {
@@ -148,46 +277,76 @@ export class CommonExpressionHelpers {
148
277
  return resultViralLoadStatus ?? null;
149
278
  };
150
279
 
151
- calcNextVisitDate = (followupDate, arvDispensedInDays) => {
152
- let resultNextVisitDate: Date;
280
+ /**
281
+ * Calculates the next clinic visit date based on ARV dispensing duration.
282
+ * @param followupDate - The current follow-up/encounter date
283
+ * @param arvDispensedInDays - Number of days of ARV medication dispensed
284
+ * @returns The next visit date (followupDate + arvDispensedInDays), or null if inputs are missing
285
+ */
286
+ calcNextVisitDate = (followupDate: Date, arvDispensedInDays: number): Date | null => {
153
287
  if (followupDate && arvDispensedInDays) {
154
- resultNextVisitDate = new Date(followupDate.getTime() + arvDispensedInDays * 24 * 60 * 60 * 1000);
288
+ return new Date(followupDate.getTime() + arvDispensedInDays * 24 * 60 * 60 * 1000);
155
289
  }
156
- return resultNextVisitDate ?? null;
290
+ return null;
157
291
  };
158
292
 
159
- calcTreatmentEndDate = (followupDate: Date, arvDispensedInDays: number, patientStatus: string) => {
160
- let resultTreatmentEndDate = {};
161
- let extraDaysAdded = 30 + arvDispensedInDays;
162
- if (followupDate && arvDispensedInDays && patientStatus == '160429AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') {
163
- resultTreatmentEndDate = new Date(followupDate.getTime() + extraDaysAdded * 24 * 60 * 60 * 1000);
293
+ /**
294
+ * Calculates the treatment end date for patients on ART.
295
+ * Adds a 30-day grace period plus the ARV dispensing duration.
296
+ *
297
+ * WARNING: This function checks against a hardcoded concept UUID (160429AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA)
298
+ * for 'Currently in Treatment' status. This UUID may not exist or may differ in your system.
299
+ * Consider implementing this logic in form expressions with configurable concept references instead.
300
+ *
301
+ * @param followupDate - The current follow-up/encounter date
302
+ * @param arvDispensedInDays - Number of days of ARV medication dispensed
303
+ * @param patientStatus - The patient's treatment status UUID (must match hardcoded UUID)
304
+ * @returns Treatment end date (followupDate + 30 + arvDispensedInDays), or null if conditions not met
305
+ * @deprecated Consider implementing treatment end date logic in form expressions with
306
+ * configurable concept UUIDs instead of using this hardcoded helper.
307
+ */
308
+ calcTreatmentEndDate = (followupDate: Date, arvDispensedInDays: number, patientStatus: string): Date | null => {
309
+ if (!followupDate || !arvDispensedInDays || patientStatus !== '160429AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA') {
310
+ return null;
164
311
  }
165
- return followupDate && arvDispensedInDays && patientStatus == '160429AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA'
166
- ? resultTreatmentEndDate
167
- : null;
312
+ const extraDaysAdded = 30 + arvDispensedInDays;
313
+ return new Date(followupDate.getTime() + extraDaysAdded * 24 * 60 * 60 * 1000);
168
314
  };
169
315
 
170
- calcAgeBasedOnDate = (dateValue?: ConstructorParameters<typeof Date>[0] | null) => {
171
- let targetYear = null;
172
- if (dateValue) {
173
- targetYear = new Date(dateValue).getFullYear();
174
- } else {
175
- targetYear = new Date().getFullYear();
176
- }
177
- let birthDate = new Date(this.patient.birthDate).getFullYear();
178
- let calculatedYear = targetYear - birthDate;
179
- return calculatedYear;
316
+ /**
317
+ * Calculates the patient's age in years based on a reference date.
318
+ * Note: Uses year-only calculation (ignores month/day), so a patient born in December 1990
319
+ * will be considered 31 years old on January 1, 2021.
320
+ * @param dateValue - The reference date to calculate age at (defaults to today if not provided)
321
+ * @returns Age in years (year difference only, not precise age)
322
+ */
323
+ calcAgeBasedOnDate = (dateValue?: ConstructorParameters<typeof Date>[0] | null): number => {
324
+ const targetYear = dateValue ? new Date(dateValue).getFullYear() : new Date().getFullYear();
325
+ const birthYear = new Date(this.patient.birthDate).getFullYear();
326
+ return targetYear - birthYear;
180
327
  };
181
328
 
182
- //Ampath Helper Functions
183
- calcBSA = (height: number, weight: number) => {
184
- let result: string;
185
- if (height && weight) {
186
- result = Math.sqrt((height * weight) / 3600).toFixed(2);
329
+ /**
330
+ * Calculates Body Surface Area (BSA) using the Mosteller formula.
331
+ * Formula: √((height × weight) / 3600)
332
+ * @param height - Height in centimeters
333
+ * @param weight - Weight in kilograms
334
+ * @returns BSA in m² rounded to 2 decimal places, or null if inputs are missing
335
+ */
336
+ calcBSA = (height: number, weight: number): number | null => {
337
+ if (!height || !weight) {
338
+ return null;
187
339
  }
188
- return result ? parseFloat(result) : null;
340
+ return parseFloat(Math.sqrt((height * weight) / 3600).toFixed(2));
189
341
  };
190
342
 
343
+ /**
344
+ * Checks if an array contains ALL of the specified members.
345
+ * @param array - The array to search in
346
+ * @param members - A single value or array of values that must all be present
347
+ * @returns true if array contains all members, false otherwise.
348
+ * Returns true for empty members array. Returns false for null/non-array input.
349
+ */
191
350
  arrayContains = <T = any>(array: T[], members: T[] | T) => {
192
351
  if (!array || !Array.isArray(array)) {
193
352
  return false;
@@ -214,6 +373,13 @@ export class CommonExpressionHelpers {
214
373
  return true;
215
374
  };
216
375
 
376
+ /**
377
+ * Checks if an array contains ANY of the specified members.
378
+ * @param array - The array to search in
379
+ * @param members - An array of values where at least one must be present
380
+ * @returns true if array contains at least one member, false otherwise.
381
+ * Returns true for empty members array. Returns false for null/non-array input.
382
+ */
217
383
  arrayContainsAny = <T = any>(array: T[], members: T[]) => {
218
384
  if (!array || !Array.isArray(array)) {
219
385
  return false;
@@ -240,14 +406,27 @@ export class CommonExpressionHelpers {
240
406
  return false;
241
407
  };
242
408
 
409
+ /**
410
+ * Parses a date string into a Date object using OpenMRS framework parsing.
411
+ * @param dateString - The date string to parse
412
+ * @returns A Date object
413
+ */
243
414
  parseDate = (dateString: string) => {
244
415
  return parseDate(dateString);
245
416
  };
246
417
 
418
+ /**
419
+ * Formats a date value into a string.
420
+ * @param value - The date to format (Date object or value that can be converted to Date)
421
+ * @param format - Optional dayjs format string (e.g., 'YYYY-MM-DD', 'DD/MM/YYYY').
422
+ * If not provided, uses OpenMRS default locale format.
423
+ * @returns Formatted date string
424
+ * @throws Error if the value cannot be converted to a valid date
425
+ */
247
426
  formatDate = (value: ConstructorParameters<typeof Date>[0], format?: string) => {
248
427
  if (!(value instanceof Date)) {
249
428
  value = new Date(value);
250
- if (value === null || value === undefined || isNaN(value.getTime())) {
429
+ if (isNaN(value.getTime())) {
251
430
  throw new Error('DateFormatException: value passed is not a valid date');
252
431
  }
253
432
  }
@@ -257,6 +436,12 @@ export class CommonExpressionHelpers {
257
436
  return formatDate(value);
258
437
  };
259
438
 
439
+ /**
440
+ * Extracts values for a specific key from an array of objects (typically repeating group data).
441
+ * @param key - The property key to extract from each object
442
+ * @param array - Array of objects to extract values from
443
+ * @returns Array of values for the specified key
444
+ */
260
445
  extractRepeatingGroupValues = (key: string | number | symbol, array: Record<string | number | symbol, unknown>[]) => {
261
446
  const values = array.map(function (item) {
262
447
  return item[key];
@@ -266,20 +451,14 @@ export class CommonExpressionHelpers {
266
451
 
267
452
  /**
268
453
  * Calculates the gravida (total number of pregnancies) based on term pregnancies and abortions/miscarriages.
269
- *
270
- * @param {number|string} parityTerm - The number of term pregnancies.
271
- * @param {number|string} parityAbortion - The number of abortions (including miscarriages).
272
- * @returns {number} The total number of pregnancies (gravida).
273
- * @throws {Error} If either input is not a valid number.
274
- *
275
- * @example
276
- * const gravida = calcGravida(2, 1);
277
- * console.log(gravida); // Output: 3
454
+ * @param parityTerm - The number of term pregnancies (can be number or numeric string)
455
+ * @param parityAbortion - The number of abortions including miscarriages (can be number or numeric string)
456
+ * @returns The total number of pregnancies (gravida)
457
+ * @throws Error if either input is not a valid number
278
458
  */
279
-
280
- calcGravida = (parityTerm, parityAbortion) => {
281
- const term = parseInt(parityTerm, 10);
282
- const abortion = parseInt(parityAbortion, 10);
459
+ calcGravida = (parityTerm: number | string, parityAbortion: number | string): number => {
460
+ const term = typeof parityTerm === 'number' ? parityTerm : parseInt(parityTerm, 10);
461
+ const abortion = typeof parityAbortion === 'number' ? parityAbortion : parseInt(parityAbortion, 10);
283
462
 
284
463
  if (!Number.isInteger(term) || !Number.isInteger(abortion)) {
285
464
  throw new Error('Both inputs must be valid numbers.');
@@ -288,181 +467,106 @@ export class CommonExpressionHelpers {
288
467
  return term + abortion;
289
468
  };
290
469
 
291
- calcWeightForHeightZscore = (height, weight) => {
470
+ /**
471
+ * Calculates the Weight-for-Height Z-score for pediatric patients using WHO growth standards.
472
+ * Used to assess acute malnutrition (wasting).
473
+ * @param height - Patient's height/length in centimeters (valid range: 45-110 cm)
474
+ * @param weight - Patient's weight in kilograms
475
+ * @returns Z-score as a string (e.g., '-2', '0', '1'), '-4' if out of range, or null if inputs missing
476
+ */
477
+ calcWeightForHeightZscore = (height: number, weight: number): string | null => {
478
+ if (!height || !weight) {
479
+ return null;
480
+ }
481
+
292
482
  const birthDate = new Date(this.patient.birthDate);
293
483
  const weightForHeightRef = getZRefByGenderAndAge(this.patient.sex, birthDate, new Date()).weightForHeightRef;
294
- let refSection;
295
- let formattedSDValue;
296
- if (height && weight) {
297
- height = parseFloat(height).toFixed(1);
298
- }
484
+
485
+ const formattedHeight = height.toFixed(1);
299
486
  const standardHeightMin = 45;
300
487
  const standardMaxHeight = 110;
301
- if (height < standardHeightMin || height > standardMaxHeight) {
302
- formattedSDValue = -4;
303
- } else {
304
- refSection = filter(weightForHeightRef, (refObject) => {
305
- return parseFloat(refObject['Length']).toFixed(1) === height;
306
- });
307
- }
308
488
 
309
- const refSectionObject = first(refSection);
310
- if (refSectionObject) {
311
- const refObjectValues = Object.keys(refSectionObject)
312
- .map((key) => refSectionObject[key])
313
- .map((x) => x);
314
- const refObjectKeys = Object.keys(refSectionObject);
315
- const minimumValue = refObjectValues[1];
316
- const minReferencePoint = [];
317
- if (weight < minimumValue) {
318
- minReferencePoint.push(minimumValue);
319
- } else {
320
- forEach(refObjectValues, (value) => {
321
- if (value <= weight) {
322
- minReferencePoint.push(value);
323
- }
324
- });
325
- }
326
- const lastReferenceValue = last(minReferencePoint);
327
- const lastValueIndex = findIndex(refObjectValues, (o) => {
328
- return o === lastReferenceValue;
329
- });
330
- const SDValue = refObjectKeys[lastValueIndex];
331
- formattedSDValue = SDValue?.replace('SD', '');
332
- if (formattedSDValue.includes('neg')) {
333
- formattedSDValue = formattedSDValue.substring(1, 0);
334
- formattedSDValue = '-' + formattedSDValue;
335
- }
336
- if (
337
- formattedSDValue === 'S' ||
338
- formattedSDValue === 'L' ||
339
- formattedSDValue === 'M' ||
340
- formattedSDValue === '-5'
341
- ) {
342
- formattedSDValue = '-4';
343
- }
489
+ if (parseFloat(formattedHeight) < standardHeightMin || parseFloat(formattedHeight) > standardMaxHeight) {
490
+ return '-4';
344
491
  }
345
492
 
346
- return height && weight ? formattedSDValue : null;
493
+ const refSection = filter(weightForHeightRef, (refObject) => {
494
+ return parseFloat(refObject['Length']).toFixed(1) === formattedHeight;
495
+ });
496
+
497
+ const refSectionObject = first(refSection);
498
+ return this.calculateZScoreFromRef(refSectionObject, weight);
347
499
  };
348
500
 
349
- calcBMIForAgeZscore = (height, weight) => {
501
+ /**
502
+ * Calculates the BMI-for-Age Z-score for pediatric patients using WHO growth standards.
503
+ * Used to assess both undernutrition and overweight/obesity.
504
+ * @param height - Patient's height in centimeters
505
+ * @param weight - Patient's weight in kilograms
506
+ * @returns Z-score as a string (e.g., '-2', '0', '1'), or null if inputs missing
507
+ */
508
+ calcBMIForAgeZscore = (height: number, weight: number): string | null => {
509
+ if (!height || !weight) {
510
+ return null;
511
+ }
512
+
350
513
  const birthDate = new Date(this.patient.birthDate);
351
514
  const bmiForAgeRef = getZRefByGenderAndAge(this.patient.sex, birthDate, new Date()).bmiForAgeRef;
352
- let bmi;
353
- const maxAgeInDays = 1856;
354
- if (height && weight) {
355
- bmi = (weight / (((height / 100) * height) / 100)).toFixed(1);
356
- }
357
- const refSectionObject = first(bmiForAgeRef);
358
- let formattedSDValue;
359
- if (refSectionObject) {
360
- const refObjectValues = Object.keys(refSectionObject)
361
- .map((key) => refSectionObject[key])
362
- .map((x) => x);
363
- const refObjectKeys = Object.keys(refSectionObject);
364
- const minimumValue = refObjectValues[1];
365
- const minReferencePoint = [];
366
- if (bmi < minimumValue) {
367
- minReferencePoint.push(minimumValue);
368
- } else {
369
- forEach(refObjectValues, (value) => {
370
- if (value <= bmi) {
371
- minReferencePoint.push(value);
372
- }
373
- });
374
- }
375
- const lastReferenceValue = last(minReferencePoint);
376
- const lastValueIndex = findIndex(refObjectValues, (o) => {
377
- return o === lastReferenceValue;
378
- });
379
- const SDValue = refObjectKeys[lastValueIndex];
380
- formattedSDValue = SDValue?.replace('SD', '');
381
- if (formattedSDValue.includes('neg')) {
382
- formattedSDValue = formattedSDValue.substring(1, 0);
383
- formattedSDValue = '-' + formattedSDValue;
384
- }
385
515
 
386
- if (
387
- formattedSDValue === 'S' ||
388
- formattedSDValue === 'L' ||
389
- formattedSDValue === 'M' ||
390
- formattedSDValue === '-5'
391
- ) {
392
- formattedSDValue = '-4';
393
- }
394
- }
516
+ const heightInMeters = height / 100;
517
+ const bmi = parseFloat((weight / (heightInMeters * heightInMeters)).toFixed(1));
395
518
 
396
- return bmi && refSectionObject ? formattedSDValue : null;
519
+ const refSectionObject = first(bmiForAgeRef);
520
+ return this.calculateZScoreFromRef(refSectionObject, bmi);
397
521
  };
398
522
 
399
- calcHeightForAgeZscore = (height, weight) => {
523
+ /**
524
+ * Calculates the Height-for-Age Z-score for pediatric patients using WHO growth standards.
525
+ * Used to assess chronic malnutrition (stunting).
526
+ * @param height - Patient's height/length in centimeters
527
+ * @param _weight - Unused parameter kept for backward compatibility
528
+ * @returns Z-score as a string (e.g., '-2', '0', '1'), or null if height is missing
529
+ */
530
+ calcHeightForAgeZscore = (height: number, _weight?: number): string | null => {
531
+ if (!height) {
532
+ return null;
533
+ }
534
+
400
535
  const birthDate = new Date(this.patient.birthDate);
401
536
  const heightForAgeRef = getZRefByGenderAndAge(this.patient.sex, birthDate, new Date()).heightForAgeRef;
402
537
  const refSectionObject = first(heightForAgeRef);
403
- let formattedSDValue;
404
- if (refSectionObject) {
405
- const refObjectValues = Object.keys(refSectionObject)
406
- .map((key) => refSectionObject[key])
407
- .map((x) => x);
408
- const refObjectKeys = Object.keys(refSectionObject);
409
- const minimumValue = refObjectValues[1];
410
- const minReferencePoint = [];
411
- if (height < minimumValue) {
412
- minReferencePoint.push(minimumValue);
413
- } else {
414
- forEach(refObjectValues, (value) => {
415
- if (value <= height) {
416
- minReferencePoint.push(value);
417
- }
418
- });
419
- }
420
- const lastReferenceValue = last(minReferencePoint);
421
- const lastValueIndex = findIndex(refObjectValues, (o) => {
422
- return o === lastReferenceValue;
423
- });
424
- const SDValue = refObjectKeys[lastValueIndex];
425
- formattedSDValue = SDValue?.replace('SD', '');
426
- if (formattedSDValue.includes('neg')) {
427
- formattedSDValue = formattedSDValue.substring(1, 0);
428
- formattedSDValue = '-' + formattedSDValue;
429
- }
430
-
431
- if (
432
- formattedSDValue === 'S' ||
433
- formattedSDValue === 'L' ||
434
- formattedSDValue === 'M' ||
435
- formattedSDValue === '-5'
436
- ) {
437
- formattedSDValue = '-4';
438
- }
439
- }
440
538
 
441
- return height && weight && refSectionObject ? formattedSDValue : null;
539
+ return this.calculateZScoreFromRef(refSectionObject, height);
442
540
  };
443
541
 
444
- calcTimeDifference = (obsDate: Date | dayjs.Dayjs, timeFrame: 'd' | 'w' | 'm' | 'y') => {
445
- let daySinceLastObs: number | string = '';
542
+ /**
543
+ * Calculates the time difference between an observation date and today.
544
+ * @param obsDate - The observation/reference date to compare against today
545
+ * @param timeFrame - The unit of time: 'd' (days), 'w' (weeks), 'm' (months), or 'y' (years)
546
+ * @returns The absolute time difference as a number, or 0 if obsDate is not provided
547
+ */
548
+ calcTimeDifference = (obsDate: Date | dayjs.Dayjs, timeFrame: 'd' | 'w' | 'm' | 'y'): number => {
549
+ if (!obsDate) {
550
+ return 0;
551
+ }
552
+
446
553
  const endDate = dayjs();
447
- if (obsDate) {
448
- if (timeFrame == 'd') {
449
- daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'day', true)));
450
- }
451
- if (timeFrame == 'w') {
452
- daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'week', true)));
453
- }
454
- if (timeFrame == 'm') {
455
- daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'month', true)));
456
- }
457
- if (timeFrame == 'y') {
458
- daySinceLastObs = Math.abs(Math.round(endDate.diff(obsDate, 'year', true)));
459
- }
554
+ switch (timeFrame) {
555
+ case 'd':
556
+ return Math.abs(Math.round(endDate.diff(obsDate, 'day', true)));
557
+ case 'w':
558
+ return Math.abs(Math.round(endDate.diff(obsDate, 'week', true)));
559
+ case 'm':
560
+ return Math.abs(Math.round(endDate.diff(obsDate, 'month', true)));
561
+ case 'y':
562
+ return Math.abs(Math.round(endDate.diff(obsDate, 'year', true)));
460
563
  }
461
- return daySinceLastObs === '' ? '0' : daySinceLastObs;
462
564
  };
463
565
 
464
566
  /**
465
- * Used as wrapper around async functions. It basically evaluates the promised value.
567
+ * Resolves a Promise and returns its value. Used to await async operations in form expressions.
568
+ * @param lazy - A Promise to resolve
569
+ * @returns A Promise that resolves to the value of the input Promise
466
570
  */
467
571
  resolve = (lazy: Promise<unknown>) => {
468
572
  return Promise.resolve(lazy);
@@ -484,6 +588,12 @@ export function simpleHash(str: string) {
484
588
  return hash;
485
589
  }
486
590
 
591
+ /**
592
+ * Registers a dependency relationship between a form node and a field.
593
+ * When the determinant field's value changes, the dependent node will be re-evaluated.
594
+ * @param node - The dependent node (page, section, or field) that depends on the determinant
595
+ * @param determinant - The field that the node depends on
596
+ */
487
597
  export function registerDependency(node: FormNode, determinant: FormField) {
488
598
  if (!node || !determinant) {
489
599
  return;