pict 1.0.391 → 1.0.393

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/README.md CHANGED
@@ -186,4 +186,4 @@ MIT
186
186
 
187
187
  ## Contributing
188
188
 
189
- Pull requests are welcome. For details on our code of conduct, contribution process, and testing requirements, see the [Retold Contributing Guide](https://github.com/stevenvelozo/retold/blob/main/docs/contributing.md).
189
+ Pull requests are welcome. For details on our code of conduct, contribution process, and testing requirements, see the [Retold Contributing Guide](https://github.com/fable-retold/retold/blob/main/docs/contributing.md).
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.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){/**
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.79","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
@@ -2801,7 +2801,18 @@ if(tmpProgressTracker.CurrentCount<1&&tmpProgressTracker.EndTimeStamp<1){return`
2801
2801
  else if(tmpProgressTracker.EndTimeStamp<1){return`ProgressTracker ${tmpProgressTracker.Hash} is ${tmpProgressTracker.PercentComplete.toFixed(3)}% completed - ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations over ${this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)} (median ${this.progressTimes.formatTimeDuration(tmpProgressTracker.AverageOperationTime)} per). Estimated completion: ${this.progressTimes.formatTimeDuration(tmpProgressTracker.EstimatedCompletionTime)}`;}// 4. Done
2802
2802
  else{return`ProgressTracker ${tmpProgressTracker.Hash} is done. ${tmpProgressTracker.CurrentCount} / ${tmpProgressTracker.TotalCount} operations were completed in ${this.progressTimes.formatTimeDuration(tmpProgressTracker.ElapsedTime)} (median ${this.progressTimes.formatTimeDuration(tmpProgressTracker.AverageOperationTime)} per).`;}}}logProgressTrackerStatus(pProgressTrackerHash){this.fable.log.info(this.getProgressTrackerStatusString(pProgressTrackerHash));}}module.exports=FableServiceProgressTrackerSet;},{"./Fable-Service-ProgressTracker/ProgressTracker.js":96,"fable-serviceproviderbase":59}],98:[function(require,module,exports){(function(Buffer){(function(){const libFableServiceBase=require('fable-serviceproviderbase');const libSimpleGet=require('simple-get');const libCookie=require('cookie');const libHttp=require('http');const libHttps=require('https');class FableServiceRestClient extends libFableServiceBase{constructor(pFable,pOptions,pServiceHash){super(pFable,pOptions,pServiceHash);this.TraceLog=false;if(this.options.TraceLog||this.fable.TraceLog){this.TraceLog=true;}this.dataFormat=this.fable.services.DataFormat;this.serviceType='RestClient';this.cookie=false;// This is a function that can be overridden, to allow the management
2803
2803
  // of the request options before they are passed to the request library.
2804
- this.prepareRequestOptions=pOptions=>{return pOptions;};// Default per-request timeout (ms). Applied in preRequest when a caller
2804
+ this.prepareRequestOptions=pOptions=>{return pOptions;};// Optional, UI-agnostic authentication-recovery hook. Default null, so
2805
+ // existing server-to-server callers are completely unaffected. When set
2806
+ // to an (async) function and a completed response has statusCode 401 on a
2807
+ // request that is not itself a replay, the client awaits this hook; a
2808
+ // truthy resolution replays the original request exactly once, anything
2809
+ // else returns the original 401 to the caller unchanged. The hook never
2810
+ // imports app UI -- the app assigns it (e.g. a re-auth modal controller),
2811
+ // which is what makes recovery reusable across every Fable consumer.
2812
+ this.authenticationRecovery=null;// Single-flight state for the recovery hook: a burst of concurrent 401s
2813
+ // collapses to exactly one authenticationRecovery() call. Cleared on
2814
+ // settle so a later, unrelated expiry re-arms recovery.
2815
+ this._authenticationRecoveryPromise=null;// Default per-request timeout (ms). Applied in preRequest when a caller
2805
2816
  // does not supply their own. Node 20+ installs a ~5s socket timeout on
2806
2817
  // http.globalAgent that aborts legitimately long-running requests; any
2807
2818
  // explicit `timeout` on the request options takes that default out of
@@ -2907,15 +2918,54 @@ pResponse.resume();let tmpRedirectsRemaining=typeof pOptions.maxRedirects==='num
2907
2918
  // (possibly different) protocol of the redirect target. We do
2908
2919
  // NOT re-run preRequest in full — RestClientURLPrefix and
2909
2920
  // prepareCookies are first-call-only.
2910
- const tmpPreparedNext=this.prepareRequestOptions(tmpNextOptions);if(this.TraceLog){this.fable.log.debug(`--> redirect ${pResponse.statusCode} to ${tmpPreparedNext.url}`);}return this._executeWithRedirects(tmpPreparedNext,fCallback);});}executeChunkedRequest(pOptions,fCallback){let tmpOptions=this.preRequest(pOptions);tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} request to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpData='';pResponse.on('data',pChunk=>{// For JSON, the chunk is the serialized object.
2911
- if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> ${tmpOptions.method} data chunk size ${pChunk.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime)}ms`);}tmpData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> ${tmpOptions.method} completed data size ${tmpData.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}return fCallback(pError,pResponse,tmpData);});});}executeChunkedRequestBinary(pOptions,fCallback){let tmpOptions=this.preRequest(pOptions);tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} request to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}tmpOptions.json=false;tmpOptions.encoding=null;return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpDataBuffer=false;pResponse.on('data',pChunk=>{// For JSON, the chunk is the serialized object.
2921
+ const tmpPreparedNext=this.prepareRequestOptions(tmpNextOptions);if(this.TraceLog){this.fable.log.debug(`--> redirect ${pResponse.statusCode} to ${tmpPreparedNext.url}`);}return this._executeWithRedirects(tmpPreparedNext,fCallback);});}executeChunkedRequest(pOptions,fCallback){let tmpReplayOptions=this._captureReplayOptions(pOptions);let tmpOptions=this.preRequest(pOptions);tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} request to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpData='';pResponse.on('data',pChunk=>{// For JSON, the chunk is the serialized object.
2922
+ if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> ${tmpOptions.method} data chunk size ${pChunk.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime)}ms`);}tmpData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> ${tmpOptions.method} completed data size ${tmpData.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}return this._completeWithRecovery(tmpReplayOptions,pError,pResponse,tmpData,()=>this.executeChunkedRequest(tmpReplayOptions,fCallback),fCallback);});});}executeChunkedRequestBinary(pOptions,fCallback){let tmpReplayOptions=this._captureReplayOptions(pOptions);let tmpOptions=this.preRequest(pOptions);tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} request to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}tmpOptions.json=false;tmpOptions.encoding=null;return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpDataBuffer=false;pResponse.on('data',pChunk=>{// For JSON, the chunk is the serialized object.
2912
2923
  if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> ${tmpOptions.method} data chunk size ${pChunk.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime)}ms`);}// TODO: Potentially create a third option that streams this to a file? So it doesn't have to hold it all in memory.
2913
- if(!tmpDataBuffer){tmpDataBuffer=Buffer.from(pChunk);}else{tmpDataBuffer=Buffer.concat([tmpDataBuffer,pChunk]);}});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> ${tmpOptions.method} completed data size ${tmpDataBuffer.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}return fCallback(pError,pResponse,tmpDataBuffer);});});}executeJSONRequest(pOptions,fCallback){pOptions.json=true;let tmpOptions=this.preRequest(pOptions);if(!('headers'in tmpOptions)){tmpOptions.headers={};}/* Automated headers break some APIs
2924
+ if(!tmpDataBuffer){tmpDataBuffer=Buffer.from(pChunk);}else{tmpDataBuffer=Buffer.concat([tmpDataBuffer,pChunk]);}});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> ${tmpOptions.method} completed data size ${tmpDataBuffer.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}return this._completeWithRecovery(tmpReplayOptions,pError,pResponse,tmpDataBuffer,()=>this.executeChunkedRequestBinary(tmpReplayOptions,fCallback),fCallback);});});}/**
2925
+ * Shallow-snapshot the caller's options BEFORE preRequest mutates them, so a
2926
+ * recovery replay can re-run the same request cleanly. preRequest prepends
2927
+ * RestClientURLPrefix to `url` in place; replaying the already-mutated object
2928
+ * would double-prefix. Replaying from this snapshot re-runs preRequest once.
2929
+ *
2930
+ * @param {Object} pOptions - The caller's request options.
2931
+ * @return {Object} A shallow copy safe to re-issue.
2932
+ * @private
2933
+ */_captureReplayOptions(pOptions){return Object.assign({},pOptions);}/**
2934
+ * Invoke the authenticationRecovery hook under a single-flight guard. A burst
2935
+ * of concurrent 401s shares one hook invocation; all await the same promise,
2936
+ * then each replays independently. The shared promise is cleared on settle so
2937
+ * a later, unrelated expiry re-arms recovery. Resolves to a strict boolean
2938
+ * (true only when the hook resolved exactly true); rejects if the hook throws.
2939
+ *
2940
+ * @param {Object} pRequestContext - Context passed to the hook ({ options, response }).
2941
+ * @return {Promise<boolean>}
2942
+ * @private
2943
+ */_runAuthenticationRecovery(pRequestContext){if(this._authenticationRecoveryPromise){return this._authenticationRecoveryPromise;}let tmpInvocation;try{tmpInvocation=Promise.resolve(this.authenticationRecovery(pRequestContext));}catch(pError){tmpInvocation=Promise.reject(pError);}this._authenticationRecoveryPromise=tmpInvocation.then(pResult=>{this._authenticationRecoveryPromise=null;return pResult===true;},pError=>{this._authenticationRecoveryPromise=null;throw pError;});return this._authenticationRecoveryPromise;}/**
2944
+ * Shared completion seam for every request path. On a completed 401, with a
2945
+ * recovery hook installed, on a request that is not itself already a replay,
2946
+ * it awaits recovery: on success it replays the request exactly once (via
2947
+ * fReplay, whose pReplayOptions carry the __authRetry marker so the replay can
2948
+ * never re-enter recovery -- a hard one-retry cap); otherwise it delivers the
2949
+ * original response to fCallback unchanged. Every other outcome (2xx, 5xx,
2950
+ * timeout, no hook) passes straight through, so opt-out callers are untouched.
2951
+ *
2952
+ * @param {Object} pReplayOptions - The replay snapshot (also carries the retry marker).
2953
+ * @param {Error|null} pError - The error from the request, if any.
2954
+ * @param {Object} pResponse - The response object (statusCode read here).
2955
+ * @param {*} pBody - The processed body for this path (parsed JSON / string / Buffer).
2956
+ * @param {Function} fReplay - Re-issues the request from pReplayOptions.
2957
+ * @param {Function} fCallback - The caller's callback (pError, pResponse, pBody).
2958
+ * @private
2959
+ */_completeWithRecovery(pReplayOptions,pError,pResponse,pBody,fReplay,fCallback){if(pResponse&&pResponse.statusCode===401&&typeof this.authenticationRecovery==='function'&&!pReplayOptions.__authRetry){// Stamp the one-retry marker before recovery so the replay can never
2960
+ // itself trigger another recovery pass.
2961
+ pReplayOptions.__authRetry=true;this._runAuthenticationRecovery({options:pReplayOptions,response:pResponse}).then(pRecovered=>{if(pRecovered===true){return fReplay();}return fCallback(pError,pResponse,pBody);},()=>{// A throwing/rejecting recovery is treated as "not recovered":
2962
+ // hand the original 401 back untouched.
2963
+ return fCallback(pError,pResponse,pBody);});return;}return fCallback(pError,pResponse,pBody);}executeJSONRequest(pOptions,fCallback){pOptions.json=true;let tmpReplayOptions=this._captureReplayOptions(pOptions);let tmpOptions=this.preRequest(pOptions);if(!('headers'in tmpOptions)){tmpOptions.headers={};}/* Automated headers break some APIs
2914
2964
  if (!('Content-Type' in tmpOptions.headers))
2915
2965
  {
2916
2966
  tmpOptions.headers['Content-Type'] = 'application/json';
2917
2967
  }
2918
- */tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} JSON request to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> JSON ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpJSONData='';pResponse.on('data',pChunk=>{if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> JSON ${tmpOptions.method} data chunk size ${pChunk.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime)}ms`);}tmpJSONData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> JSON ${tmpOptions.method} completed - received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}let tmpParsedJSON;try{tmpParsedJSON=JSON.parse(tmpJSONData);}catch(pParseError){let tmpStatusCode=pResponse?pResponse.statusCode:'unknown';return fCallback(new Error(`JSON parse failed (HTTP ${tmpStatusCode}): ${tmpJSONData.substring(0,200)}`),pResponse,null);}return fCallback(pError,pResponse,tmpParsedJSON);});});}getJSON(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeJSONRequest(tmpRequestOptions,fCallback);}putJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`PUT JSON Error Invalid options object`));}pOptions.method='PUT';return this.executeJSONRequest(pOptions,fCallback);}postJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`POST JSON Error Invalid options object`));}pOptions.method='POST';return this.executeJSONRequest(pOptions,fCallback);}patchJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`PATCH JSON Error Invalid options object`));}pOptions.method='PATCH';return this.executeJSONRequest(pOptions,fCallback);}headJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`HEAD JSON Error Invalid options object`));}pOptions.method='HEAD';return this.executeJSONRequest(pOptions,fCallback);}delJSON(pOptions,fCallback){pOptions.method='DELETE';return this.executeJSONRequest(pOptions,fCallback);}/**
2968
+ */tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} JSON request to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> JSON ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpJSONData='';pResponse.on('data',pChunk=>{if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> JSON ${tmpOptions.method} data chunk size ${pChunk.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime)}ms`);}tmpJSONData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> JSON ${tmpOptions.method} completed - received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}let tmpParsedJSON;try{tmpParsedJSON=JSON.parse(tmpJSONData);}catch(pParseError){let tmpStatusCode=pResponse?pResponse.statusCode:'unknown';return fCallback(new Error(`JSON parse failed (HTTP ${tmpStatusCode}): ${tmpJSONData.substring(0,200)}`),pResponse,null);}return this._completeWithRecovery(tmpReplayOptions,pError,pResponse,tmpParsedJSON,()=>this.executeJSONRequest(tmpReplayOptions,fCallback),fCallback);});});}getJSON(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeJSONRequest(tmpRequestOptions,fCallback);}putJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`PUT JSON Error Invalid options object`));}pOptions.method='PUT';return this.executeJSONRequest(pOptions,fCallback);}postJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`POST JSON Error Invalid options object`));}pOptions.method='POST';return this.executeJSONRequest(pOptions,fCallback);}patchJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`PATCH JSON Error Invalid options object`));}pOptions.method='PATCH';return this.executeJSONRequest(pOptions,fCallback);}headJSON(pOptions,fCallback){if(typeof pOptions.body!='object'){return fCallback(new Error(`HEAD JSON Error Invalid options object`));}pOptions.method='HEAD';return this.executeJSONRequest(pOptions,fCallback);}delJSON(pOptions,fCallback){pOptions.method='DELETE';return this.executeJSONRequest(pOptions,fCallback);}/**
2919
2969
  * Upload binary data via POST.
2920
2970
  *
2921
2971
  * Accepts Buffer, Blob, or File as the body. In the browser, Blob/File
@@ -2937,8 +2987,10 @@ return this._executeBinaryUploadInternal(pOptions,fCallback,fOnProgress);}/**
2937
2987
  * @param {(pError?: Error, pResponse: any, pBody?: any) => void} fCallback - Callback (pError, pResponse, pBody)
2938
2988
  * @param {(pProgress: number) => void} [fOnProgress] - Optional progress callback (0.0 to 1.0); called with 1.0 on completion
2939
2989
  * @private
2940
- */_executeBinaryUploadInternal(pOptions,fCallback,fOnProgress){let tmpOptions=this.preRequest(pOptions);tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} binary upload to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}tmpOptions.json=false;return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> Binary upload ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpData='';pResponse.on('data',pChunk=>{if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> Binary upload ${tmpOptions.method} response chunk size ${pChunk.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime)}ms`);}tmpData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> Binary upload ${tmpOptions.method} completed in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}// Signal completion via progress callback
2941
- if(typeof fOnProgress==='function'){fOnProgress(1.0);}return fCallback(pError,pResponse,tmpData);});});}getRawText(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeChunkedRequest(tmpRequestOptions,fCallback);}}module.exports=FableServiceRestClient;}).call(this);}).call(this,require("buffer").Buffer);},{"buffer":20,"cookie":31,"fable-serviceproviderbase":59,"http":167,"https":113,"simple-get":166}],99:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceTemplate extends libFableServiceBase{// Underscore and lodash have a behavior, _.template, which compiles a
2990
+ */_executeBinaryUploadInternal(pOptions,fCallback,fOnProgress){let tmpReplayOptions=this._captureReplayOptions(pOptions);let tmpOptions=this.preRequest(pOptions);tmpOptions.RequestStartTime=this.fable.log.getTimeStamp();if(this.TraceLog){this.fable.log.debug(`Beginning ${tmpOptions.method} binary upload to ${tmpOptions.url} at ${tmpOptions.RequestStartTime}`);}tmpOptions.json=false;return this._executeWithRedirects(tmpOptions,(pError,pResponse)=>{if(pError){return fCallback(pError,pResponse);}if(this.TraceLog){let tmpConnectTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> Binary upload ${tmpOptions.method} connected in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpConnectTime)}ms code ${pResponse.statusCode}`);}let tmpData='';pResponse.on('data',pChunk=>{if(this.TraceLog){let tmpChunkTime=this.fable.log.getTimeStamp();this.fable.log.debug(`--> Binary upload ${tmpOptions.method} response chunk size ${pChunk.length}b received in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpChunkTime)}ms`);}tmpData+=pChunk;});pResponse.on('end',()=>{if(this.TraceLog){let tmpCompletionTime=this.fable.log.getTimeStamp();this.fable.log.debug(`==> Binary upload ${tmpOptions.method} completed in ${this.dataFormat.formatTimeDelta(tmpOptions.RequestStartTime,tmpCompletionTime)}ms`);}// Deliver terminally: fire the completion progress only when
2991
+ // we actually hand the response back (not when we are about to
2992
+ // replay after recovery -- the replay signals its own progress).
2993
+ let fDeliver=(pDeliverError,pDeliverResponse,pDeliverBody)=>{if(typeof fOnProgress==='function'){fOnProgress(1.0);}return fCallback(pDeliverError,pDeliverResponse,pDeliverBody);};return this._completeWithRecovery(tmpReplayOptions,pError,pResponse,tmpData,()=>this._executeBinaryUploadInternal(tmpReplayOptions,fCallback,fOnProgress),fDeliver);});});}getRawText(pOptionsOrURL,fCallback){let tmpRequestOptions=typeof pOptionsOrURL=='object'?pOptionsOrURL:{};if(typeof pOptionsOrURL=='string'){tmpRequestOptions.url=pOptionsOrURL;}tmpRequestOptions.method='GET';return this.executeChunkedRequest(tmpRequestOptions,fCallback);}}module.exports=FableServiceRestClient;}).call(this);}).call(this,require("buffer").Buffer);},{"buffer":20,"cookie":31,"fable-serviceproviderbase":59,"http":167,"https":113,"simple-get":166}],99:[function(require,module,exports){const libFableServiceBase=require('fable-serviceproviderbase');class FableServiceTemplate extends libFableServiceBase{// Underscore and lodash have a behavior, _.template, which compiles a
2942
2994
  // string-based template with code snippets into simple executable pieces,
2943
2995
  // with the added twist of returning a precompiled function ready to go.
2944
2996
  //
@@ -6218,7 +6270,7 @@ try{if(!global.localStorage)return false;}catch(_){return false;}var val=global.
6218
6270
  // presumably different callback function.
6219
6271
  // This makes sure that own properties are retained, so that
6220
6272
  // decorations and such are not lost along the way.
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.391","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.
6273
+ 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.393","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://github.com/fable-retold/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.79","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.
6222
6274
  /* global Pict, _Pict: writeable *//**
6223
6275
  * Simple function to load a pict Application
6224
6276
  *
@@ -6506,15 +6558,23 @@ for(let i=0;i<3;i++){const tmpHave=isNaN(tmpParts[i])?0:tmpParts[i];const tmpNee
6506
6558
  * @return {void}
6507
6559
  */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
6560
  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);}});}/**
6561
+ * Normalize a step Projection into lite-read shaping. `Lite` and
6562
+ * `LiteExtended` both map to a lite read; `LiteExtended` with no
6563
+ * ExtraColumns degrades to a plain Lite read.
6564
+ *
6565
+ * @param {Record<string, any>} [pProjection] - Optional { Mode:'Lite'|'LiteExtended', ExtraColumns:[...] }.
6566
+ * @return {{ ExtraColumns: string|null }|null} Lite-read shaping, or null for a full read.
6567
+ */_resolveLiteProjection(pProjection){if(!pProjection||pProjection.Mode!=='Lite'&&pProjection.Mode!=='LiteExtended'){return null;}const tmpExtraColumns=Array.isArray(pProjection.ExtraColumns)&&pProjection.ExtraColumns.length>0?pProjection.ExtraColumns.join(','):null;return{ExtraColumns:tmpExtraColumns};}/**
6509
6568
  * Build the POST /:Entity/Query request body for a filtered read.
6510
6569
  *
6511
6570
  * @param {string} pMeadowFilterExpression - The meadow filter string (may be empty).
6512
6571
  * @param {number|null} [pBegin] - Pagination start cursor.
6513
6572
  * @param {number|null} [pCap] - Pagination page size.
6514
- * @param {Record<string, any>} [pProjection] - Optional { Mode:'LiteExtended', ExtraColumns:[...] }.
6573
+ * @param {Record<string, any>} [pProjection] - Optional { Mode:'Lite'|'LiteExtended', ExtraColumns:[...] }.
6515
6574
  * @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;}/**
6575
+ */_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;}const tmpLiteProjection=this._resolveLiteProjection(pProjection);if(tmpLiteProjection){// Lite/LiteExtended GET reads map to a Lite Query read; ExtraColumns
6576
+ // carries the LiteExtended column list when present.
6577
+ tmpBody.Lite=true;if(tmpLiteProjection.ExtraColumns){tmpBody.ExtraColumns=tmpLiteProjection.ExtraColumns;}}return tmpBody;}/**
6518
6578
  * Read a page of an entity set, using POST /:Entity/Query when supported and
6519
6579
  * falling back to the legacy GET read otherwise. The callback mirrors
6520
6580
  * restClient.getJSON exactly: (pError, pResponse, pBody).
@@ -6526,7 +6586,7 @@ tmpBody.Lite=true;tmpBody.ExtraColumns=pProjection.ExtraColumns.join(',');}retur
6526
6586
  * @param {Record<string, any>} pReadOptions - { SupportsQuery, URLPrefix, Postfix, Projection }.
6527
6587
  * @param {(pError: Error|null, pResponse: any, pBody: any) => void} fCallback - Completion callback.
6528
6588
  * @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);}/**
6589
+ */_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 tmpLiteProjection=this._resolveLiteProjection(tmpProjection);const tmpProjectionStanza=tmpLiteProjection?tmpLiteProjection.ExtraColumns?`/LiteExtended/${tmpLiteProjection.ExtraColumns}`:'/Lite':'';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
6590
  * Read the count of an entity set, using POST /:Entity/Query (Count mode)
6531
6591
  * when supported and the legacy GET Count otherwise. The callback mirrors
6532
6592
  * restClient.getJSON: (pError, pResponse, pBody) where pBody carries .Count.
@@ -6843,25 +6903,27 @@ else if(tmpColumnName in this.entityColumnTranslations){tmpIDColumnsToCache.push
6843
6903
  * @param {string} pMeadowFilterExpression - The meadow filter expression to filter the entity set by.
6844
6904
  * @param {(pError?: Error, pEntitySet?: Array) => void} fCallback - The callback to call when the request is complete.
6845
6905
  * @param {string} [postfix] - Optional, adds a postfix string to all calls made.
6846
- * @param {Record<string, any>} [pOptions] - Optional, per-call options (e.g. { DownloadPageConcurrency: 1 }).
6906
+ * @param {Record<string, any>} [pOptions] - Optional, per-call options (e.g. { DownloadPageConcurrency: 1, Projection: { Mode: 'LiteExtended', ExtraColumns: [...] } }).
6847
6907
  *
6848
6908
  * @return {void}
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
6909
+ */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:'';const tmpProjection=pOptions&&pOptions.Projection?pOptions.Projection:null;this.initializeCache(pEntity,tmpScope);const tmpCacheKey=this._cacheKey(pEntity,tmpScope);// Resolve transport capability once for the whole read (count + every
6850
6910
  // page share the same SupportsQuery flag). The legacy GET reads embed the
6851
6911
  // filter (and any large IN-list) in the URI, which is exactly the long-URI
6852
6912
  // 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.
6913
+ this.resolveEntityQuerySupport(pEntity,tmpURLPrefix,(pSupportError,pSupportsQuery)=>{const tmpReadOptions={SupportsQuery:pSupportsQuery,URLPrefix:tmpURLPrefix,Postfix:postfix||'',Projection:tmpProjection};// Discard anything from the cache that has expired or is over size.
6854
6914
  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.
6855
6915
  // Does not respect parallelization.
6856
6916
  const pageSize=250;let page=0;let returnSet=[];const recursiveCallback=(pDownloadError,pDownloadResponse,pDownloadBody)=>{// A transport-level failure (e.g. connection reset, expired TLS
6857
6917
  // cert) calls back with no response, so guard every pDownloadResponse
6858
6918
  // deref and surface the underlying error rather than throwing a
6859
6919
  // TypeError on the missing statusCode.
6860
- if(pDownloadError||pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400||!Array.isArray(pDownloadBody)){const tmpStatusCode=pDownloadResponse?pDownloadResponse.statusCode:'no response';const tmpStatusDetail=pDownloadError?pDownloadError.message||pDownloadError:pDownloadResponse?pDownloadResponse.statusMessage:'';this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${tmpStatusCode} ${tmpStatusDetail}`);return fCallback(pDownloadError||new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${tmpStatusCode} ${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
6920
+ if(pDownloadError||pDownloadResponse&&pDownloadResponse.statusCode&&pDownloadResponse.statusCode>=400||!Array.isArray(pDownloadBody)){const tmpStatusCode=pDownloadResponse?pDownloadResponse.statusCode:'no response';const tmpStatusDetail=pDownloadError?pDownloadError.message||pDownloadError:pDownloadResponse?pDownloadResponse.statusMessage:'';this.log.error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${tmpStatusCode} ${tmpStatusDetail}`);return fCallback(pDownloadError||new Error(`Error getting entity set of [${pEntity}] filtered to [${pMeadowFilterExpression}]: ${tmpStatusCode} ${JSON.stringify(pDownloadBody||{})}`),[]);}returnSet=returnSet.concat(pDownloadBody);if((pDownloadBody===null||pDownloadBody===void 0?void 0:pDownloadBody.length)<pageSize){// Do not cache projected (partial) records see getEntitySetPage.
6921
+ if(!tmpProjection){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
6861
6922
  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.
6862
6923
  // Per-call DownloadPageConcurrency overrides the provider-level default.
6863
6924
  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
6864
- 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));});}////////////////////////////////////////////////////////////////////////////////
6925
+ let tmpEntitySet=[];for(let i=0;i<tmpPages.length;i++){if(Array.isArray(tmpPages[i].Records)){tmpEntitySet=tmpEntitySet.concat(tmpPages[i].Records);}}// Do not cache projected (partial) records — see getEntitySetPage.
6926
+ if(!tmpProjection){if(tmpEntitySet){this.recordSetCache[tmpCacheKey].put(tmpEntitySet,pMeadowFilterExpression);}this.cacheIndividualEntityRecords(pEntity,tmpEntitySet,tmpScope);}return fCallback(pFullDownloadError,tmpEntitySet);});},postfix,pOptions.URLPrefix);}.bind(this));});}////////////////////////////////////////////////////////////////////////////////
6865
6927
  // Entity Creation and Update Methods
6866
6928
  /**
6867
6929
  * @param {string} pEntityType - The type of the entity to format the URL for.