pict 1.0.386 → 1.0.388

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/dist/pict.js CHANGED
@@ -771,7 +771,7 @@ generateUUIDv4(){let tmpBuffer=new Array(16);var tmpRandomBytes=this.randomByteG
771
771
  tmpRandomBytes[6]=tmpRandomBytes[6]&0x0f|0x40;tmpRandomBytes[8]=tmpRandomBytes[8]&0x3f|0x80;return this.bytesToUUID(tmpRandomBytes);}// Simple random UUID generation
772
772
  generateRandom(){let tmpUUID='';for(let i=0;i<this._UUIDLength;i++){tmpUUID+=this._UUIDRandomDictionary.charAt(Math.floor(Math.random()*(this._UUIDRandomDictionary.length-1)));}return tmpUUID;}// Adapted from node-uuid (https://github.com/kelektiv/node-uuid)
773
773
  getUUID(){if(this._UUIDModeRandom){return this.generateRandom();}else{return this.generateUUIDv4();}}}// This is for backwards compatibility
774
- function autoConstruct(pSettings){return new FableUUID(pSettings);}module.exports=FableUUID;module.exports.new=autoConstruct;},{"../package.json":64,"./Fable-UUID-Random.js":65,"fable-serviceproviderbase":59}],67:[function(require,module,exports){module.exports={"name":"fable","version":"3.1.75","description":"A service dependency injection, configuration and logging library.","main":"source/Fable.js","scripts":{"start":"node source/Fable.js","coverage":"npx quack coverage","test":"npx quack test","build":"npx quack build","prepublishOnly":"npx quack build","docker-dev-build":"docker build ./ -f Dockerfile_LUXURYCode -t fable-image:local","docker-dev-run":"docker run -it -d --name fable-dev -p 30001:8080 -p 38086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/fable\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" fable-image:local","docker-dev-shell":"docker exec -it fable-dev /bin/bash","tests":"npx quack test -g"},"mocha":{"diff":true,"extension":["js"],"package":"./package.json","reporter":"spec","slow":"75","timeout":"5000","ui":"tdd","watch-files":["source/**/*.js","test/**/*.js"],"watch-ignore":["lib/vendor"]},"browser":{"./source/service/Fable-Service-EnvironmentData.js":"./source/service/Fable-Service-EnvironmentData-Web.js","./source/service/Fable-Service-FilePersistence.js":"./source/service/Fable-Service-FilePersistence-Web.js"},"repository":{"type":"git","url":"https://github.com/stevenvelozo/fable.git"},"keywords":["entity","behavior"],"author":"Steven Velozo <steven@velozo.com> (http://velozo.com/)","license":"MIT","bugs":{"url":"https://github.com/stevenvelozo/fable/issues"},"homepage":"https://github.com/stevenvelozo/fable","devDependencies":{"pict-docuserve":"^1.3.2","quackage":"^1.2.3"},"dependencies":{"async.eachlimit":"^0.5.2","async.waterfall":"^0.5.2","big.js":"^7.0.1","cachetrax":"^1.0.6","cookie":"^1.1.1","data-arithmatic":"^1.0.7","dayjs":"^1.11.19","fable-log":"^3.0.18","fable-serviceproviderbase":"^3.0.19","fable-settings":"^3.0.16","fable-uuid":"^3.0.14","manyfest":"^1.0.49","simple-get":"^4.0.1"}};},{}],68:[function(require,module,exports){/**
774
+ function autoConstruct(pSettings){return new FableUUID(pSettings);}module.exports=FableUUID;module.exports.new=autoConstruct;},{"../package.json":64,"./Fable-UUID-Random.js":65,"fable-serviceproviderbase":59}],67:[function(require,module,exports){module.exports={"name":"fable","version":"3.1.78","description":"A service dependency injection, configuration and logging library.","main":"source/Fable.js","scripts":{"start":"node source/Fable.js","coverage":"npx quack coverage","test":"npx quack test","build":"npx quack build","prepublishOnly":"npx quack build","docker-dev-build":"docker build ./ -f Dockerfile_LUXURYCode -t fable-image:local","docker-dev-run":"docker run -it -d --name fable-dev -p 30001:8080 -p 38086:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/fable\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" fable-image:local","docker-dev-shell":"docker exec -it fable-dev /bin/bash","tests":"npx quack test -g"},"mocha":{"diff":true,"extension":["js"],"package":"./package.json","reporter":"spec","slow":"75","timeout":"5000","ui":"tdd","watch-files":["source/**/*.js","test/**/*.js"],"watch-ignore":["lib/vendor"]},"browser":{"./source/service/Fable-Service-EnvironmentData.js":"./source/service/Fable-Service-EnvironmentData-Web.js","./source/service/Fable-Service-FilePersistence.js":"./source/service/Fable-Service-FilePersistence-Web.js"},"repository":{"type":"git","url":"https://github.com/fable-retold/fable.git"},"keywords":["entity","behavior"],"author":"Steven Velozo <steven@velozo.com> (http://velozo.com/)","license":"MIT","bugs":{"url":"https://github.com/fable-retold/fable/issues"},"homepage":"https://github.com/fable-retold/fable","devDependencies":{"pict-docuserve":"^1.4.19","quackage":"^1.3.0"},"dependencies":{"async.eachlimit":"^0.5.2","async.waterfall":"^0.5.2","big.js":"^7.0.1","cachetrax":"^1.0.6","cookie":"^1.1.1","data-arithmatic":"^1.0.7","dayjs":"^1.11.19","fable-log":"^3.0.18","fable-serviceproviderbase":"^3.0.19","fable-settings":"^3.0.16","fable-uuid":"^3.0.14","manyfest":"^1.0.49","simple-get":"^4.0.1"}};},{}],68:[function(require,module,exports){/**
775
775
  * Fable Application Services Support Library
776
776
  * @author <steven@velozo.com>
777
777
  */// Pre-init services
@@ -805,7 +805,7 @@ this.serviceTypes.push(pServiceType);}// Using the static member of the class is
805
805
  if(typeof pServiceClass=='function'&&pServiceClass.isFableService){// Add the class to the list of classes
806
806
  this.serviceClasses[pServiceType]=pServiceClass;}else{// Add the base class to the list of classes
807
807
  this.log.error(`Attempted to add service type [${pServiceType}] with an invalid class. Using base service class, which will not crash but won't provide meaningful services.`);this.serviceClasses[pServiceType]=libFableServiceBase;}return this.serviceClasses[pServiceType];}addServiceTypeIfNotExists(pServiceType,pServiceClass){if(!(pServiceType in this.servicesMap)){return this.addServiceType(pServiceType,pServiceClass);}else{return this.serviceClasses[pServiceType];}}// This is for the services that are meant to run mostly single-instance so need a default at initialization
808
- addAndInstantiateServiceType(pServiceType,pServiceClass){this.addServiceType(pServiceType,pServiceClass);return this.instantiateServiceProvider(pServiceType,{},`${pServiceType}-Default`);}addAndInstantiateServiceTypeIfNotExists(pServiceType,pServiceClass){this.addServiceTypeIfNotExists(pServiceType,pServiceClass);if(!(pServiceType in this.servicesMap)||!(pServiceType in this.fable)){return this.instantiateServiceProvider(pServiceType,{},`${pServiceType}-Default`);}else{return this[pServiceType];}}addAndInstantiateSingletonService(pServiceType,pOptions,pServiceClass){this.addServiceTypeIfNotExists(pServiceType,pServiceClass);if(!(pServiceType in this.servicesMap)||!(pServiceType in this.fable)){return this.instantiateServiceProvider(pServiceType,{},`${pServiceType}-Default`);}else{return this[pServiceType];}}// Some services expect to be overloaded / customized class.
808
+ addAndInstantiateServiceType(pServiceType,pServiceClass){this.addServiceType(pServiceType,pServiceClass);return this.instantiateServiceProvider(pServiceType,{},`${pServiceType}-Default`);}addAndInstantiateServiceTypeIfNotExists(pServiceType,pServiceClass){this.addServiceTypeIfNotExists(pServiceType,pServiceClass);if(!(pServiceType in this.servicesMap)||!(pServiceType in this.fable)){return this.instantiateServiceProvider(pServiceType,{},`${pServiceType}-Default`);}else{return this[pServiceType];}}addAndInstantiateSingletonService(pServiceType,pOptions,pServiceClass){this.addServiceTypeIfNotExists(pServiceType,pServiceClass);if(!(pServiceType in this.servicesMap)||!(pServiceType in this.fable)){return this.instantiateServiceProvider(pServiceType,pOptions,`${pServiceType}-Default`);}else{return this[pServiceType];}}// Some services expect to be overloaded / customized class.
809
809
  instantiateServiceProviderFromPrototype(pServiceType,pOptions,pCustomServiceHash,pServicePrototype){// Instantiate the service
810
810
  let tmpService=new pServicePrototype(this,pOptions,pCustomServiceHash);if(this.extraServiceInitialization){tmpService=this.extraServiceInitialization(tmpService);}// Add the service to the service map
811
811
  this.servicesMap[pServiceType][tmpService.Hash]=tmpService;// If this is the first service of this type, make it the default
@@ -1025,7 +1025,26 @@ return pSign+(pDecimal?this.formatterInsertCommas(pBefore)+pDecimal+pAfter:this.
1025
1025
  *
1026
1026
  * @param {*} pNumber
1027
1027
  * @returns {string}
1028
- */formatterAddCommasToNumber(pNumber){// If the regex doesn't match, `replace` returns the string unmodified
1028
+ *//**
1029
+ * Trim whitespace from both ends of a value (stringified first).
1030
+ * @param {any} pValue
1031
+ * @return {string}
1032
+ */stringTrim(pValue){return pValue===undefined||pValue===null?'':pValue.toString().trim();}/**
1033
+ * Trim trailing whitespace (e.g. SQL CHAR(n) padding).
1034
+ * @param {any} pValue
1035
+ * @return {string}
1036
+ */stringTrimEnd(pValue){return pValue===undefined||pValue===null?'':pValue.toString().replace(/\s+$/,'');}/**
1037
+ * Trim leading whitespace.
1038
+ * @param {any} pValue
1039
+ * @return {string}
1040
+ */stringTrimStart(pValue){return pValue===undefined||pValue===null?'':pValue.toString().replace(/^\s+/,'');}/**
1041
+ * Comma-group a numeric string for human reading, preserving the value
1042
+ * VERBATIM (no float round-trip — arbitrary-precision strings group
1043
+ * intact). Compose with TOFIXED for decimal control. Non-numeric
1044
+ * strings pass through unchanged; missing values return ''.
1045
+ * @param {any} pValue
1046
+ * @return {string}
1047
+ */addCommasToValue(pValue){return pValue===undefined||pValue===null?'':this.formatterAddCommasToNumber(pValue.toString());}formatterAddCommasToNumber(pNumber){// If the regex doesn't match, `replace` returns the string unmodified
1029
1048
  return pNumber.toString().replace(this._Regex_formatterAddCommasToNumber,this.processAddCommasToNumberRegex.bind(this));}/**
1030
1049
  * This will take a number and format it as a dollar string. It will also add commas to the number. If the number is not a number, it will return '--'.
1031
1050
  *
@@ -1085,7 +1104,7 @@ getMonthFromDate(pJavascriptDate){var tmpMonths=["January","February","March","A
1085
1104
  * @param {object} pEnclosureStartSymbolMap
1086
1105
  * @param {object} pEnclosureEndSymbolMap
1087
1106
  * @returns the count of segments in the string as a number
1088
- */stringCountSegments(pString,pSeparator,pEnclosureStartSymbolMap,pEnclosureEndSymbolMap){let tmpString=typeof pString=='string'?pString:'';let tmpSeparator=typeof pSeparator=='string'?pSeparator:'.';let tmpEnclosureStartSymbolMap=typeof pEnclosureStartSymbolMap=='object'?pEnclosureStart:{'{':0,'[':1,'(':2};let tmpEnclosureEndSymbolMap=typeof pEnclosureEndSymbolMap=='object'?pEnclosureEnd:{'}':0,']':1,')':2};if(pString.length<1){return 0;}let tmpSegmentCount=1;let tmpEnclosureStack=[];for(let i=0;i<tmpString.length;i++){// IF This is the start of a segment
1107
+ */stringCountSegments(pString,pSeparator,pEnclosureStartSymbolMap,pEnclosureEndSymbolMap){let tmpString=typeof pString=='string'?pString:'';let tmpSeparator=typeof pSeparator=='string'?pSeparator:'.';let tmpEnclosureStartSymbolMap=typeof pEnclosureStartSymbolMap=='object'?pEnclosureStartSymbolMap:{'{':0,'[':1,'(':2};let tmpEnclosureEndSymbolMap=typeof pEnclosureEndSymbolMap=='object'?pEnclosureEndSymbolMap:{'}':0,']':1,')':2};if(pString.length<1){return 0;}let tmpSegmentCount=1;let tmpEnclosureStack=[];for(let i=0;i<tmpString.length;i++){// IF This is the start of a segment
1089
1108
  if(tmpString[i]==tmpSeparator// AND we are not in a nested portion of the string
1090
1109
  &&tmpEnclosureStack.length==0){// Increment the segment count
1091
1110
  tmpSegmentCount++;}// IF This is the start of an enclosure
@@ -1101,7 +1120,9 @@ tmpEnclosureStack.pop();}}return tmpSegmentCount;}/**
1101
1120
  * @param {object} pEnclosureStartSymbolMap
1102
1121
  * @param {object} pEnclosureEndSymbolMap
1103
1122
  * @returns the first segment in the string as a string
1104
- */stringGetSegments(pString,pSeparator,pEnclosureStartSymbolMap,pEnclosureEndSymbolMap){let tmpString=typeof pString=='string'?pString:'';let tmpSeparator=typeof pSeparator=='string'?pSeparator:'.';let tmpEnclosureStartSymbolMap=typeof pEnclosureStartSymbolMap=='object'?pEnclosureStart:{'{':0,'[':1,'(':2,'"':3,"'":4};let tmpEnclosureEndSymbolMap=typeof pEnclosureEndSymbolMap=='object'?pEnclosureEnd:{'}':0,']':1,')':2,'"':3,"'":4};let tmpCurrentSegmentStart=0;let tmpSegmentList=[];if(pString.length<1){return tmpSegmentList;}let tmpEnclosureStack=[];for(let i=0;i<tmpString.length;i++){// IF This is the start of a segment
1123
+ */stringGetSegments(pString,pSeparator,pEnclosureStartSymbolMap,pEnclosureEndSymbolMap){let tmpString=typeof pString=='string'?pString:'';let tmpSeparator=typeof pSeparator=='string'?pSeparator:'.';// An empty-string enclosure argument disables enclosure handling
1124
+ // entirely (plain split) — expressions cannot pass object literals.
1125
+ if(pEnclosureStartSymbolMap===''){pEnclosureStartSymbolMap={};pEnclosureEndSymbolMap={};}let tmpEnclosureStartSymbolMap=typeof pEnclosureStartSymbolMap=='object'?pEnclosureStartSymbolMap:{'{':0,'[':1,'(':2,'"':3,"'":4};let tmpEnclosureEndSymbolMap=typeof pEnclosureEndSymbolMap=='object'?pEnclosureEndSymbolMap:{'}':0,']':1,')':2,'"':3,"'":4};let tmpCurrentSegmentStart=0;let tmpSegmentList=[];if(pString.length<1){return tmpSegmentList;}let tmpEnclosureStack=[];for(let i=0;i<tmpString.length;i++){// IF This is the start of a segment
1105
1126
  if(tmpString[i]==tmpSeparator// AND we are not in a nested portion of the string
1106
1127
  &&tmpEnclosureStack.length==0){// Return the segment
1107
1128
  tmpSegmentList.push(tmpString.substring(tmpCurrentSegmentStart,i));tmpCurrentSegmentStart=i+1;}// IF This is the start of an enclosure
@@ -1117,7 +1138,7 @@ tmpEnclosureStack.pop();}}if(tmpCurrentSegmentStart<tmpString.length){tmpSegment
1117
1138
  * @param {object} pEnclosureStartSymbolMap
1118
1139
  * @param {object} pEnclosureEndSymbolMap
1119
1140
  * @returns the first segment in the string as a string
1120
- */stringGetFirstSegment(pString,pSeparator,pEnclosureStartSymbolMap,pEnclosureEndSymbolMap){let tmpString=typeof pString=='string'?pString:'';let tmpSeparator=typeof pSeparator=='string'?pSeparator:'.';let tmpEnclosureStartSymbolMap=typeof pEnclosureStartSymbolMap=='object'?pEnclosureStart:{'{':0,'[':1,'(':2};let tmpEnclosureEndSymbolMap=typeof pEnclosureEndSymbolMap=='object'?pEnclosureEnd:{'}':0,']':1,')':2};if(pString.length<1){return 0;}let tmpEnclosureStack=[];for(let i=0;i<tmpString.length;i++){// IF This is the start of a segment
1141
+ */stringGetFirstSegment(pString,pSeparator,pEnclosureStartSymbolMap,pEnclosureEndSymbolMap){let tmpString=typeof pString=='string'?pString:'';let tmpSeparator=typeof pSeparator=='string'?pSeparator:'.';let tmpEnclosureStartSymbolMap=typeof pEnclosureStartSymbolMap=='object'?pEnclosureStartSymbolMap:{'{':0,'[':1,'(':2};let tmpEnclosureEndSymbolMap=typeof pEnclosureEndSymbolMap=='object'?pEnclosureEndSymbolMap:{'}':0,']':1,')':2};if(pString.length<1){return 0;}let tmpEnclosureStack=[];for(let i=0;i<tmpString.length;i++){// IF This is the start of a segment
1121
1142
  if(tmpString[i]==tmpSeparator// AND we are not in a nested portion of the string
1122
1143
  &&tmpEnclosureStack.length==0){// Return the segment
1123
1144
  return tmpString.substring(0,i);}// IF This is the start of an enclosure
@@ -1319,7 +1340,19 @@ this.plugin_advancedFormat=require('dayjs/plugin/advancedFormat');this.dayJS.ext
1319
1340
  * @param {string|Date|number} pDateEnd - The end date. Can be a string, Date object, or timestamp. Defaults to the current date if not provided.
1320
1341
  * @param {boolean} pRequireEndDate - If true, the end date must be provided; otherwise, it defaults to the current date.
1321
1342
  * @returns {number} The difference in years between the two dates. Returns NaN if the start date is invalid.
1322
- */dateYearDifference(pDateStart,pDateEnd){let pRequireEndDate=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(pDateStart===undefined||pDateStart===null||pDateStart===''){return NaN;}if((pRequireEndDate||pRequireEndDate==1||pRequireEndDate=='1')&&(pDateEnd===undefined||pDateEnd===null||pDateEnd==='')){return NaN;}let tmpStartDate=this.dayJS(pDateStart);let tmpEndDate=this.dayJS(pDateEnd);return tmpEndDate.diff(tmpStartDate,'year');}dateAddMilliseconds(pDate,pAmount){return this.dateMath(pDate,pAmount,'millisecond','add');}dateAddSeconds(pDate,pAmount){return this.dateMath(pDate,pAmount,'second','add');}dateAddMinutes(pDate,pAmount){return this.dateMath(pDate,pAmount,'minute','add');}dateAddHours(pDate,pAmount){return this.dateMath(pDate,pAmount,'hour','add');}dateAddDays(pDate,pAmount){return this.dateMath(pDate,pAmount,'day','add');}dateAddWeeks(pDate,pAmount){return this.dateMath(pDate,pAmount,'week','add');}dateAddMonths(pDate,pAmount){return this.dateMath(pDate,pAmount,'month','add');}dateAddYears(pDate,pAmount){return this.dateMath(pDate,pAmount,'year','add');}dateMath(pDate,pAmount,pUnit,pOperation){try{let tmpDate=this.dayJS.utc(pDate);if(pOperation==='add'){tmpDate=tmpDate.add(pAmount,pUnit);}else if(pOperation==='subtract'){tmpDate=tmpDate.subtract(pAmount,pUnit);}return this.dayJS.utc(tmpDate).toISOString();}catch(pError){return undefined;}}dateFromParts(pYear,pMonth,pDay){let pHour=arguments.length>3&&arguments[3]!==undefined?arguments[3]:0;let pMinute=arguments.length>4&&arguments[4]!==undefined?arguments[4]:0;let pSecond=arguments.length>5&&arguments[5]!==undefined?arguments[5]:0;let pMillisecond=arguments.length>6&&arguments[6]!==undefined?arguments[6]:0;try{let tmpDate=this.dayJS.utc().year(pYear).month(pMonth-1).date(pDay).hour(pHour).minute(pMinute).second(pSecond).millisecond(pMillisecond);return tmpDate.toISOString();}catch(pError){return undefined;}}}module.exports=DateManipulation;},{"dayjs":32,"dayjs/plugin/advancedFormat":33,"dayjs/plugin/isoWeek":34,"dayjs/plugin/relativeTime":35,"dayjs/plugin/timezone":36,"dayjs/plugin/utc":37,"dayjs/plugin/weekOfYear":38,"dayjs/plugin/weekday":39,"fable-serviceproviderbase":59}],75:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceEnvironmentData extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='EnvironmentData';this.Environment=`node.js`;}}module.exports=FableServiceEnvironmentData;},{"fable-serviceproviderbase":59}],76:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');/* Trying a different pattern for this service ...
1343
+ */dateYearDifference(pDateStart,pDateEnd){let pRequireEndDate=arguments.length>2&&arguments[2]!==undefined?arguments[2]:false;if(pDateStart===undefined||pDateStart===null||pDateStart===''){return NaN;}if((pRequireEndDate||pRequireEndDate==1||pRequireEndDate=='1')&&(pDateEnd===undefined||pDateEnd===null||pDateEnd==='')){return NaN;}let tmpStartDate=this.dayJS(pDateStart);let tmpEndDate=this.dayJS(pDateEnd);return tmpEndDate.diff(tmpStartDate,'year');}dateAddMilliseconds(pDate,pAmount){return this.dateMath(pDate,pAmount,'millisecond','add');}dateAddSeconds(pDate,pAmount){return this.dateMath(pDate,pAmount,'second','add');}dateAddMinutes(pDate,pAmount){return this.dateMath(pDate,pAmount,'minute','add');}dateAddHours(pDate,pAmount){return this.dateMath(pDate,pAmount,'hour','add');}dateAddDays(pDate,pAmount){return this.dateMath(pDate,pAmount,'day','add');}dateAddWeeks(pDate,pAmount){return this.dateMath(pDate,pAmount,'week','add');}dateAddMonths(pDate,pAmount){return this.dateMath(pDate,pAmount,'month','add');}dateAddYears(pDate,pAmount){return this.dateMath(pDate,pAmount,'year','add');}dateMath(pDate,pAmount,pUnit,pOperation){try{let tmpDate=this.dayJS.utc(pDate);if(pOperation==='add'){tmpDate=tmpDate.add(pAmount,pUnit);}else if(pOperation==='subtract'){tmpDate=tmpDate.subtract(pAmount,pUnit);}return this.dayJS.utc(tmpDate).toISOString();}catch(pError){return undefined;}}/**
1344
+ * Format a date value with dayjs format tokens, rendered in a timezone.
1345
+ *
1346
+ * Timezone resolution: the explicit parameter wins; otherwise the
1347
+ * dayjs default set via `dayJS.tz.setDefault(...)` (the host
1348
+ * application's configured timezone — e.g. pict apps set this to the
1349
+ * document's project timezone); otherwise the host's local zone.
1350
+ *
1351
+ * @param {string|number|Date} pDateValue - ISO string, epoch millis, or Date
1352
+ * @param {string} pFormat - dayjs format tokens (e.g. 'YYYY-MM-DD h:mm A')
1353
+ * @param {string} [pTimezone] - optional IANA zone override (e.g. 'America/Chicago')
1354
+ * @return {string|undefined} the formatted string, or undefined on an unparseable value
1355
+ */formatDate(pDateValue,pFormat,pTimezone){try{let tmpDate=this.dayJS(pDateValue);if(!tmpDate.isValid()){return undefined;}let tmpFormat=typeof pFormat==='string'&&pFormat.length>0?pFormat:'YYYY-MM-DD HH:mm:ss';tmpDate=typeof pTimezone==='string'&&pTimezone.length>0?tmpDate.tz(pTimezone):tmpDate.tz();return tmpDate.format(tmpFormat);}catch(pError){return undefined;}}dateFromParts(pYear,pMonth,pDay){let pHour=arguments.length>3&&arguments[3]!==undefined?arguments[3]:0;let pMinute=arguments.length>4&&arguments[4]!==undefined?arguments[4]:0;let pSecond=arguments.length>5&&arguments[5]!==undefined?arguments[5]:0;let pMillisecond=arguments.length>6&&arguments[6]!==undefined?arguments[6]:0;try{let tmpDate=this.dayJS.utc().year(pYear).month(pMonth-1).date(pDay).hour(pHour).minute(pMinute).second(pSecond).millisecond(pMillisecond);return tmpDate.toISOString();}catch(pError){return undefined;}}}module.exports=DateManipulation;},{"dayjs":32,"dayjs/plugin/advancedFormat":33,"dayjs/plugin/isoWeek":34,"dayjs/plugin/relativeTime":35,"dayjs/plugin/timezone":36,"dayjs/plugin/utc":37,"dayjs/plugin/weekOfYear":38,"dayjs/plugin/weekday":39,"fable-serviceproviderbase":59}],75:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceEnvironmentData extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='EnvironmentData';this.Environment=`node.js`;}}module.exports=FableServiceEnvironmentData;},{"fable-serviceproviderbase":59}],76:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');/* Trying a different pattern for this service ...
1323
1356
  *
1324
1357
  * This service is a simple expression parser that can handle math expressions, with magic(tm) lookup of addresses with a manifest.
1325
1358
  *
@@ -1618,7 +1651,7 @@ tmpCurrentTokenType='Value';tmpCurrentToken+=tmpCharacter;// continue;
1618
1651
  // tmpResults.ExpressionParserLog.push(`ExpressionParser.tokenize found an unknown character code ${tmpCharCode} character ${tmpCharacter} in the expression: ${pExpression} at index ${i}`);
1619
1652
  // this.log.warn(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);
1620
1653
  }if(tmpCurrentTokenType&&tmpCurrentToken.length>0){tmpResults.RawTokens.push(tmpCurrentToken);}tmpResults.OriginalRawTokens=Array.from(tmpResults.RawTokens);// Potentially mutate the tokens based on directives in the tokenized expression
1621
- this.TokenizerDirectiveMutation.parseDirectives(tmpResults);return tmpResults.RawTokens;}}module.exports=ExpressionTokenizer;},{"./Fable-Service-ExpressionParser-Base.js":77}],80:[function(require,module,exports){module.exports={"sqrt":{"Name":"Square Root","Address":"fable.Math.sqrtPrecise"},"percent":{"Name":"Compute Percent (in IS over OF format)","Address":"fable.Math.percentagePrecise"},"compare":{"Name":"Compare","Address":"fable.Math.comparePrecise"},"abs":{"Name":"Absolute Value","Address":"fable.Math.absPrecise"},"floor":{"Name":"Floor Value","Address":"fable.Math.floorPrecise"},"ceil":{"Name":"Ceiling Value","Address":"fable.Math.ceilPrecise"},"rad":{"Name":"Degrees to Radians","Address":"fable.Math.radPrecise"},"pi":{"Name":"Pi","Address":"fable.Math.piPrecise"},"euler":{"Name":"Euler","Address":"fable.Math.eulerPrecise"},"log":{"Name":"Logarithm","Address":"fable.Math.logPrecise"},"exp":{"Name":"Eulers Number to the Power Of N","Address":"fable.Math.expPrecise"},"sin":{"Name":"Sine","Address":"fable.Math.sin"},"cos":{"Name":"Cosine","Address":"fable.Math.cos"},"tan":{"Name":"Tangent","Address":"fable.Math.tan"},"count":{"Name":"Count Set Elements","Address":"fable.Math.countSetElements"},"countset":{"Name":"Count Set Elements","Address":"fable.Math.countSetElements"},"sortset":{"Name":"Sort Set","Address":"fable.Math.sortSetPrecise"},"bucketset":{"Name":"Bucket Set","Address":"fable.Math.bucketSetPrecise"},"sorthistogram":{"Name":"Sort Histogram","Address":"fable.Math.sortHistogramPrecise"},"sorthistogrambykeys":{"Name":"Sort Histogram by Keys","Address":"fable.Math.sortHistogramByKeys"},"max":{"Name":"Maximum","Address":"fable.Math.maxPrecise"},"min":{"Name":"Minimum","Address":"fable.Math.minPrecise"},"sum":{"Name":"Sum","Address":"fable.Math.sumPrecise"},"avg":{"Name":"Average","Address":"fable.Math.averagePrecise"},"mean":{"Name":"Mean","Address":"fable.Math.meanPrecise"},"median":{"Name":"Median","Address":"fable.Math.medianPrecise"},"mode":{"Name":"Mode","Address":"fable.Math.modePrecise"},"var":{"Name":"Variance (Sample)","Address":"fable.Math.variancePrecise"},"vara":{"Name":"Variance (Sample)","Address":"fable.Math.variancePrecise"},"varp":{"Name":"Variance (Population)","Address":"fable.Math.populationVariancePrecise"},"stdev":{"Name":"Standard Deviation (Sample)","Address":"fable.Math.standardDeviationPrecise"},"stdeva":{"Name":"Standard Deviation (Sample)","Address":"fable.Math.standardDeviationPrecise"},"stdevp":{"Name":"Standard Deviation (Population)","Address":"fable.Math.populationStandardDeviationPrecise"},"round":{"Name":"Round","Address":"fable.Math.roundPrecise"},"tofixed":{"Name":"To Fixed","Address":"fable.Math.toFixedPrecise"},"cumulativesummation":{"Name":"Sum each value in a Histogram or Value Map cumulatively, creating or setting a property with the result on each row","Address":"fable.Math.cumulativeSummation"},"subtractingsummation":{"Name":"Subtract each subsequent value in a Histogram or Value Map cumulatively (by default from the first row), creating or setting a property with the result on each row.","Address":"fable.Math.subtractingSummation"},"iterativeseries":{"Name":"Perform an Iterative Series of Mathematical Operations on Set Elements","Address":"fable.Math.iterativeSeries"},"countsetelements":{"Name":"Count Set Elements in a Histogram or Value Map","Address":"fable.Math.countSetElements"},"getvalue":{"Name":"Get Value from Application State or Services (AppData, etc.)","Address":"fable.Utility.getInternalValueByHash","AddressParameterIndices":[0]},"setvalue":{"Name":"Set Value to Application State or Services (AppData, etc.)","Address":"fable.Utility.setInternalValueByHash","AddressParameterIndices":[0]},"objectkeystoarray":{"Name":"Get Array of an Object's keys","Address":"fable.Utility.objectKeysToArray"},"objectvaluestoarray":{"Name":"Get Array of an Object's values","Address":"fable.Utility.objectValuesToArray"},"generatearrayofobjectsfromsets":{"Name":"Generate Array of Objects from Sets","Address":"fable.Utility.generateArrayOfObjectsFromSets"},"objectvaluessortbyexternalobjectarray":{"Name":"Get Array of an Object's values sorted by an external array","Address":"fable.Utility.objectValuesSortByExternalArray"},"setkeystoarray":{"Name":"Get Array of an Object's keys","Address":"fable.Utility.objectKeysToArray"},"setvaluestoarray":{"Name":"Get Array of an Object's values","Address":"fable.Utility.objectValuesToArray"},"histogramkeystoarray":{"Name":"Get Array of an Object's keys","Address":"fable.Utility.objectKeysToArray"},"histogramvaluestoarray":{"Name":"Get Array of an Object's values","Address":"fable.Utility.objectValuesToArray"},"createarrayfromabsolutevalues":{"Name":"Create Array from Absolute Values","Address":"fable.Utility.createArrayFromAbsoluteValues"},"flatten":{"Name":"flatten an array of values","Address":"fable.Utility.flattenArrayOfSolverInputs"},"findfirstvaluebyexactmatch":{"Name":"find + map on array of objects","Address":"fable.Utility.findFirstValueByExactMatchInternal","AddressParameterIndices":[0,1,3]},"findfirstvaluebystringincludes":{"Name":"find + map on array of objects","Address":"fable.Utility.findFirstValueByStringIncludesInternal","AddressParameterIndices":[0,1,3]},"match":{"Name":"Implementation of sheets MATCH() function","Address":"fable.Utility.findIndexInternal","AddressParameterIndices":[1]},"resolvehtmlentities":{"Name":"resolve HTML entities","Address":"fable.DataFormat.resolveHtmlEntities"},"concat":{"Name":"concatenate an array of values and output a string","Address":"fable.DataFormat.concatenateStringsInternal"},"concatraw":{"Name":"concatenate an array of values and output a string","Address":"fable.DataFormat.concatenateStringsRawInternal"},"arrayconcat":{"Name":"concatenate two or more arrays generating a single output array","Address":"fable.Utility.concatenateArrays"},"join":{"Name":"join an array of values and output a string","Address":"fable.DataFormat.joinStringsInternal"},"joinraw":{"Name":"join an array of values and output a string","Address":"fable.DataFormat.joinStringsRawInternal"},"if":{"Name":"perform a conditional operator on two values, and choose one of two outcomes based on the result","Address":"fable.Logic.checkIf"},"when":{"Name":"perform a 'truthy' check on one value, and return one of two outcomes based on the result","Address":"fable.Logic.when"},"ternary":{"Name":"numeric-aware ternary selection (used by ? :: operator desugaring)","Address":"fable.Logic.ternary"},"entryinset":{"Name":"Entry in Set","Address":"fable.Math.entryInSet"},"smallestinset":{"Name":"Smallest in Set","Address":"fable.Math.smallestInSet"},"largestinset":{"Name":"Largest in Set","Address":"fable.Math.largestInSet"},"aggregationhistogram":{"Name":"Generate a Histogram by Exact Value Aggregation","Address":"fable.Math.histogramAggregationByExactValueFromInternalState"},"aggregationhistogrambyobject":{"Name":"Generate a Histogram by Exact Value Aggregation from Object Property","Address":"fable.Math.histogramAggregationByExactValue"},"distributionhistogram":{"Name":"Generate a Histogram Based on Value Distribution","Address":"fable.Math.histogramDistributionByExactValueFromInternalState"},"distributionhistogrambyobject":{"Name":"Generate a Histogram Based on Value Distribution from Object Property","Address":"fable.Math.histogramDistributionByExactValue"},"setconcatenate":{"Name":"Set Concatenate","Address":"fable.Math.setConcatenate"},"getvaluearray":{"Name":"Get Value Array from Application State or Services (AppData, etc.)","Address":"fable.Utility.createValueArrayByHashParametersFromInternal","AddressParameterIndices":[0,1,2,3,4,5,6,7,8,9]},"getvalueobject":{"Name":"Get Value Object from Application State or Services (AppData, etc.)","Address":"fable.Utility.createValueObjectByHashParametersFromInternal","AddressParameterIndices":[0,1,2,3,4,5,6,7,8,9]},"cleanvaluearray":{"Name":"Clean Value Array","Address":"fable.Math.cleanValueArray"},"cleanvalueobject":{"Name":"Clean Value Object","Address":"fable.Math.cleanValueObject"},"polynomialregression":{"Name":"Perform an nth degree Polynomial Regression on a Set of X and Y Values","Address":"fable.Math.polynomialRegression"},"randominteger":{"Name":"Random Integer","Address":"fable.DataGeneration.randomInteger"},"randomintegerbetween":{"Name":"Random Integer Between Two Numbers","Address":"fable.DataGeneration.randomIntegerBetween"},"randomintegerupto":{"Name":"Random Integer","Address":"fable.DataGeneration.randomIntegerUpTo"},"randomfloat":{"Name":"Random Float","Address":"fable.DataGeneration.randomFloat"},"randomfloatbetween":{"Name":"Random Float","Address":"fable.DataGeneration.randomFloatBetween"},"randomfloatupto":{"Name":"Random Float","Address":"fable.DataGeneration.randomFloatUpTo"},"datemilliseconddifference":{"Name":"Date Difference in Milliseconds","Address":"fable.Dates.dateMillisecondDifference"},"dateseconddifference":{"Name":"Date Difference in Seconds","Address":"fable.Dates.dateSecondDifference"},"dateminutedifference":{"Name":"Date Difference in Minutes","Address":"fable.Dates.dateMinuteDifference"},"datehourdifference":{"Name":"Date Difference in Hours","Address":"fable.Dates.dateHourDifference"},"datedaydifference":{"Name":"Date Difference in Days","Address":"fable.Dates.dateDayDifference"},"dateweekdifference":{"Name":"Date Difference in Weeks","Address":"fable.Dates.dateWeekDifference"},"datemonthdifference":{"Name":"Date Difference in Months","Address":"fable.Dates.dateMonthDifference"},"dateyeardifference":{"Name":"Date Difference in Years","Address":"fable.Dates.dateYearDifference"},"datemathadd":{"Name":"Date Math Add","Address":"fable.Dates.dateMath"},"dateaddmilliseconds":{"Name":"Date Add Milliseconds","Address":"fable.Dates.dateAddMilliseconds"},"dateaddseconds":{"Name":"Date Add Seconds","Address":"fable.Dates.dateAddSeconds"},"dateaddminutes":{"Name":"Date Add Minutes","Address":"fable.Dates.dateAddMinutes"},"dateaddhours":{"Name":"Date Add Hours","Address":"fable.Dates.dateAddHours"},"dateadddays":{"Name":"Date Add Days","Address":"fable.Dates.dateAddDays"},"dateaddweeks":{"Name":"Date Add Weeks","Address":"fable.Dates.dateAddWeeks"},"dateaddmonths":{"Name":"Date Add Months","Address":"fable.Dates.dateAddMonths"},"dateaddyears":{"Name":"Date Add Years","Address":"fable.Dates.dateAddYears"},"datefromparts":{"Name":"Date From Parts","Address":"fable.Dates.dateFromParts"},"slice":{"Name":"Slice Array","Address":"fable.Utility.slice"},"createvalueobjectbyhashes":{"Name":"Create Value Object by Hashes","Address":"fable.Utility.createValueObjectByHashes","AddressParameterIndices":[0,1,2,3,4,5,6,7,8,9]},"slope":{"Name":"Slope of Linear Regression Line (Excel SLOPE equivalent)","Address":"fable.Math.slopePrecise"},"intercept":{"Name":"Y-Intercept of Linear Regression Line (Excel INTERCEPT equivalent)","Address":"fable.Math.interceptPrecise"},"leastsquares":{"Name":"Perform a Least Squares Regression on a Set of Independent Variable Vectors and a Dependent Variable Vector","Address":"fable.Math.leastSquares"},"linest":{"Name":"Perform a Least Squares Regression on a Set of Independent Variable Vectors and a Dependent Variable Vector","Address":"fable.Math.leastSquares"},"matrixtranspose":{"Name":"Transpose a Matrix","Address":"fable.Math.matrixTranspose"},"matrixmultiply":{"Name":"Multiply Two Matrices","Address":"fable.Math.matrixMultiply"},"matrixvectormultiply":{"Name":"Multiply a Matrix by a Vector","Address":"fable.Math.matrixVectorMultiply"},"matrixinverse":{"Name":"Inverse a Matrix","Address":"fable.Math.matrixInverse"},"gaussianelimination":{"Name":"Solve a System of Linear Equations using Gaussian Elimination","Address":"fable.Math.gaussianElimination"},"predict":{"Name":"Predict Y Values from X Values using a Regression Model","Address":"fable.Math.predictFromRegressionModel"},"stringcountsegments":{"Name":"Count Segments in a String","Address":"fable.DataFormat.stringCountSegments"},"stringgetsegments":{"Name":"Get Segments from a String","Address":"fable.DataFormat.stringGetSegments"},"bezierpoint":{"Name":"Evaluate a Point on a Cubic Bezier Curve at Parameter t","Address":"fable.Math.bezierPoint"},"beziercurvefit":{"Name":"Fit a Cubic Bezier Curve to a Set of Data Points","Address":"fable.Math.bezierCurveFit"},"generateguid":{"Name":"Generate a GUID string","Address":"fable.getUUID"}};},{}],81:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionParserLinter extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Linter';}lintTokenizedExpression(pTokenizedExpression,pResultObject){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};tmpResults.LinterResults=[];// Guard against bad data being passed in
1654
+ this.TokenizerDirectiveMutation.parseDirectives(tmpResults);return tmpResults.RawTokens;}}module.exports=ExpressionTokenizer;},{"./Fable-Service-ExpressionParser-Base.js":77}],80:[function(require,module,exports){module.exports={"sqrt":{"Name":"Square Root","Address":"fable.Math.sqrtPrecise"},"percent":{"Name":"Compute Percent (in IS over OF format)","Address":"fable.Math.percentagePrecise"},"compare":{"Name":"Compare","Address":"fable.Math.comparePrecise"},"abs":{"Name":"Absolute Value","Address":"fable.Math.absPrecise"},"floor":{"Name":"Floor Value","Address":"fable.Math.floorPrecise"},"ceil":{"Name":"Ceiling Value","Address":"fable.Math.ceilPrecise"},"rad":{"Name":"Degrees to Radians","Address":"fable.Math.radPrecise"},"pi":{"Name":"Pi","Address":"fable.Math.piPrecise"},"euler":{"Name":"Euler","Address":"fable.Math.eulerPrecise"},"log":{"Name":"Logarithm","Address":"fable.Math.logPrecise"},"exp":{"Name":"Eulers Number to the Power Of N","Address":"fable.Math.expPrecise"},"sin":{"Name":"Sine","Address":"fable.Math.sin"},"cos":{"Name":"Cosine","Address":"fable.Math.cos"},"tan":{"Name":"Tangent","Address":"fable.Math.tan"},"count":{"Name":"Count Set Elements","Address":"fable.Math.countSetElements"},"countset":{"Name":"Count Set Elements","Address":"fable.Math.countSetElements"},"sortset":{"Name":"Sort Set","Address":"fable.Math.sortSetPrecise"},"bucketset":{"Name":"Bucket Set","Address":"fable.Math.bucketSetPrecise"},"sorthistogram":{"Name":"Sort Histogram","Address":"fable.Math.sortHistogramPrecise"},"sorthistogrambykeys":{"Name":"Sort Histogram by Keys","Address":"fable.Math.sortHistogramByKeys"},"max":{"Name":"Maximum","Address":"fable.Math.maxPrecise"},"min":{"Name":"Minimum","Address":"fable.Math.minPrecise"},"sum":{"Name":"Sum","Address":"fable.Math.sumPrecise"},"avg":{"Name":"Average","Address":"fable.Math.averagePrecise"},"mean":{"Name":"Mean","Address":"fable.Math.meanPrecise"},"median":{"Name":"Median","Address":"fable.Math.medianPrecise"},"mode":{"Name":"Mode","Address":"fable.Math.modePrecise"},"var":{"Name":"Variance (Sample)","Address":"fable.Math.variancePrecise"},"vara":{"Name":"Variance (Sample)","Address":"fable.Math.variancePrecise"},"varp":{"Name":"Variance (Population)","Address":"fable.Math.populationVariancePrecise"},"stdev":{"Name":"Standard Deviation (Sample)","Address":"fable.Math.standardDeviationPrecise"},"stdeva":{"Name":"Standard Deviation (Sample)","Address":"fable.Math.standardDeviationPrecise"},"stdevp":{"Name":"Standard Deviation (Population)","Address":"fable.Math.populationStandardDeviationPrecise"},"round":{"Name":"Round","Address":"fable.Math.roundPrecise"},"tofixed":{"Name":"To Fixed","Address":"fable.Math.toFixedPrecise"},"cumulativesummation":{"Name":"Sum each value in a Histogram or Value Map cumulatively, creating or setting a property with the result on each row","Address":"fable.Math.cumulativeSummation"},"subtractingsummation":{"Name":"Subtract each subsequent value in a Histogram or Value Map cumulatively (by default from the first row), creating or setting a property with the result on each row.","Address":"fable.Math.subtractingSummation"},"iterativeseries":{"Name":"Perform an Iterative Series of Mathematical Operations on Set Elements","Address":"fable.Math.iterativeSeries"},"countsetelements":{"Name":"Count Set Elements in a Histogram or Value Map","Address":"fable.Math.countSetElements"},"getvalue":{"Name":"Get Value from Application State or Services (AppData, etc.)","Address":"fable.Utility.getInternalValueByHash","AddressParameterIndices":[0]},"setvalue":{"Name":"Set Value to Application State or Services (AppData, etc.)","Address":"fable.Utility.setInternalValueByHash","AddressParameterIndices":[0]},"objectkeystoarray":{"Name":"Get Array of an Object's keys","Address":"fable.Utility.objectKeysToArray"},"objectvaluestoarray":{"Name":"Get Array of an Object's values","Address":"fable.Utility.objectValuesToArray"},"generatearrayofobjectsfromsets":{"Name":"Generate Array of Objects from Sets","Address":"fable.Utility.generateArrayOfObjectsFromSets"},"objectvaluessortbyexternalobjectarray":{"Name":"Get Array of an Object's values sorted by an external array","Address":"fable.Utility.objectValuesSortByExternalArray"},"setkeystoarray":{"Name":"Get Array of an Object's keys","Address":"fable.Utility.objectKeysToArray"},"setvaluestoarray":{"Name":"Get Array of an Object's values","Address":"fable.Utility.objectValuesToArray"},"histogramkeystoarray":{"Name":"Get Array of an Object's keys","Address":"fable.Utility.objectKeysToArray"},"histogramvaluestoarray":{"Name":"Get Array of an Object's values","Address":"fable.Utility.objectValuesToArray"},"createarrayfromabsolutevalues":{"Name":"Create Array from Absolute Values","Address":"fable.Utility.createArrayFromAbsoluteValues"},"flatten":{"Name":"flatten an array of values","Address":"fable.Utility.flattenArrayOfSolverInputs"},"findfirstvaluebyexactmatch":{"Name":"find + map on array of objects","Address":"fable.Utility.findFirstValueByExactMatchInternal","AddressParameterIndices":[0,1,3]},"findfirstvaluebystringincludes":{"Name":"find + map on array of objects","Address":"fable.Utility.findFirstValueByStringIncludesInternal","AddressParameterIndices":[0,1,3]},"match":{"Name":"Implementation of sheets MATCH() function","Address":"fable.Utility.findIndexInternal","AddressParameterIndices":[1]},"resolvehtmlentities":{"Name":"resolve HTML entities","Address":"fable.DataFormat.resolveHtmlEntities"},"concat":{"Name":"concatenate an array of values and output a string","Address":"fable.DataFormat.concatenateStringsInternal"},"concatraw":{"Name":"concatenate an array of values and output a string","Address":"fable.DataFormat.concatenateStringsRawInternal"},"arrayconcat":{"Name":"concatenate two or more arrays generating a single output array","Address":"fable.Utility.concatenateArrays"},"join":{"Name":"join an array of values and output a string","Address":"fable.DataFormat.joinStringsInternal"},"joinraw":{"Name":"join an array of values and output a string","Address":"fable.DataFormat.joinStringsRawInternal"},"if":{"Name":"perform a conditional operator on two values, and choose one of two outcomes based on the result","Address":"fable.Logic.checkIf"},"when":{"Name":"perform a 'truthy' check on one value, and return one of two outcomes based on the result","Address":"fable.Logic.when"},"ternary":{"Name":"numeric-aware ternary selection (used by ? :: operator desugaring)","Address":"fable.Logic.ternary"},"entryinset":{"Name":"Entry in Set","Address":"fable.Math.entryInSet"},"smallestinset":{"Name":"Smallest in Set","Address":"fable.Math.smallestInSet"},"largestinset":{"Name":"Largest in Set","Address":"fable.Math.largestInSet"},"aggregationhistogram":{"Name":"Generate a Histogram by Exact Value Aggregation","Address":"fable.Math.histogramAggregationByExactValueFromInternalState"},"aggregationhistogrambyobject":{"Name":"Generate a Histogram by Exact Value Aggregation from Object Property","Address":"fable.Math.histogramAggregationByExactValue"},"distributionhistogram":{"Name":"Generate a Histogram Based on Value Distribution","Address":"fable.Math.histogramDistributionByExactValueFromInternalState"},"distributionhistogrambyobject":{"Name":"Generate a Histogram Based on Value Distribution from Object Property","Address":"fable.Math.histogramDistributionByExactValue"},"setconcatenate":{"Name":"Set Concatenate","Address":"fable.Math.setConcatenate"},"getvaluearray":{"Name":"Get Value Array from Application State or Services (AppData, etc.)","Address":"fable.Utility.createValueArrayByHashParametersFromInternal","AddressParameterIndices":[0,1,2,3,4,5,6,7,8,9]},"getvalueobject":{"Name":"Get Value Object from Application State or Services (AppData, etc.)","Address":"fable.Utility.createValueObjectByHashParametersFromInternal","AddressParameterIndices":[0,1,2,3,4,5,6,7,8,9]},"cleanvaluearray":{"Name":"Clean Value Array","Address":"fable.Math.cleanValueArray"},"cleanvalueobject":{"Name":"Clean Value Object","Address":"fable.Math.cleanValueObject"},"polynomialregression":{"Name":"Perform an nth degree Polynomial Regression on a Set of X and Y Values","Address":"fable.Math.polynomialRegression"},"randominteger":{"Name":"Random Integer","Address":"fable.DataGeneration.randomInteger"},"randomintegerbetween":{"Name":"Random Integer Between Two Numbers","Address":"fable.DataGeneration.randomIntegerBetween"},"randomintegerupto":{"Name":"Random Integer","Address":"fable.DataGeneration.randomIntegerUpTo"},"randomfloat":{"Name":"Random Float","Address":"fable.DataGeneration.randomFloat"},"randomfloatbetween":{"Name":"Random Float","Address":"fable.DataGeneration.randomFloatBetween"},"randomfloatupto":{"Name":"Random Float","Address":"fable.DataGeneration.randomFloatUpTo"},"datemilliseconddifference":{"Name":"Date Difference in Milliseconds","Address":"fable.Dates.dateMillisecondDifference"},"dateseconddifference":{"Name":"Date Difference in Seconds","Address":"fable.Dates.dateSecondDifference"},"dateminutedifference":{"Name":"Date Difference in Minutes","Address":"fable.Dates.dateMinuteDifference"},"datehourdifference":{"Name":"Date Difference in Hours","Address":"fable.Dates.dateHourDifference"},"datedaydifference":{"Name":"Date Difference in Days","Address":"fable.Dates.dateDayDifference"},"dateweekdifference":{"Name":"Date Difference in Weeks","Address":"fable.Dates.dateWeekDifference"},"datemonthdifference":{"Name":"Date Difference in Months","Address":"fable.Dates.dateMonthDifference"},"dateyeardifference":{"Name":"Date Difference in Years","Address":"fable.Dates.dateYearDifference"},"datemathadd":{"Name":"Date Math Add","Address":"fable.Dates.dateMath"},"dateaddmilliseconds":{"Name":"Date Add Milliseconds","Address":"fable.Dates.dateAddMilliseconds"},"dateaddseconds":{"Name":"Date Add Seconds","Address":"fable.Dates.dateAddSeconds"},"dateaddminutes":{"Name":"Date Add Minutes","Address":"fable.Dates.dateAddMinutes"},"dateaddhours":{"Name":"Date Add Hours","Address":"fable.Dates.dateAddHours"},"dateadddays":{"Name":"Date Add Days","Address":"fable.Dates.dateAddDays"},"dateaddweeks":{"Name":"Date Add Weeks","Address":"fable.Dates.dateAddWeeks"},"dateaddmonths":{"Name":"Date Add Months","Address":"fable.Dates.dateAddMonths"},"dateaddyears":{"Name":"Date Add Years","Address":"fable.Dates.dateAddYears"},"datefromparts":{"Name":"Date From Parts","Address":"fable.Dates.dateFromParts"},"slice":{"Name":"Slice Array","Address":"fable.Utility.slice"},"createvalueobjectbyhashes":{"Name":"Create Value Object by Hashes","Address":"fable.Utility.createValueObjectByHashes","AddressParameterIndices":[0,1,2,3,4,5,6,7,8,9]},"slope":{"Name":"Slope of Linear Regression Line (Excel SLOPE equivalent)","Address":"fable.Math.slopePrecise"},"intercept":{"Name":"Y-Intercept of Linear Regression Line (Excel INTERCEPT equivalent)","Address":"fable.Math.interceptPrecise"},"leastsquares":{"Name":"Perform a Least Squares Regression on a Set of Independent Variable Vectors and a Dependent Variable Vector","Address":"fable.Math.leastSquares"},"linest":{"Name":"Perform a Least Squares Regression on a Set of Independent Variable Vectors and a Dependent Variable Vector","Address":"fable.Math.leastSquares"},"matrixtranspose":{"Name":"Transpose a Matrix","Address":"fable.Math.matrixTranspose"},"matrixmultiply":{"Name":"Multiply Two Matrices","Address":"fable.Math.matrixMultiply"},"matrixvectormultiply":{"Name":"Multiply a Matrix by a Vector","Address":"fable.Math.matrixVectorMultiply"},"matrixinverse":{"Name":"Inverse a Matrix","Address":"fable.Math.matrixInverse"},"gaussianelimination":{"Name":"Solve a System of Linear Equations using Gaussian Elimination","Address":"fable.Math.gaussianElimination"},"predict":{"Name":"Predict Y Values from X Values using a Regression Model","Address":"fable.Math.predictFromRegressionModel"},"stringcountsegments":{"Name":"Count Segments in a String","Address":"fable.DataFormat.stringCountSegments"},"stringgetsegments":{"Name":"Get Segments from a String","Address":"fable.DataFormat.stringGetSegments"},"bezierpoint":{"Name":"Evaluate a Point on a Cubic Bezier Curve at Parameter t","Address":"fable.Math.bezierPoint"},"beziercurvefit":{"Name":"Fit a Cubic Bezier Curve to a Set of Data Points","Address":"fable.Math.bezierCurveFit"},"generateguid":{"Name":"Generate a GUID string","Address":"fable.getUUID"},"isnumeric":{"Name":"Is Numeric (1/0; trims strings, empty/text are not numeric)","Address":"fable.Math.isNumeric"},"tonumber":{"Name":"To Number (with fallback for non-numeric values)","Address":"fable.Math.toNumber"},"aggregationjoinbyobject":{"Name":"Collect (join) per-key string values by Exact Value from Object Property","Address":"fable.Math.histogramAggregationJoinByExactValue"},"trim":{"Name":"Trim whitespace from both ends of a value","Address":"fable.DataFormat.stringTrim"},"rtrim":{"Name":"Trim trailing whitespace from a value (e.g. SQL CHAR(n) padding)","Address":"fable.DataFormat.stringTrimEnd"},"ltrim":{"Name":"Trim leading whitespace from a value","Address":"fable.DataFormat.stringTrimStart"},"addcommas":{"Name":"Comma-group a numeric string verbatim (no float coercion; compose with TOFIXED for decimals)","Address":"fable.DataFormat.addCommasToValue"},"dateformat":{"Name":"Format a Date with dayjs tokens in a timezone (explicit param, else the configured default)","Address":"fable.Dates.formatDate"},"chartticks":{"Name":"Compute readable chart axis tick labels (sparse same-length array) from a dense value set","Address":"fable.Math.chartTickLabels"},"setdensity":{"Name":"Analyze ordered-set density: natural boundaries (calendar units / nice numeric steps / positions) fitting a budget","Address":"fable.Math.analyzeSetDensity"}};},{}],81:[function(require,module,exports){const libExpressionParserOperationBase=require('./Fable-Service-ExpressionParser-Base.js');class ExpressionParserLinter extends libExpressionParserOperationBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.serviceType='ExpressionParser-Linter';}lintTokenizedExpression(pTokenizedExpression,pResultObject){let tmpResults=typeof pResultObject==='object'?pResultObject:{ExpressionParserLog:[]};tmpResults.LinterResults=[];// Guard against bad data being passed in
1622
1655
  if(!Array.isArray(pTokenizedExpression)){tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.lintTokenizedExpression was passed a non-array tokenized expression.`);tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return pTokenizedExpression;}if(pTokenizedExpression.length<1){tmpResults.ExpressionParserLog.push(`ERROR: ExpressionParser.lintTokenizedExpression was passed an empty tokenized expression.`);tmpResults.LinterResults.push(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);this.log.error(tmpResults.ExpressionParserLog[tmpResults.ExpressionParserLog.length-1]);return pTokenizedExpression;}// 1. Check for balanced parenthesis
1623
1656
  let tmpParenthesisDepth=0;// If it is in a state address, we don't care about the parenthesis
1624
1657
  // State addresses are between squiggly brackets
@@ -1966,6 +1999,34 @@ tmpResult=Math.pow(tmpLeftValue,Number(pRightValue));}return tmpResult.toString(
1966
1999
  * @param {number|string} pValue - The number to calculate the absolute value of.
1967
2000
  * @returns {string} The absolute value of the input number as a string.
1968
2001
  */absPrecise(pValue){let tmpValue=isNaN(pValue)?0:pValue;let tmpLeftArbitraryValue=new this.bigNumber(tmpValue);let tmpResult=tmpLeftArbitraryValue.abs();return tmpResult.toString();}/**
2002
+ * Tests whether a value is a numeric decimal string (after trimming).
2003
+ * Empty strings, whitespace, non-numeric text, null and undefined are NOT
2004
+ * numeric; '0' IS. Returns the strings '1' / '0' so the result composes
2005
+ * with the expression grammar's comparison-style truth values.
2006
+ *
2007
+ * Validity is tested with a decimal grammar (sign, digits, optional
2008
+ * fraction, optional exponent) rather than a float round-trip, so
2009
+ * arbitrary-precision values — long mantissas, integers past 2^53,
2010
+ * exponents beyond float range — classify correctly for the string-based
2011
+ * precise math this service does.
2012
+ *
2013
+ * @param {*} pValue - The value to test.
2014
+ * @returns {string} '1' when numeric, '0' otherwise.
2015
+ */isNumeric(pValue){if(pValue===null||pValue===undefined){return'0';}const tmpString=String(pValue).trim();if(tmpString===''){return'0';}return /^[+-]?(\d+(\.\d*)?|\.\d+)([eE][+-]?\d+)?$/.test(tmpString)?'1':'0';}/**
2016
+ * Validates a value as numeric, with an explicit fallback for anything
2017
+ * that is not (empty strings, text placeholders, null). The numeric
2018
+ * counterpart to isNumeric — together they express "skip or zero-fill
2019
+ * dirty readings while still counting real zeroes".
2020
+ *
2021
+ * PRECISION CONTRACT: when the value IS numeric, the original (trimmed)
2022
+ * string is returned VERBATIM — never round-tripped through a float — so
2023
+ * arbitrary-precision values flow into the precise math untouched
2024
+ * ('1.500' stays '1.500'; 25-digit mantissas keep every digit).
2025
+ *
2026
+ * @param {*} pValue - The value to validate.
2027
+ * @param {number|string} [pFallback=0] - Returned when pValue is not numeric.
2028
+ * @returns {string} The original numeric string (or the fallback) as a string.
2029
+ */toNumber(pValue,pFallback){if(this.isNumeric(pValue)==='0'){return pFallback===undefined||pFallback===null?'0':String(pFallback);}return String(pValue).trim();}/**
1969
2030
  * Calculates the floor of a number precisely.
1970
2031
  *
1971
2032
  * @param {string|number} pValue - The number to calculate the floor value of.
@@ -2192,11 +2253,62 @@ let tmpCleanedObject={};let tmpKeys=Object.keys(pValueObject);for(let i=0;i<tmpK
2192
2253
  * Make a histogram of representative counts for exact values (.tostring() is the keys to count)
2193
2254
  * @param {Array} pValueSet
2194
2255
  * @param {string} pValueAddress
2195
- */histogramDistributionByExactValue(pValueObjectSet,pValueAddress,pManifest){if(!Array.isArray(pValueObjectSet)){return pValueObjectSet;}if(!pValueAddress){return{};}let tmpHistogram={};for(let i=0;i<pValueObjectSet.length;i++){let tmpValue=this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueAddress,pManifest).toString();if(!(tmpValue in tmpHistogram)){tmpHistogram[tmpValue]=0;}tmpHistogram[tmpValue]=tmpHistogram[tmpValue]+1;}return tmpHistogram;}histogramDistributionByExactValueFromInternalState(pValueObjectSetAddress,pValueAddress){if(!pValueObjectSetAddress){return{};}let tmpValueObjectSet=this.fable.Utility.getInternalValueByHash(pValueObjectSetAddress);return this.histogramDistributionByExactValue(tmpValueObjectSet,pValueAddress);}/**
2256
+ */histogramDistributionByExactValue(pValueObjectSet,pValueAddress,pManifest){if(!Array.isArray(pValueObjectSet)){return pValueObjectSet;}let tmpHistogram={};for(let i=0;i<pValueObjectSet.length;i++){// Without a value address, the set elements themselves are counted
2257
+ // (plain value sets); with one, count the addressed property.
2258
+ let tmpRawValue=pValueAddress?this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueAddress,pManifest):pValueObjectSet[i];if(tmpRawValue===undefined||tmpRawValue===null){continue;}let tmpValue=tmpRawValue.toString();if(!(tmpValue in tmpHistogram)){tmpHistogram[tmpValue]=0;}tmpHistogram[tmpValue]=tmpHistogram[tmpValue]+1;}return tmpHistogram;}histogramDistributionByExactValueFromInternalState(pValueObjectSetAddress,pValueAddress){if(!pValueObjectSetAddress){return{};}// The expression parser hands the resolved SET, not an address —
2259
+ // delegate directly so DISTRIBUTIONHISTOGRAM(set) works.
2260
+ if(Array.isArray(pValueObjectSetAddress)){return this.histogramDistributionByExactValue(pValueObjectSetAddress,pValueAddress);}let tmpValueObjectSet=this.fable.Utility.getInternalValueByHash(pValueObjectSetAddress);return this.histogramDistributionByExactValue(tmpValueObjectSet,pValueAddress);}/**
2196
2261
  * Make a histogram of representative counts for exact values (.tostring() is the keys to count)
2197
2262
  * @param {Array} pValueSet
2198
2263
  * @param {string} pValueAddress
2199
- */histogramAggregationByExactValue(pValueObjectSet,pValueAddress,pValueAmountAddress,pManifest){if(!Array.isArray(pValueObjectSet)){return pValueObjectSet;}if(!pValueAddress||!pValueAmountAddress){return{};}let tmpHistogram={};for(let i=0;i<pValueObjectSet.length;i++){let tmpValue=this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueAddress,pManifest);if(typeof tmpValue===undefined){continue;}let tmpAmount=this.parsePrecise(this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueAmountAddress,pManifest),NaN);if(!(tmpValue in tmpHistogram)){tmpHistogram[tmpValue]=0;}if(!isNaN(tmpAmount)){tmpHistogram[tmpValue]=this.addPrecise(tmpHistogram[tmpValue],tmpAmount);}}return tmpHistogram;}/**
2264
+ *//**
2265
+ * Analyze the density of an ordered value set and find natural boundary
2266
+ * positions that fit a budget — the generic core behind axis ticks, HTML
2267
+ * group headers, timeline scrubbers, and any other "too many labels"
2268
+ * presentation problem.
2269
+ *
2270
+ * Domain handling:
2271
+ * - Date-like sets: boundaries where values cross calendar units,
2272
+ * using the finest unit (day, week, month, quarter, year) that fits
2273
+ * the budget; labels formatted at the unit's natural grain.
2274
+ * - Numeric sets: boundaries where values cross multiples of a "nice"
2275
+ * step (1 / 2 / 2.5 / 5 x 10^k) chosen to fit the budget; labels are
2276
+ * the crossed multiples.
2277
+ * - Everything else (ordinal): uniform positional boundaries.
2278
+ *
2279
+ * Assumes the set is ordered (an axis/sequence); unordered input
2280
+ * degrades to ordinal handling.
2281
+ *
2282
+ * @param {Array} pValueSet - ordered values, one per position
2283
+ * @param {number} [pMaximumBoundaries=12] - boundary budget (clamped 2..100)
2284
+ * @return {{ SetType: string, Unit: string, Boundaries: Array<{Index: number, Value: any, Label: string}> }}
2285
+ */analyzeSetDensity(pValueSet,pMaximumBoundaries){let tmpBudget=parseInt(pMaximumBoundaries,10);tmpBudget=isNaN(tmpBudget)?12:Math.max(2,Math.min(100,tmpBudget));const tmpResult={SetType:'ordinal',Unit:'position',Boundaries:[]};if(!Array.isArray(pValueSet)||pValueSet.length<1){return tmpResult;}if(pValueSet.length<=tmpBudget){tmpResult.Boundaries=pValueSet.map((pValue,pIndex)=>({Index:pIndex,Value:pValue,Label:pValue===undefined||pValue===null?'':pValue.toString()}));return tmpResult;}const tmpNumbers=pValueSet.map(pValue=>this.parsePrecise(pValue,NaN));const tmpIsNumericSet=tmpNumbers.every(pNumber=>!isNaN(pNumber));const tmpDayJS=this.fable.Dates.dayJS;const tmpIsDateSet=!tmpIsNumericSet&&pValueSet.every(pValue=>pValue!==undefined&&pValue!==null&&pValue!==''&&tmpDayJS(pValue.toString()).isValid());if(tmpIsDateSet){tmpResult.SetType='date';const tmpUnitLadder=[{Unit:'day',Key:'YYYY-MM-DD',Label:'MMM D'},{Unit:'week',Key:'GGGG-WW',Label:'MMM D'},{Unit:'month',Key:'YYYY-MM',Label:'MMM YYYY'},{Unit:'quarter',Key:'YYYY-Q',Label:'[Q]Q YYYY'},{Unit:'year',Key:'YYYY',Label:'YYYY'}];const tmpParsed=pValueSet.map(pValue=>tmpDayJS(pValue.toString()));for(let u=0;u<tmpUnitLadder.length;u++){const tmpBucketKeys=tmpParsed.map(pDate=>pDate.format(tmpUnitLadder[u].Key));const tmpDistinct=new Set(tmpBucketKeys).size;if(tmpDistinct<=tmpBudget||u===tmpUnitLadder.length-1){tmpResult.Unit=tmpUnitLadder[u].Unit;const tmpBoundaryStride=Math.max(1,Math.ceil(tmpDistinct/tmpBudget));let tmpSeen={};let tmpBoundaryIndex=-1;for(let i=0;i<pValueSet.length;i++){if(tmpBucketKeys[i]in tmpSeen){continue;}tmpSeen[tmpBucketKeys[i]]=true;tmpBoundaryIndex++;if(tmpBoundaryIndex%tmpBoundaryStride!==0){continue;}tmpResult.Boundaries.push({Index:i,Value:pValueSet[i],Label:tmpParsed[i].format(tmpUnitLadder[u].Label)});}return tmpResult;}}}if(tmpIsNumericSet){tmpResult.SetType='number';// Nice step: smallest of {1, 2, 2.5, 5} x 10^k giving <= budget
2286
+ // boundary crossings over the value range.
2287
+ const tmpMinimum=Math.min(...tmpNumbers);const tmpMaximum=Math.max(...tmpNumbers);const tmpRange=tmpMaximum-tmpMinimum;if(tmpRange>0){const tmpRawStep=tmpRange/tmpBudget;const tmpMagnitude=Math.pow(10,Math.floor(Math.log10(tmpRawStep)));let tmpStep=10*tmpMagnitude;for(const tmpNice of[1,2,2.5,5]){if(tmpNice*tmpMagnitude>=tmpRawStep){tmpStep=tmpNice*tmpMagnitude;break;}}tmpResult.Unit=`step:${tmpStep}`;let tmpLastBucket=null;for(let i=0;i<pValueSet.length;i++){const tmpBucket=Math.floor(tmpNumbers[i]/tmpStep);if(tmpBucket===tmpLastBucket){continue;}tmpLastBucket=tmpBucket;const tmpNiceValue=tmpBucket*tmpStep;tmpResult.Boundaries.push({Index:i,Value:pValueSet[i],Label:this.roundPrecise(tmpNiceValue,6).toString()});}if(tmpResult.Boundaries.length<=tmpBudget){return tmpResult;}// Non-monotonic data can cross steps many times — degrade.
2288
+ tmpResult.Boundaries=[];}}// Ordinal: uniform positional stride.
2289
+ tmpResult.SetType=tmpResult.SetType==='ordinal'?'ordinal':tmpResult.SetType;tmpResult.Unit='position';const tmpStride=Math.ceil(pValueSet.length/tmpBudget);for(let i=0;i<pValueSet.length;i+=tmpStride){tmpResult.Boundaries.push({Index:i,Value:pValueSet[i],Label:pValueSet[i]===undefined||pValueSet[i]===null?'':pValueSet[i].toString()});}return tmpResult;}/**
2290
+ * Chart-shaped projection of analyzeSetDensity: a sparse label array of
2291
+ * the SAME LENGTH as the input ('' at non-boundary positions), dropping
2292
+ * directly into a category chart's labels with alignment preserved.
2293
+ *
2294
+ * @param {Array} pValueSet
2295
+ * @param {number} [pMaximumTicks=12]
2296
+ * @return {Array<string>}
2297
+ */chartTickLabels(pValueSet,pMaximumTicks){if(!Array.isArray(pValueSet)){return pValueSet;}const tmpAnalysis=this.analyzeSetDensity(pValueSet,pMaximumTicks);const tmpLabels=new Array(pValueSet.length).fill('');for(const tmpBoundary of tmpAnalysis.Boundaries){tmpLabels[tmpBoundary.Index]=tmpBoundary.Label;}return tmpLabels;}/**
2298
+ * Collect (join) per-key string values from a set of objects — the
2299
+ * string-valued sibling of histogramAggregationByExactValue. Returns an
2300
+ * insertion-ordered { key: "v1<separator>v2..." } object, so
2301
+ * OBJECTKEYSTOARRAY / OBJECTVALUESTOARRAY produce aligned parallel
2302
+ * arrays. Values join verbatim (no numeric coercion); undefined/null
2303
+ * values are skipped.
2304
+ *
2305
+ * @param {Array} pValueObjectSet
2306
+ * @param {string} pValueAddress - address of the grouping key on each object
2307
+ * @param {string} pValueJoinAddress - address of the value to collect
2308
+ * @param {string} pSeparator - separator placed between collected values
2309
+ * @param {object} pManifest
2310
+ * @return {Object<string, string>}
2311
+ */histogramAggregationJoinByExactValue(pValueObjectSet,pValueAddress,pValueJoinAddress,pSeparator,pManifest){if(!Array.isArray(pValueObjectSet)){return pValueObjectSet;}if(!pValueAddress||!pValueJoinAddress){return{};}let tmpSeparator=typeof pSeparator==='string'?pSeparator:',';let tmpHistogram={};for(let i=0;i<pValueObjectSet.length;i++){let tmpKey=this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueAddress,pManifest);if(tmpKey===undefined||tmpKey===null){continue;}tmpKey=tmpKey.toString();let tmpValue=this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueJoinAddress,pManifest);if(tmpValue===undefined||tmpValue===null){continue;}tmpHistogram[tmpKey]=tmpKey in tmpHistogram?tmpHistogram[tmpKey]+tmpSeparator+tmpValue.toString():tmpValue.toString();}return tmpHistogram;}histogramAggregationByExactValue(pValueObjectSet,pValueAddress,pValueAmountAddress,pManifest){if(!Array.isArray(pValueObjectSet)){return pValueObjectSet;}if(!pValueAddress||!pValueAmountAddress){return{};}let tmpHistogram={};for(let i=0;i<pValueObjectSet.length;i++){let tmpValue=this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueAddress,pManifest);if(typeof tmpValue===undefined){continue;}let tmpAmount=this.parsePrecise(this.fable.Utility.getValueByHash(pValueObjectSet[i],pValueAmountAddress,pManifest),NaN);if(!(tmpValue in tmpHistogram)){tmpHistogram[tmpValue]=0;}if(!isNaN(tmpAmount)){tmpHistogram[tmpValue]=this.addPrecise(tmpHistogram[tmpValue],tmpAmount);}}return tmpHistogram;}/**
2200
2312
  * Aggregates a histogram by exact value from an internal state object.
2201
2313
  *
2202
2314
  * @param {string} pValueObjectSetAddress - The address of the internal value object set.
@@ -6106,7 +6218,7 @@ try{if(!global.localStorage)return false;}catch(_){return false;}var val=global.
6106
6218
  // presumably different callback function.
6107
6219
  // This makes sure that own properties are retained, so that
6108
6220
  // decorations and such are not lost along the way.
6109
- module.exports=wrappy;function wrappy(fn,cb){if(fn&&cb)return wrappy(fn)(cb);if(typeof fn!=='function')throw new TypeError('need wrapper function');Object.keys(fn).forEach(function(k){wrapper[k]=fn[k];});return wrapper;function wrapper(){var args=new Array(arguments.length);for(var i=0;i<args.length;i++){args[i]=arguments[i];}var ret=fn.apply(this,args);var cb=args[args.length-1];if(typeof ret==='function'&&ret!==cb){Object.keys(cb).forEach(function(k){ret[k]=cb[k];});}return ret;}}},{}],191:[function(require,module,exports){module.exports=extend;var hasOwnProperty=Object.prototype.hasOwnProperty;function extend(){var target={};for(var i=0;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;}},{}],192:[function(require,module,exports){module.exports={"name":"pict","version":"1.0.386","description":"Pict browser library.","main":"source/Pict.js","scripts":{"start":"node source/Pict.js","test":"npx quack test","tests":"npx quack test -g","coverage":"npx quack coverage","build":"npx quack build","prepublishOnly":"npx quack build","docker-dev-build":"docker build ./ -f Dockerfile_LUXURYCode -t pict-image:local","docker-dev-run":"docker run -it -d --name pict-dev -p 37447:8080 -p 19506:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/pict\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" pict-image:local","docker-dev-shell":"docker exec -it pict-dev /bin/bash","lint":"eslint source/**/*.js test/**/*.js","types":"tsc -p ."},"files":["source/","dist/","types/"],"types":"types/source/Pict.d.ts","mocha":{"diff":true,"extension":["js"],"package":"./package.json","reporter":"spec","slow":"75","timeout":"5000","ui":"tdd","watch-files":["source/**/*.js","test/**/*.js"],"watch-ignore":["lib/vendor"]},"repository":{"type":"git","url":"git+https://stevenvelozo@github.com/stevenvelozo/pict.git"},"author":"steven velozo <steven@velozo.com>","license":"MIT","bugs":{"url":"https://github.com/fable-retold/pict/issues"},"homepage":"https://github.com/fable-retold/pict#readme","devDependencies":{"@eslint/js":"^9.39.1","@types/jquery":"^3.5.33","@types/sinon":"^17.0.4","eslint":"^9.39.1","globals":"^16.5.0","pict-docuserve":"^1.4.19","quackage":"^1.3.0","sinon":"^20.0.0","typescript":"^5.9.3"},"dependencies":{"cachetrax":"^1.0.6","fable":"^3.1.78","pict-application":"^1.0.34","pict-provider":"^1.0.13","pict-template":"^1.0.15","pict-view":"^1.0.68"}};},{}],193:[function(require,module,exports){// This assumes Pict has been required in the browser. Delcare these as globals so linter can do its job.
6221
+ module.exports=wrappy;function wrappy(fn,cb){if(fn&&cb)return wrappy(fn)(cb);if(typeof fn!=='function')throw new TypeError('need wrapper function');Object.keys(fn).forEach(function(k){wrapper[k]=fn[k];});return wrapper;function wrapper(){var args=new Array(arguments.length);for(var i=0;i<args.length;i++){args[i]=arguments[i];}var ret=fn.apply(this,args);var cb=args[args.length-1];if(typeof ret==='function'&&ret!==cb){Object.keys(cb).forEach(function(k){ret[k]=cb[k];});}return ret;}}},{}],191:[function(require,module,exports){module.exports=extend;var hasOwnProperty=Object.prototype.hasOwnProperty;function extend(){var target={};for(var i=0;i<arguments.length;i++){var source=arguments[i];for(var key in source){if(hasOwnProperty.call(source,key)){target[key]=source[key];}}}return target;}},{}],192:[function(require,module,exports){module.exports={"name":"pict","version":"1.0.388","description":"Pict browser library.","main":"source/Pict.js","scripts":{"start":"node source/Pict.js","test":"npx quack test","tests":"npx quack test -g","coverage":"npx quack coverage","build":"npx quack build","prepublishOnly":"npx quack build","docker-dev-build":"docker build ./ -f Dockerfile_LUXURYCode -t pict-image:local","docker-dev-run":"docker run -it -d --name pict-dev -p 37447:8080 -p 19506:8086 -v \"$PWD/.config:/home/coder/.config\" -v \"$PWD:/home/coder/pict\" -u \"$(id -u):$(id -g)\" -e \"DOCKER_USER=$USER\" pict-image:local","docker-dev-shell":"docker exec -it pict-dev /bin/bash","lint":"eslint source/**/*.js test/**/*.js","types":"tsc -p ."},"files":["source/","dist/","types/"],"types":"types/source/Pict.d.ts","mocha":{"diff":true,"extension":["js"],"package":"./package.json","reporter":"spec","slow":"75","timeout":"5000","ui":"tdd","watch-files":["source/**/*.js","test/**/*.js"],"watch-ignore":["lib/vendor"]},"repository":{"type":"git","url":"git+https://stevenvelozo@github.com/stevenvelozo/pict.git"},"author":"steven velozo <steven@velozo.com>","license":"MIT","bugs":{"url":"https://github.com/fable-retold/pict/issues"},"homepage":"https://github.com/fable-retold/pict#readme","devDependencies":{"@eslint/js":"^9.39.1","@types/jquery":"^3.5.33","@types/sinon":"^17.0.4","eslint":"^9.39.1","globals":"^16.5.0","pict-docuserve":"^1.4.19","quackage":"^1.3.0","sinon":"^20.0.0","typescript":"^5.9.3"},"dependencies":{"cachetrax":"^1.0.6","fable":"^3.1.78","pict-application":"^1.0.34","pict-provider":"^1.0.13","pict-template":"^1.0.15","pict-view":"^1.0.68"}};},{}],193:[function(require,module,exports){// This assumes Pict has been required in the browser. Delcare these as globals so linter can do its job.
6110
6222
  /* global Pict, _Pict: writeable *//**
6111
6223
  * Simple function to load a pict Application
6112
6224
  *
@@ -6323,12 +6435,108 @@ const tmpTargetElement=typeof pAddress==='string'?window.jQuery(pAddress):window
6323
6435
  */toggleClass(pAddress,pClass){if(this.hasClass(pAddress,pClass)){this.removeClass(pAddress,pClass);}else{this.addClass(pAddress,pClass);}}}module.exports=PictContentAssignment;},{"fable":68}],197:[function(require,module,exports){const libFableServiceBase=require('fable').ServiceProviderBase;class PictDataProvider extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);/** @type {import('fable') & import('./Pict')} */this.fable;this.serviceType='PictDataProvider';}/**
6324
6436
  * @param {string} pAddress - The address of the data to retrieve
6325
6437
  * @param {object} [pData] - (optional) The record to provide to the address resolver
6326
- */getDataByAddress(pAddress,pData){let tmpData=typeof pData==='undefined'?{}:pData;const tmpAddressSpace={Fable:this.fable,Pict:this.fable,AppData:this.fable.AppData,Bundle:this.fable.Bundle,Record:tmpData};return this.fable.manifest.getValueByHash(tmpAddressSpace,pAddress);}}module.exports=PictDataProvider;},{"fable":68}],198:[function(require,module,exports){const libFableServiceBase=require('fable').ServiceProviderBase;class PictMeadowEntityProvider extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){var _this$fable$RestClien;super(pFable,pOptions,pServiceHash);/** @type {any} */this.options;/** @type {import('./Pict') & { settings: any } & { newAnticipate: any }} */this.fable;/** @type {any} */this.log;this.serviceType='PictMeadowProvider';if(this.fable.settings.PictDefaultURLPrefix){this.options.urlPrefix=this.fable.settings.PictDefaultURLPrefix;}else if(!this.options.urlPrefix){this.options.urlPrefix='/1.0/';}if(!this.options.downloadBatchSize){if(typeof this.fable.settings.PictDefaultDownloadBatchSize==='number'){this.options.downloadBatchSize=this.fable.settings.PictDefaultDownloadBatchSize;}else{this.options.downloadBatchSize=100;}}if(typeof this.options.downloadPageConcurrency!=='number'){this.options.downloadPageConcurrency=typeof this.fable.settings.PictDefaultDownloadPageConcurrency==='number'?this.fable.settings.PictDefaultDownloadPageConcurrency:4;}if(typeof this.options.maxBundleConcurrency!=='number'){this.options.maxBundleConcurrency=typeof this.fable.settings.PictDefaultMaxBundleConcurrency==='number'?this.fable.settings.PictDefaultMaxBundleConcurrency:8;}//@ts-ignore - FIXME - remove once we have fable types
6438
+ */getDataByAddress(pAddress,pData){let tmpData=typeof pData==='undefined'?{}:pData;const tmpAddressSpace={Fable:this.fable,Pict:this.fable,AppData:this.fable.AppData,Bundle:this.fable.Bundle,Record:tmpData};return this.fable.manifest.getValueByHash(tmpAddressSpace,pAddress);}}module.exports=PictDataProvider;},{"fable":68}],198:[function(require,module,exports){const libFableServiceBase=require('fable').ServiceProviderBase;// Minimum meadow-endpoints version, per major, that serves the body-driven
6439
+ // POST /:Entity/Query read route. The route was added in 2.1.0 and 4.1.0 but
6440
+ // never shipped on the 3.x line or 4.0.x — so support is NOT monotonic across
6441
+ // majors and a flat ">= x" comparison would be wrong. Used only as a fallback
6442
+ // when the server does not advertise an explicit Capabilities flag.
6443
+ const QUERY_ENDPOINT_MIN_VERSION_BY_MAJOR={2:'2.1.0',4:'4.1.0'};class PictMeadowEntityProvider extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){var _this$fable$RestClien;super(pFable,pOptions,pServiceHash);/** @type {any} */this.options;/** @type {import('./Pict') & { settings: any } & { newAnticipate: any }} */this.fable;/** @type {any} */this.log;this.serviceType='PictMeadowProvider';if(this.fable.settings.PictDefaultURLPrefix){this.options.urlPrefix=this.fable.settings.PictDefaultURLPrefix;}else if(!this.options.urlPrefix){this.options.urlPrefix='/1.0/';}if(!this.options.downloadBatchSize){if(typeof this.fable.settings.PictDefaultDownloadBatchSize==='number'){this.options.downloadBatchSize=this.fable.settings.PictDefaultDownloadBatchSize;}else{this.options.downloadBatchSize=100;}}if(typeof this.options.downloadPageConcurrency!=='number'){this.options.downloadPageConcurrency=typeof this.fable.settings.PictDefaultDownloadPageConcurrency==='number'?this.fable.settings.PictDefaultDownloadPageConcurrency:4;}if(typeof this.options.maxBundleConcurrency!=='number'){this.options.maxBundleConcurrency=typeof this.fable.settings.PictDefaultMaxBundleConcurrency==='number'?this.fable.settings.PictDefaultMaxBundleConcurrency:8;}//@ts-ignore - FIXME - remove once we have fable types
6327
6444
  this.restClient=(_this$fable$RestClien=this.fable.RestClient)!==null&&_this$fable$RestClien!==void 0?_this$fable$RestClien:this.fable.instantiateServiceProviderWithoutRegistration('RestClient');/** @type {Record<string, import('cachetrax')>} */this.recordCache={};/** @type {Record<string, import('cachetrax')>} */this.recordSetCache={};this.entityColumnTranslations={CreatingIDUser:'User',UpdatingIDUser:'User',DeletingIDUser:'User'};/** @type {(pOptions: Record<string, any>) => Record<string, any>} */this.prepareRequestOptions=pOptions=>{return pOptions;};/**
6328
6445
  * After buildBundleWaves() is called by gatherDataFromServer(), this
6329
6446
  * property holds the computed wave schedule for inspection/debugging.
6330
6447
  * @type {Array<Array<{Index: number, Step: Record<string, any>}>>|null}
6331
- */this.lastBundleWaves=null;}/**
6448
+ */this.lastBundleWaves=null;// Master switch for the body-driven POST /:Entity/Query transport. When
6449
+ // false the provider always uses the legacy GET reads (no capability
6450
+ // probe is ever issued). Defaults on; opt out via option or setting.
6451
+ if(typeof this.options.UseQueryEndpoint==='boolean'){this.useQueryEndpoint=this.options.UseQueryEndpoint;}else if(typeof this.fable.settings.PictMeadowUseQueryEndpoint==='boolean'){this.useQueryEndpoint=this.fable.settings.PictMeadowUseQueryEndpoint;}else{this.useQueryEndpoint=true;}/**
6452
+ * Per-(urlPrefix, entity) capability cache. Different entities can resolve
6453
+ * to different backend services (and thus different meadow-endpoints
6454
+ * versions) behind the same urlPrefix, so support is cached per entity.
6455
+ * @type {Record<string, { SupportsQuery: boolean, Metadata: (Record<string, any>|null) }>}
6456
+ */this.endpointCapabilityCache={};/**
6457
+ * In-flight capability probes, keyed identically to the cache, so
6458
+ * concurrent reads of the same entity collapse onto a single Schema probe.
6459
+ * @type {Record<string, Array<(pError: Error|null, pSupportsQuery: boolean) => void>>}
6460
+ */this.endpointCapabilityInflight={};}/**
6461
+ * Compute the capability cache key for an (entity, urlPrefix) pair.
6462
+ *
6463
+ * @param {string} pEntity - The entity name.
6464
+ * @param {string} [pURLPrefix] - The URL prefix in play (defaults to the provider default).
6465
+ * @return {string} The cache key.
6466
+ */_capabilityKey(pEntity,pURLPrefix){return`${pURLPrefix||this.options.urlPrefix}::${pEntity}`;}/**
6467
+ * Major-version-aware check of whether a meadow-endpoints version string
6468
+ * serves the POST /:Entity/Query route. Support is keyed off the major
6469
+ * version (see QUERY_ENDPOINT_MIN_VERSION_BY_MAJOR) because the route was
6470
+ * backported to 2.1.0 and added in 4.1.0, but absent on 3.x and 4.0.x.
6471
+ *
6472
+ * @param {string} pVersion - A semver string (e.g. '4.1.0').
6473
+ * @return {boolean} True if the version is known to serve the Query route.
6474
+ */isMeadowEndpointsVersionQueryCapable(pVersion){if(typeof pVersion!=='string'){return false;}const tmpParts=pVersion.split('.').map(pPart=>{return parseInt(pPart,10);});if(tmpParts.length<1||isNaN(tmpParts[0])){return false;}const tmpMajor=tmpParts[0];const tmpMinimum=QUERY_ENDPOINT_MIN_VERSION_BY_MAJOR[tmpMajor];if(!tmpMinimum){return false;}const tmpMinimumParts=tmpMinimum.split('.').map(pPart=>{return parseInt(pPart,10);});// Lexicographic compare of [major, minor, patch] with missing parts as 0.
6475
+ for(let i=0;i<3;i++){const tmpHave=isNaN(tmpParts[i])?0:tmpParts[i];const tmpNeed=isNaN(tmpMinimumParts[i])?0:tmpMinimumParts[i];if(tmpHave>tmpNeed){return true;}if(tmpHave<tmpNeed){return false;}}return true;}/**
6476
+ * Decide, from a Schema endpoint response body, whether the serving
6477
+ * meadow-endpoints supports the POST /:Entity/Query route. An explicit
6478
+ * Capabilities flag wins when present; otherwise fall back to a
6479
+ * major-version-aware check of the advertised meadow-endpoints version.
6480
+ * Servers that advertise nothing (older deployments) are unsupported.
6481
+ *
6482
+ * @param {Record<string, any>} pSchemaBody - The Schema endpoint response body.
6483
+ * @return {boolean} True if POST /:Entity/Query is supported.
6484
+ */evaluateQueryEndpointSupport(pSchemaBody){if(!pSchemaBody||typeof pSchemaBody!=='object'){return false;}const tmpMetadata=pSchemaBody.RetoldMetadata;if(!tmpMetadata||typeof tmpMetadata!=='object'){return false;}if(tmpMetadata.Capabilities&&typeof tmpMetadata.Capabilities.QueryEndpoint==='boolean'){return tmpMetadata.Capabilities.QueryEndpoint;}const tmpVersion=tmpMetadata.PackageVersions&&tmpMetadata.PackageVersions['meadow-endpoints'];if(typeof tmpVersion==='string'){return this.isMeadowEndpointsVersionQueryCapable(tmpVersion);}return false;}/**
6485
+ * Seed the capability cache for an entity from a Schema response the caller
6486
+ * already has in hand (e.g. pict-section-recordset fetches the schema during
6487
+ * initialization). Avoids a redundant capability probe.
6488
+ *
6489
+ * @param {string} pEntity - The entity name.
6490
+ * @param {Record<string, any>} pSchemaBody - The Schema endpoint response body.
6491
+ * @param {string} [pURLPrefix] - The URL prefix the schema was fetched from.
6492
+ * @return {boolean} The resolved support value now cached.
6493
+ */primeEntityCapabilityFromSchema(pEntity,pSchemaBody,pURLPrefix){const tmpSupportsQuery=this.evaluateQueryEndpointSupport(pSchemaBody);this.endpointCapabilityCache[this._capabilityKey(pEntity,pURLPrefix)]={SupportsQuery:tmpSupportsQuery,Metadata:pSchemaBody&&pSchemaBody.RetoldMetadata||null};return tmpSupportsQuery;}/**
6494
+ * Resolve whether POST /:Entity/Query is usable for an entity, probing the
6495
+ * Schema endpoint once and caching the result per (urlPrefix, entity).
6496
+ * Concurrent calls for the same key collapse onto a single probe.
6497
+ *
6498
+ * Probe failures (network/parse) are NOT cached — they resolve to false (GET
6499
+ * fallback) for this call but allow a later retry, so a transient blip does
6500
+ * not permanently disable the faster transport for the session. A successful
6501
+ * probe of an older server (no metadata) caches false and never re-probes.
6502
+ *
6503
+ * @param {string} pEntity - The entity name.
6504
+ * @param {string} pURLPrefix - The URL prefix in play.
6505
+ * @param {(pError: Error|null, pSupportsQuery: boolean) => void} fCallback - Completion callback.
6506
+ * @return {void}
6507
+ */resolveEntityQuerySupport(pEntity,pURLPrefix,fCallback){if(!this.useQueryEndpoint){return fCallback(null,false);}const tmpKey=this._capabilityKey(pEntity,pURLPrefix);if(tmpKey in this.endpointCapabilityCache){return fCallback(null,this.endpointCapabilityCache[tmpKey].SupportsQuery);}if(this.endpointCapabilityInflight[tmpKey]){this.endpointCapabilityInflight[tmpKey].push(fCallback);return;}this.endpointCapabilityInflight[tmpKey]=[fCallback];/** @type {Record<string, any>} */let tmpOptions={url:`${pURLPrefix||this.options.urlPrefix}${pEntity}/Schema`};tmpOptions=this.prepareRequestOptions(tmpOptions);this.restClient.getJSON(tmpOptions,(pError,pResponse,pBody)=>{let tmpSupportsQuery=false;if(!pError){try{tmpSupportsQuery=this.evaluateQueryEndpointSupport(pBody);}catch(pEvaluateError){tmpSupportsQuery=false;}// Only cache on a clean probe; transient errors stay re-probeable.
6508
+ this.endpointCapabilityCache[tmpKey]={SupportsQuery:tmpSupportsQuery,Metadata:pBody&&pBody.RetoldMetadata||null};}else{this.log.warn(`EntityProvider capability probe for [${pEntity}] failed; falling back to GET reads: ${pError}`);}const tmpCallbacks=this.endpointCapabilityInflight[tmpKey];delete this.endpointCapabilityInflight[tmpKey];for(let i=0;i<tmpCallbacks.length;i++){tmpCallbacks[i](null,tmpSupportsQuery);}});}/**
6509
+ * Build the POST /:Entity/Query request body for a filtered read.
6510
+ *
6511
+ * @param {string} pMeadowFilterExpression - The meadow filter string (may be empty).
6512
+ * @param {number|null} [pBegin] - Pagination start cursor.
6513
+ * @param {number|null} [pCap] - Pagination page size.
6514
+ * @param {Record<string, any>} [pProjection] - Optional { Mode:'LiteExtended', ExtraColumns:[...] }.
6515
+ * @return {Record<string, any>} The request body envelope.
6516
+ */_buildQueryReadBody(pMeadowFilterExpression,pBegin,pCap,pProjection){/** @type {Record<string, any>} */const tmpBody={};if(pMeadowFilterExpression){tmpBody.Filter=pMeadowFilterExpression;}if(typeof pBegin==='number'){tmpBody.Begin=pBegin;}if(typeof pCap==='number'){tmpBody.Cap=pCap;}if(pProjection&&pProjection.Mode==='LiteExtended'&&Array.isArray(pProjection.ExtraColumns)&&pProjection.ExtraColumns.length>0){// LiteExtended GET maps to a Lite read carrying ExtraColumns on Query.
6517
+ tmpBody.Lite=true;tmpBody.ExtraColumns=pProjection.ExtraColumns.join(',');}return tmpBody;}/**
6518
+ * Read a page of an entity set, using POST /:Entity/Query when supported and
6519
+ * falling back to the legacy GET read otherwise. The callback mirrors
6520
+ * restClient.getJSON exactly: (pError, pResponse, pBody).
6521
+ *
6522
+ * @param {string} pEntity - The entity name.
6523
+ * @param {string} pMeadowFilterExpression - The meadow filter string (may be empty).
6524
+ * @param {number|null} pBegin - Pagination start cursor (null for unpaged).
6525
+ * @param {number|null} pCap - Pagination page size (null for unpaged).
6526
+ * @param {Record<string, any>} pReadOptions - { SupportsQuery, URLPrefix, Postfix, Projection }.
6527
+ * @param {(pError: Error|null, pResponse: any, pBody: any) => void} fCallback - Completion callback.
6528
+ * @return {void}
6529
+ */_readEntityPage(pEntity,pMeadowFilterExpression,pBegin,pCap,pReadOptions,fCallback){const tmpURLPrefix=pReadOptions&&pReadOptions.URLPrefix||this.options.urlPrefix;const tmpPostfix=pReadOptions&&pReadOptions.Postfix||'';const tmpProjection=pReadOptions?pReadOptions.Projection:null;if(pReadOptions&&pReadOptions.SupportsQuery){const tmpRequestOptions={url:`${tmpURLPrefix}${pEntity}s/Query${tmpPostfix}`,body:this._buildQueryReadBody(pMeadowFilterExpression,pBegin,pCap,tmpProjection)};return this.restClient.postJSON(tmpRequestOptions,fCallback);}const tmpFilterStanza=pMeadowFilterExpression?`/FilteredTo/${pMeadowFilterExpression}`:'';const tmpProjectionStanza=tmpProjection&&tmpProjection.Mode==='LiteExtended'&&Array.isArray(tmpProjection.ExtraColumns)&&tmpProjection.ExtraColumns.length>0?`/LiteExtended/${tmpProjection.ExtraColumns.join(',')}`:'';const tmpPageStanza=typeof pBegin==='number'&&typeof pCap==='number'?`/${pBegin}/${pCap}`:'';const tmpURL=`${tmpURLPrefix}${pEntity}s${tmpProjectionStanza}${tmpFilterStanza}${tmpPageStanza}${tmpPostfix}`;return this.restClient.getJSON(tmpURL,fCallback);}/**
6530
+ * Read the count of an entity set, using POST /:Entity/Query (Count mode)
6531
+ * when supported and the legacy GET Count otherwise. The callback mirrors
6532
+ * restClient.getJSON: (pError, pResponse, pBody) where pBody carries .Count.
6533
+ *
6534
+ * @param {string} pEntity - The entity name.
6535
+ * @param {string} pMeadowFilterExpression - The meadow filter string (may be empty).
6536
+ * @param {Record<string, any>} pReadOptions - { SupportsQuery, URLPrefix, Postfix }.
6537
+ * @param {(pError: Error|null, pResponse: any, pBody: any) => void} fCallback - Completion callback.
6538
+ * @return {void}
6539
+ */_readEntityCount(pEntity,pMeadowFilterExpression,pReadOptions,fCallback){const tmpURLPrefix=pReadOptions&&pReadOptions.URLPrefix||this.options.urlPrefix;const tmpPostfix=pReadOptions&&pReadOptions.Postfix||'';if(pReadOptions&&pReadOptions.SupportsQuery){const tmpBody=this._buildQueryReadBody(pMeadowFilterExpression,null,null,null);tmpBody.Count=true;const tmpRequestOptions={url:`${tmpURLPrefix}${pEntity}s/Query${tmpPostfix}`,body:tmpBody};return this.restClient.postJSON(tmpRequestOptions,fCallback);}const tmpFilterStanza=pMeadowFilterExpression?`/FilteredTo/${pMeadowFilterExpression}`:'';const tmpURL=`${tmpURLPrefix}${pEntity}s/Count${tmpFilterStanza}${tmpPostfix}`;return this.restClient.getJSON(tmpURL,fCallback);}/**
6332
6540
  * Compute the cache bucket key for an entity, optionally namespaced by a scope.
6333
6541
  * A non-empty scope yields an isolated bucket (`Entity::Scope`) so scoped/partial
6334
6542
  * (e.g. Lite) records never touch the global `Entity` cache that full-record
@@ -6422,7 +6630,7 @@ this.fable.manifest.setValueByHash(tmpDestinationEntity,tmpBucketAddress,tmpSour
6422
6630
  *
6423
6631
  * @param {Record<string, any>} pConfiguration - The configuration object for the dataset projection.
6424
6632
  * @param {Record<string, any>} pContext - The context object to use when parsing the record prototype template and resolving the output recordset address mapping.
6425
- */projectDataset(pConfiguration,pContext){let tmpInputRecordset=this.fable.manifest.getValueByHash(pContext,pConfiguration.InputRecordsetAddress);if(typeof tmpInputRecordset==null||typeof tmpInputRecordset!=='object'){throw new Error(`EntityBundleRequest failed to project dataset because the input recordset [${pConfiguration.InputRecordsetAddress}] did not return an valid object.`);}if(!Array.isArray(tmpInputRecordset)){tmpInputRecordset=[tmpInputRecordset];}let tmpDefaultOutputRecordset=this.fable.manifest.getValueByHash(pContext,pConfiguration.OutputRecordsetAddress);if(!tmpDefaultOutputRecordset){tmpDefaultOutputRecordset=[];this.fable.manifest.setValueByHash(pContext,pConfiguration.OutputRecordsetAddress,tmpDefaultOutputRecordset);}for(const tmpInputRecord of tmpInputRecordset){let tmpOutputRecordset=tmpDefaultOutputRecordset;let tmpOutputRecordsetAddressOverride;if(typeof pConfiguration.OutputRecordsetAddressMapping==='object'){tmpOutputRecordsetAddressOverride=this._resolveOutputRecordsetAddressMapping(pConfiguration,pContext,tmpInputRecord);if(tmpOutputRecordsetAddressOverride){tmpOutputRecordset=this.fable.manifest.getValueByHash(pContext,tmpOutputRecordsetAddressOverride);if(!tmpOutputRecordset){tmpOutputRecordset=[];this.fable.manifest.setValueByHash(pContext,tmpOutputRecordsetAddressOverride,tmpOutputRecordset);}}if(!tmpOutputRecordset||!Array.isArray(tmpOutputRecordset)){tmpOutputRecordset=tmpDefaultOutputRecordset;}}const tmpPrototypeAddress=this.fable.parseTemplate(pConfiguration.RecordPrototypeAddress,Object.assign({InputRecord:tmpInputRecord},pContext));const tmpRecordPrototype=this.fable.manifest.getValueByHash(Object.assign({InputRecord:tmpInputRecord,OutputRecordset:tmpOutputRecordset},pContext),tmpPrototypeAddress);let tmpOutputRecord={};if(Array.isArray(tmpRecordPrototype)&&tmpRecordPrototype.length>0){tmpOutputRecord=tmpRecordPrototype[0];}else{tmpOutputRecordset.push(tmpOutputRecord);}let tmpRecordFieldMapping=pConfiguration.RecordFieldMapping[tmpOutputRecordsetAddressOverride]||pConfiguration.RecordFieldMapping.Default;if(!tmpRecordFieldMapping){tmpRecordFieldMapping=pConfiguration.RecordFieldMapping[Object.keys(pConfiguration.RecordFieldMapping)[0]];}if(!tmpRecordFieldMapping){throw new Error(`EntityBundleRequest failed to project dataset because the record field mapping for [${tmpOutputRecordsetAddressOverride}] did not return a mapping.`);}for(const tmpInputFieldAddress of Object.keys(tmpRecordFieldMapping)){const tmpOutputFieldAddress=tmpRecordFieldMapping[tmpInputFieldAddress];const tmpInputFieldValue=this.fable.manifest.getValueByHash(Object.assign({InputRecord:tmpInputRecord},pContext),tmpInputFieldAddress);this.fable.manifest.setValueByHash(Object.assign({OutputRecord:tmpOutputRecord},pContext),tmpOutputFieldAddress,tmpInputFieldValue);}}}/**
6633
+ */projectDataset(pConfiguration,pContext){let tmpInputRecordset=this.fable.manifest.getValueByHash(pContext,pConfiguration.InputRecordsetAddress);if(tmpInputRecordset==null||typeof tmpInputRecordset!=='object'){throw new Error(`EntityBundleRequest failed to project dataset because the input recordset [${pConfiguration.InputRecordsetAddress}] did not return an valid object.`);}if(!Array.isArray(tmpInputRecordset)){tmpInputRecordset=[tmpInputRecordset];}let tmpDefaultOutputRecordset=this.fable.manifest.getValueByHash(pContext,pConfiguration.OutputRecordsetAddress);if(!tmpDefaultOutputRecordset){tmpDefaultOutputRecordset=[];this.fable.manifest.setValueByHash(pContext,pConfiguration.OutputRecordsetAddress,tmpDefaultOutputRecordset);}for(const tmpInputRecord of tmpInputRecordset){let tmpOutputRecordset=tmpDefaultOutputRecordset;let tmpOutputRecordsetAddressOverride;if(typeof pConfiguration.OutputRecordsetAddressMapping==='object'){tmpOutputRecordsetAddressOverride=this._resolveOutputRecordsetAddressMapping(pConfiguration,pContext,tmpInputRecord);if(tmpOutputRecordsetAddressOverride){tmpOutputRecordset=this.fable.manifest.getValueByHash(pContext,tmpOutputRecordsetAddressOverride);if(!tmpOutputRecordset){tmpOutputRecordset=[];this.fable.manifest.setValueByHash(pContext,tmpOutputRecordsetAddressOverride,tmpOutputRecordset);}}if(!tmpOutputRecordset||!Array.isArray(tmpOutputRecordset)){tmpOutputRecordset=tmpDefaultOutputRecordset;}}const tmpPrototypeAddress=this.fable.parseTemplate(pConfiguration.RecordPrototypeAddress,Object.assign({InputRecord:tmpInputRecord},pContext));const tmpRecordPrototype=this.fable.manifest.getValueByHash(Object.assign({InputRecord:tmpInputRecord,OutputRecordset:tmpOutputRecordset},pContext),tmpPrototypeAddress);let tmpOutputRecord={};if(Array.isArray(tmpRecordPrototype)&&tmpRecordPrototype.length>0){tmpOutputRecord=tmpRecordPrototype[0];}else{tmpOutputRecordset.push(tmpOutputRecord);}let tmpRecordFieldMapping=pConfiguration.RecordFieldMapping[tmpOutputRecordsetAddressOverride]||pConfiguration.RecordFieldMapping.Default;if(!tmpRecordFieldMapping){tmpRecordFieldMapping=pConfiguration.RecordFieldMapping[Object.keys(pConfiguration.RecordFieldMapping)[0]];}if(!tmpRecordFieldMapping){throw new Error(`EntityBundleRequest failed to project dataset because the record field mapping for [${tmpOutputRecordsetAddressOverride}] did not return a mapping.`);}for(const tmpInputFieldAddress of Object.keys(tmpRecordFieldMapping)){const tmpOutputFieldAddress=tmpRecordFieldMapping[tmpInputFieldAddress];const tmpInputFieldValue=this.fable.manifest.getValueByHash(Object.assign({InputRecord:tmpInputRecord},pContext),tmpInputFieldAddress);this.fable.manifest.setValueByHash(Object.assign({OutputRecord:tmpOutputRecord},pContext),tmpOutputFieldAddress,tmpInputFieldValue);}}}/**
6426
6634
  * @param {Record<string, any>} pConfiguration - The configuration object for the dataset projection.
6427
6635
  * @param {Record<string, any>} pContext - The context object to use when resolving the output recordset address mapping.
6428
6636
  * @param {Record<string, any>} pInputRecord - The input record to use when resolving the output recordset address mapping.
@@ -6551,18 +6759,28 @@ this.recordCache[tmpCacheKey].prune(()=>{});// First pass: gather IDs to fetch
6551
6759
  for(const tmpRecord of pRecordSet){const tmpIDValue=tmpRecord[tmpEntityIDSourceField];if(tmpIDValue){const tmpCachedRecord=this.recordCache[tmpCacheKey].read(tmpIDValue);if(!tmpCachedRecord){tmpEntityIDsToFetch.add(tmpIDValue);}}}// Now if there are records to fetch, do the request.
6552
6760
  if(tmpEntityIDsToFetch.size>0){tmpAnticipate.anticipate(function(fRequestComplete){const tmpIDRecordsArray=Array.from(tmpEntityIDsToFetch);this.getEntitySetByIDListChunked(tmpEntityName,tmpIDRecordsArray,{NoCount:true,Scope:pScope},fRequestComplete);}.bind(this));}}tmpAnticipate.wait(pError=>{if(pError){this.log.error(`cacheConnectedEntityRecords error gathering connected entity records: ${pError}`,{Stack:pError.stack});return fCallback(pError);}return fCallback();});}/**
6553
6761
  * Fetch a set of entity records by primary-key ID list, chunking the meadow IN
6554
- * filter so the generated GET URL never exceeds HTTP/2 header-size limits on
6555
- * large sets (oversized URLs trip a connection-level reset that takes sibling
6556
- * multiplexed requests down with it). Records are cached as a side effect of
6557
- * getEntitySet; the callback returns no data.
6762
+ * filter into requests.
6763
+ *
6764
+ * Chunk size is capability-aware: the legacy GET read embeds the IN-list in
6765
+ * the URL, so it is chunked small (ConnectedEntityIDChunkSize, default 200) to
6766
+ * keep the URL under HTTP/2 header-size limits — oversized URLs trip a
6767
+ * connection-level reset that takes sibling multiplexed requests down with it.
6768
+ * When the endpoint serves POST /:Entity/Query the IN-list rides in the body
6769
+ * (no URI limit), so a much larger chunk is used (ConnectedEntityIDQueryChunkSize,
6770
+ * default 5000) — bounding response size rather than URL length, and collapsing
6771
+ * what used to be many small requests into one (or a few). Records are cached
6772
+ * as a side effect of getEntitySet; the callback returns no data.
6558
6773
  *
6559
6774
  * @param {string} pEntityName - The entity name (e.g. 'Project').
6560
6775
  * @param {Array<number|string>} pIDRecordsArray - The primary-key IDs to fetch.
6561
- * @param {Object} pOptions - Options passed through to getEntitySet (Scope, NoCount, etc).
6776
+ * @param {Object} pOptions - Options passed through to getEntitySet (Scope, NoCount, URLPrefix, etc).
6562
6777
  * @param {(error?: Error) => void} fCallback - Completion callback.
6563
6778
  *
6564
6779
  * @return {void}
6565
- */getEntitySetByIDListChunked(pEntityName,pIDRecordsArray,pOptions,fCallback){if(!Array.isArray(pIDRecordsArray)||pIDRecordsArray.length<1){return fCallback();}const tmpAnticipate=this.fable.newAnticipate();const tmpChunkSize=this.options&&this.options.ConnectedEntityIDChunkSize||200;for(let i=0;i<pIDRecordsArray.length;i+=tmpChunkSize){const tmpIDChunk=pIDRecordsArray.slice(i,i+tmpChunkSize);tmpAnticipate.anticipate(function(fChunkComplete){const tmpMeadowFilterExpression=`FBL~ID${pEntityName}~INN~${tmpIDChunk.join(',')}`;this.getEntitySet(pEntityName,tmpMeadowFilterExpression,pError=>{if(pError){this.log.error(`getEntitySetByIDListChunked error getting connected entity records for [${pEntityName}] with IDs [${tmpIDChunk.join(',')}]: ${pError}`,{Stack:pError.stack});return fChunkComplete(pError);}return fChunkComplete();},null,pOptions);}.bind(this));}tmpAnticipate.wait(fCallback);}/**
6780
+ */getEntitySetByIDListChunked(pEntityName,pIDRecordsArray,pOptions,fCallback){if(!Array.isArray(pIDRecordsArray)||pIDRecordsArray.length<1){return fCallback();}const tmpOptions=pOptions||{};// Resolve transport capability once (cached; getEntitySet reuses it) so the
6781
+ // chunk size matches the transport. The probe URL prefix matches the one
6782
+ // getEntitySet will read from tmpOptions.URLPrefix.
6783
+ this.resolveEntityQuerySupport(pEntityName,tmpOptions.URLPrefix,(pSupportError,pSupportsQuery)=>{const tmpChunkSize=pSupportsQuery?this.options&&this.options.ConnectedEntityIDQueryChunkSize||5000:this.options&&this.options.ConnectedEntityIDChunkSize||200;const tmpAnticipate=this.fable.newAnticipate();for(let i=0;i<pIDRecordsArray.length;i+=tmpChunkSize){const tmpIDChunk=pIDRecordsArray.slice(i,i+tmpChunkSize);tmpAnticipate.anticipate(function(fChunkComplete){const tmpMeadowFilterExpression=`FBL~ID${pEntityName}~INN~${tmpIDChunk.join(',')}`;this.getEntitySet(pEntityName,tmpMeadowFilterExpression,pError=>{if(pError){this.log.error(`getEntitySetByIDListChunked error getting connected entity records for [${pEntityName}] with IDs [${tmpIDChunk.join(',')}]: ${pError}`,{Stack:pError.stack});return fChunkComplete(pError);}return fChunkComplete();},null,tmpOptions);}.bind(this));}tmpAnticipate.wait(fCallback);});}/**
6566
6784
  * For a given list of objects, cache connected entity records.
6567
6785
  *
6568
6786
  * @param {Array} pRecordSet - An array of objects to check cache on joined records for, and, get/cache the records as needed.
@@ -6597,22 +6815,22 @@ const tmpSpeculativeRecordIDColumn=`ID${pEntity}`;if(tmpEntitySet[0]&&typeof tmp
6597
6815
  * @param {string} [postfix] - Optional, adds a postfix string to the url.
6598
6816
  * @param {string} [pURLPrefix] - Optional per-request URL prefix; overrides the provider default (e.g. a private-data-lake route).
6599
6817
  * @param {Record<string, any>} [pOptions] - Optional { Scope, Projection }: cache scope and Lite/LiteExtended projection.
6600
- */getEntitySetPage(pEntity,pMeadowFilterExpression,pRecordStartCursor,pRecordCount,fCallback){let postfix=arguments.length>5&&arguments[5]!==undefined?arguments[5]:'';let pURLPrefix=arguments.length>6&&arguments[6]!==undefined?arguments[6]:'';let pOptions=arguments.length>7&&arguments[7]!==undefined?arguments[7]:{};const tmpFilterStanza=pMeadowFilterExpression?`/FilteredTo/${pMeadowFilterExpression}`:'';// LiteExtended projection: fetch only ID/GUID/owner/update + the requested
6818
+ */getEntitySetPage(pEntity,pMeadowFilterExpression,pRecordStartCursor,pRecordCount,fCallback){let postfix=arguments.length>5&&arguments[5]!==undefined?arguments[5]:'';let pURLPrefix=arguments.length>6&&arguments[6]!==undefined?arguments[6]:'';let pOptions=arguments.length>7&&arguments[7]!==undefined?arguments[7]:{};const tmpScope=pOptions&&pOptions.Scope?pOptions.Scope:'';// Per-request URL prefix override (positional, e.g. a private-data-lake route);
6819
+ // also accepted via pOptions.URLPrefix. Falls back to the provider default.
6820
+ const tmpURLPrefix=pURLPrefix||pOptions&&pOptions.URLPrefix||this.options.urlPrefix;// LiteExtended projection: fetch only ID/GUID/owner/update + the requested
6601
6821
  // ExtraColumns (drops blob columns). Used by scoped list fetches to avoid the
6602
6822
  // heavy full-record payload.
6603
- const tmpProjectionStanza=pOptions&&pOptions.Projection&&pOptions.Projection.Mode==='LiteExtended'&&Array.isArray(pOptions.Projection.ExtraColumns)&&pOptions.Projection.ExtraColumns.length>0?`/LiteExtended/${pOptions.Projection.ExtraColumns.join(',')}`:'';const tmpScope=pOptions&&pOptions.Scope?pOptions.Scope:'';// Per-request URL prefix override (positional, e.g. a private-data-lake route);
6604
- // also accepted via pOptions.URLPrefix. Falls back to the provider default.
6605
- const tmpURLPrefix=pURLPrefix||pOptions&&pOptions.URLPrefix||this.options.urlPrefix;const tmpURL=`${tmpURLPrefix}${pEntity}s${tmpProjectionStanza}${tmpFilterStanza}/${pRecordStartCursor}/${pRecordCount}`;return this.restClient.getJSON(tmpURL+(postfix||''),function(pDownloadError,pDownloadResponse,pDownloadBody){if(pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400){this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] [${pRecordStartCursor}/${pRecordCount}] from url [${tmpURL}]: ${pDownloadResponse.statusCode} ${pDownloadResponse.statusMessage}`);return fCallback(new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] [${pRecordStartCursor}/${pRecordCount}] from url [${tmpURL}]: ${pDownloadResponse.statusCode} ${JSON.stringify(pDownloadBody||{})}`));}// Do not cache projected (Lite/partial) records in the entity cache — a
6823
+ const tmpProjection=pOptions&&pOptions.Projection?pOptions.Projection:null;this.resolveEntityQuerySupport(pEntity,tmpURLPrefix,(pSupportError,pSupportsQuery)=>{this._readEntityPage(pEntity,pMeadowFilterExpression,pRecordStartCursor,pRecordCount,{SupportsQuery:pSupportsQuery,URLPrefix:tmpURLPrefix,Postfix:postfix||'',Projection:tmpProjection},(pDownloadError,pDownloadResponse,pDownloadBody)=>{if(pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400){this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] [${pRecordStartCursor}/${pRecordCount}]: ${pDownloadResponse.statusCode} ${pDownloadResponse.statusMessage}`);return fCallback(new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] [${pRecordStartCursor}/${pRecordCount}]: ${pDownloadResponse.statusCode} ${JSON.stringify(pDownloadBody||{})}`));}// Do not cache projected (Lite/partial) records in the entity cache — a
6606
6824
  // partial record would shadow the full record for other consumers
6607
6825
  // (row-click View, {~E:~}). Projected fetches are rendered straight from
6608
6826
  // the list state and never need to be in the entity cache.
6609
- if(!(pOptions&&pOptions.Projection)){this.cacheIndividualEntityRecords(pEntity,pDownloadBody,tmpScope);}return fCallback(pDownloadError,pDownloadBody);}.bind(this));}/**
6827
+ if(!tmpProjection){this.cacheIndividualEntityRecords(pEntity,pDownloadBody,tmpScope);}return fCallback(pDownloadError,pDownloadBody);});});}/**
6610
6828
  * @param {string} pEntity - The name of the entity to get the count of.
6611
6829
  * @param {string} pMeadowFilterExpression - The meadow filter expression to filter the entity set by.
6612
6830
  * @param {(pError?: Error, pRecordCount?: number) => void} fCallback - The callback function to call when the operation is complete.
6613
6831
  * @param {string} [postfix] - Optional, adds a postfix string to the count url
6614
6832
  * @param {string} [pURLPrefix] - Optional per-request URL prefix; overrides the provider default.
6615
- */getEntitySetRecordCount(pEntity,pMeadowFilterExpression,fCallback){let postfix=arguments.length>3&&arguments[3]!==undefined?arguments[3]:'';let pURLPrefix=arguments.length>4&&arguments[4]!==undefined?arguments[4]:'';const tmpFilterStanza=pMeadowFilterExpression?`/FilteredTo/${pMeadowFilterExpression}`:'';const tmpURL=`${pURLPrefix||this.options.urlPrefix}${pEntity}s/Count${tmpFilterStanza}`;return this.restClient.getJSON(tmpURL+(postfix||''),function(pError,pResponse,pBody){if(pResponse&&pResponse.statusCode&&pResponse.statusCode>=400){this.log.error(`Error getting entity count of [${pEntity}] filtered to [${pMeadowFilterExpression}] from url [${tmpURL}]: ${pResponse.statusCode} ${pResponse.statusMessage}`);return fCallback(new Error(`Error getting entity count of [${pEntity}] filtered to [${pMeadowFilterExpression}] from url [${tmpURL}]: ${pResponse.statusCode} ${JSON.stringify(pBody||{})}`));}if(pError){this.log.error(`Error getting entity count of [${pEntity}] filtered to [${pMeadowFilterExpression}] from url [${tmpURL}]: ${pError}`);return fCallback(pError);}let tmpRecordCount=0;if(pBody.Count){tmpRecordCount=pBody.Count;}return fCallback(pError,tmpRecordCount);}.bind(this));}/**
6833
+ */getEntitySetRecordCount(pEntity,pMeadowFilterExpression,fCallback){let postfix=arguments.length>3&&arguments[3]!==undefined?arguments[3]:'';let pURLPrefix=arguments.length>4&&arguments[4]!==undefined?arguments[4]:'';this.resolveEntityQuerySupport(pEntity,pURLPrefix,(pSupportError,pSupportsQuery)=>{this._readEntityCount(pEntity,pMeadowFilterExpression,{SupportsQuery:pSupportsQuery,URLPrefix:pURLPrefix||'',Postfix:postfix||''},(pError,pResponse,pBody)=>{if(pResponse&&pResponse.statusCode&&pResponse.statusCode>=400){this.log.error(`Error getting entity count of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pResponse.statusCode} ${pResponse.statusMessage}`);return fCallback(new Error(`Error getting entity count of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pResponse.statusCode} ${JSON.stringify(pBody||{})}`));}if(pError){this.log.error(`Error getting entity count of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pError}`);return fCallback(pError);}let tmpRecordCount=0;if(pBody&&pBody.Count){tmpRecordCount=pBody.Count;}return fCallback(pError,tmpRecordCount);});});}/**
6616
6834
  * @param {string} pEntity - The name of the entity to get.
6617
6835
  * @param {string} pMeadowFilterExpression - The meadow filter expression to filter the entity set by.
6618
6836
  * @param {(pError?: Error, pEntitySet?: Array<Record<string, any>>) => void} fCallback - The callback function to call when the operation is complete.
@@ -6628,17 +6846,18 @@ else if(tmpColumnName in this.entityColumnTranslations){tmpIDColumnsToCache.push
6628
6846
  * @param {Record<string, any>} [pOptions] - Optional, per-call options (e.g. { DownloadPageConcurrency: 1 }).
6629
6847
  *
6630
6848
  * @return {void}
6631
- */getEntitySet(pEntity,pMeadowFilterExpression,fCallback){let postfix=arguments.length>3&&arguments[3]!==undefined?arguments[3]:'';let pOptions=arguments.length>4&&arguments[4]!==undefined?arguments[4]:{};const tmpURLPrefix=pOptions.URLPrefix||this.options.urlPrefix;// TODO: Should we test for too many record IDs here by string length in pMeadowFilterExpression?
6632
- // FBL~ID${pDestinationEntity}~INN~${tmpIDRecordsCommaSeparated}
6633
- // If the list is mega-long we can parse it and break it into chunks.
6634
- const tmpScope=pOptions&&pOptions.Scope?pOptions.Scope:'';this.initializeCache(pEntity,tmpScope);const tmpCacheKey=this._cacheKey(pEntity,tmpScope);// Discard anything from the cache that has expired or is over size.
6849
+ */getEntitySet(pEntity,pMeadowFilterExpression,fCallback){let postfix=arguments.length>3&&arguments[3]!==undefined?arguments[3]:'';let pOptions=arguments.length>4&&arguments[4]!==undefined?arguments[4]:{};const tmpURLPrefix=pOptions.URLPrefix||this.options.urlPrefix;const tmpScope=pOptions&&pOptions.Scope?pOptions.Scope:'';this.initializeCache(pEntity,tmpScope);const tmpCacheKey=this._cacheKey(pEntity,tmpScope);// Resolve transport capability once for the whole read (count + every
6850
+ // page share the same SupportsQuery flag). The legacy GET reads embed the
6851
+ // filter (and any large IN-list) in the URI, which is exactly the long-URI
6852
+ // failure POST /:Entity/Query avoids when the server supports it.
6853
+ this.resolveEntityQuerySupport(pEntity,tmpURLPrefix,(pSupportError,pSupportsQuery)=>{const tmpReadOptions={SupportsQuery:pSupportsQuery,URLPrefix:tmpURLPrefix,Postfix:postfix||''};// Discard anything from the cache that has expired or is over size.
6635
6854
  this.recordSetCache[tmpCacheKey].prune(function(){let tmpPossibleRecords=this.recordSetCache[tmpCacheKey].read(pMeadowFilterExpression);if(tmpPossibleRecords){return fCallback(null,tmpPossibleRecords);}if(pOptions.NoCount){// Lazily load until we hit a not full page rather than using couns.
6636
6855
  // Does not respect parallelization.
6637
- const pageSize=250;let page=0;let returnSet=[];const tmpFilterStanza=pMeadowFilterExpression?`/FilteredTo/${pMeadowFilterExpression}`:'';const recursiveCallback=(pDownloadError,pDownloadResponse,pDownloadBody)=>{if(pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400||!Array.isArray(pDownloadBody)){this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pDownloadResponse.statusCode} ${pDownloadResponse.statusMessage}`);return fCallback(new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pDownloadResponse.statusCode} ${JSON.stringify(pDownloadBody||{})}`),[]);}returnSet=returnSet.concat(pDownloadBody);if((pDownloadBody===null||pDownloadBody===void 0?void 0:pDownloadBody.length)<pageSize){this.recordSetCache[tmpCacheKey].put(returnSet,pMeadowFilterExpression);this.cacheIndividualEntityRecords(pEntity,returnSet,tmpScope);fCallback(null,returnSet);}else{page+=1;this.restClient.getJSON(`${tmpURLPrefix}${pEntity}s${tmpFilterStanza}/${page*pageSize}/${pageSize}`+(postfix||''),recursiveCallback);}};return this.restClient.getJSON(`${tmpURLPrefix}${pEntity}s${tmpFilterStanza}/${page*pageSize}/${pageSize}`+(postfix||''),recursiveCallback);}return this.getEntitySetRecordCount(pEntity,pMeadowFilterExpression,(pRecordCountError,pRecordCount)=>{if(pRecordCountError){return fCallback(pRecordCountError);}let tmpRecordCount=pRecordCount;if(isNaN(pRecordCount)){this.log.error(`Entity count did not return a number for [${pEntity}] filtered to [${pMeadowFilterExpression}]... something is fatally wrong from the server accessed in getEntitySet call.`);return fCallback(new Error('Entity count did not return a number in getEntitySet.'));}let tmpDownloadBatchSize=this.options.downloadBatchSize;const tmpFilterStanza=pMeadowFilterExpression?`/FilteredTo/${pMeadowFilterExpression}`:'';const tmpPageCount=Math.ceil(tmpRecordCount/tmpDownloadBatchSize);// Build an indexed array of page descriptors to preserve ordering
6638
- const tmpPages=[];for(let i=0;i<tmpPageCount;i++){tmpPages.push({Index:i,URL:`${tmpURLPrefix}${pEntity}s${tmpFilterStanza}/${i*tmpDownloadBatchSize}/${tmpDownloadBatchSize}`,Records:null});}// Fetch pages concurrently and reassemble in order.
6856
+ const pageSize=250;let page=0;let returnSet=[];const recursiveCallback=(pDownloadError,pDownloadResponse,pDownloadBody)=>{if(pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400||!Array.isArray(pDownloadBody)){this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pDownloadResponse.statusCode} ${pDownloadResponse.statusMessage}`);return fCallback(new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${pDownloadResponse.statusCode} ${JSON.stringify(pDownloadBody||{})}`),[]);}returnSet=returnSet.concat(pDownloadBody);if((pDownloadBody===null||pDownloadBody===void 0?void 0:pDownloadBody.length)<pageSize){this.recordSetCache[tmpCacheKey].put(returnSet,pMeadowFilterExpression);this.cacheIndividualEntityRecords(pEntity,returnSet,tmpScope);fCallback(null,returnSet);}else{page+=1;this._readEntityPage(pEntity,pMeadowFilterExpression,page*pageSize,pageSize,tmpReadOptions,recursiveCallback);}};return this._readEntityPage(pEntity,pMeadowFilterExpression,page*pageSize,pageSize,tmpReadOptions,recursiveCallback);}return this.getEntitySetRecordCount(pEntity,pMeadowFilterExpression,(pRecordCountError,pRecordCount)=>{if(pRecordCountError){return fCallback(pRecordCountError);}let tmpRecordCount=pRecordCount;if(isNaN(pRecordCount)){this.log.error(`Entity count did not return a number for [${pEntity}] filtered to [${pMeadowFilterExpression}]... something is fatally wrong from the server accessed in getEntitySet call.`);return fCallback(new Error('Entity count did not return a number in getEntitySet.'));}let tmpDownloadBatchSize=this.options.downloadBatchSize;const tmpPageCount=Math.ceil(tmpRecordCount/tmpDownloadBatchSize);// Build an indexed array of page descriptors to preserve ordering
6857
+ const tmpPages=[];for(let i=0;i<tmpPageCount;i++){tmpPages.push({Index:i,Begin:i*tmpDownloadBatchSize,Records:null});}// Fetch pages concurrently and reassemble in order.
6639
6858
  // Per-call DownloadPageConcurrency overrides the provider-level default.
6640
- const tmpPageConcurrency=typeof pOptions.DownloadPageConcurrency==='number'?pOptions.DownloadPageConcurrency:typeof this.options.downloadPageConcurrency==='number'?this.options.downloadPageConcurrency:4;this.fable.Utility.eachLimit(tmpPages,tmpPageConcurrency,(pPage,fDownloadCallback)=>{this.restClient.getJSON(pPage.URL+(postfix||''),(pDownloadError,pDownloadResponse,pDownloadBody)=>{if(pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400){this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] from url [${pPage.URL}]: ${pDownloadResponse.statusCode} ${pDownloadResponse.statusMessage}`);return fDownloadCallback(new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] from url [${pPage.URL}]: ${pDownloadResponse.statusCode} ${JSON.stringify(pDownloadBody||{})}`));}if(Array.isArray(pDownloadBody)){pPage.Records=pDownloadBody;}return fDownloadCallback(pDownloadError);});},pFullDownloadError=>{// Reassemble pages in index order to maintain consistent ordering
6641
- let tmpEntitySet=[];for(let i=0;i<tmpPages.length;i++){if(Array.isArray(tmpPages[i].Records)){tmpEntitySet=tmpEntitySet.concat(tmpPages[i].Records);}}if(tmpEntitySet){this.recordSetCache[tmpCacheKey].put(tmpEntitySet,pMeadowFilterExpression);}this.cacheIndividualEntityRecords(pEntity,tmpEntitySet,tmpScope);return fCallback(pFullDownloadError,tmpEntitySet);});},postfix,pOptions.URLPrefix);}.bind(this));}////////////////////////////////////////////////////////////////////////////////
6859
+ const tmpPageConcurrency=typeof pOptions.DownloadPageConcurrency==='number'?pOptions.DownloadPageConcurrency:typeof this.options.downloadPageConcurrency==='number'?this.options.downloadPageConcurrency:4;this.fable.Utility.eachLimit(tmpPages,tmpPageConcurrency,(pPage,fDownloadCallback)=>{this._readEntityPage(pEntity,pMeadowFilterExpression,pPage.Begin,tmpDownloadBatchSize,tmpReadOptions,(pDownloadError,pDownloadResponse,pDownloadBody)=>{if(pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400){this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] [${pPage.Begin}/${tmpDownloadBatchSize}]: ${pDownloadResponse.statusCode} ${pDownloadResponse.statusMessage}`);return fDownloadCallback(new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}] [${pPage.Begin}/${tmpDownloadBatchSize}]: ${pDownloadResponse.statusCode} ${JSON.stringify(pDownloadBody||{})}`));}if(Array.isArray(pDownloadBody)){pPage.Records=pDownloadBody;}return fDownloadCallback(pDownloadError);});},pFullDownloadError=>{// Reassemble pages in index order to maintain consistent ordering
6860
+ let tmpEntitySet=[];for(let i=0;i<tmpPages.length;i++){if(Array.isArray(tmpPages[i].Records)){tmpEntitySet=tmpEntitySet.concat(tmpPages[i].Records);}}if(tmpEntitySet){this.recordSetCache[tmpCacheKey].put(tmpEntitySet,pMeadowFilterExpression);}this.cacheIndividualEntityRecords(pEntity,tmpEntitySet,tmpScope);return fCallback(pFullDownloadError,tmpEntitySet);});},postfix,pOptions.URLPrefix);}.bind(this));});}////////////////////////////////////////////////////////////////////////////////
6642
6861
  // Entity Creation and Update Methods
6643
6862
  /**
6644
6863
  * @param {string} pEntityType - The type of the entity to format the URL for.
@@ -7224,7 +7443,9 @@ return this.contentMap[pAddress];}this.pict.log.warn(`Mock read from Address ${p
7224
7443
  * PreparedFilters: Array<PreparedFilter>,
7225
7444
  * BundleConfig?: Array<Record<string, any>>,
7226
7445
  * UserFilters?: Array<string>,
7227
- * Postfix?: string
7446
+ * Postfix?: string,
7447
+ * Scope?: string,
7448
+ * Projection?: Record<string, any>,
7228
7449
  * }} FilterState
7229
7450
  *
7230
7451
  * @typedef { 'None' |