@selvajs/compute 1.5.2 → 2.0.0-beta.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-3N5WXYQZ.js +2 -0
- package/dist/chunk-3N5WXYQZ.js.map +1 -0
- package/dist/chunk-I6BYCRNM.cjs +2 -0
- package/dist/chunk-I6BYCRNM.cjs.map +1 -0
- package/dist/chunk-KWNSHZE3.js +2 -0
- package/dist/chunk-KWNSHZE3.js.map +1 -0
- package/dist/chunk-LKSMZAOW.js +4 -0
- package/dist/chunk-LKSMZAOW.js.map +1 -0
- package/dist/chunk-MQKJ46HT.cjs +4 -0
- package/dist/chunk-MQKJ46HT.cjs.map +1 -0
- package/dist/chunk-PTMR332C.cjs +2 -0
- package/dist/chunk-PTMR332C.cjs.map +1 -0
- package/dist/core.cjs +1 -1
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +10 -15
- package/dist/core.d.ts +10 -15
- package/dist/core.js +1 -1
- package/dist/grasshopper.cjs +1 -1
- package/dist/grasshopper.cjs.map +1 -1
- package/dist/grasshopper.d.cts +77 -101
- package/dist/grasshopper.d.ts +77 -101
- package/dist/grasshopper.js +1 -1
- package/dist/handle-files-BU9vu1Fd.d.cts +260 -0
- package/dist/handle-files-BU9vu1Fd.d.ts +260 -0
- package/dist/index.cjs +1 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -4
- package/dist/index.d.ts +4 -4
- package/dist/index.js +1 -1
- package/dist/{types-Dfeei0dD.d.cts → types-CJ092lxB.d.cts} +116 -73
- package/dist/types-D1SkNje_.d.cts +87 -0
- package/dist/types-D1SkNje_.d.ts +87 -0
- package/dist/{types-Dfeei0dD.d.ts → types-XCUrJGby.d.ts} +116 -73
- package/dist/visualization-AQNCY6MG.js +2 -0
- package/dist/visualization-PRUUKBYP.cjs +2 -0
- package/dist/visualization-PRUUKBYP.cjs.map +1 -0
- package/dist/visualization.cjs +1 -1
- package/dist/visualization.cjs.map +1 -1
- package/dist/visualization.d.cts +16 -21
- package/dist/visualization.d.ts +16 -21
- package/dist/visualization.js +1 -1
- package/package.json +18 -7
- package/dist/chunk-FWK2WGU5.js +0 -2
- package/dist/chunk-FWK2WGU5.js.map +0 -1
- package/dist/chunk-OEDLGVIQ.js +0 -2
- package/dist/chunk-OEDLGVIQ.js.map +0 -1
- package/dist/chunk-OW6HV6QP.js +0 -2
- package/dist/chunk-OW6HV6QP.js.map +0 -1
- package/dist/chunk-RBNF6MNH.cjs +0 -3
- package/dist/chunk-RBNF6MNH.cjs.map +0 -1
- package/dist/chunk-SVEXPGHW.cjs +0 -2
- package/dist/chunk-SVEXPGHW.cjs.map +0 -1
- package/dist/chunk-XBIEAJBK.js +0 -3
- package/dist/chunk-XBIEAJBK.js.map +0 -1
- package/dist/chunk-ZH5AZFGB.cjs +0 -2
- package/dist/chunk-ZH5AZFGB.cjs.map +0 -1
- package/dist/chunk-ZRQRYG6F.cjs +0 -2
- package/dist/chunk-ZRQRYG6F.cjs.map +0 -1
- package/dist/errors-CEy4nM1J.d.cts +0 -149
- package/dist/errors-CEy4nM1J.d.ts +0 -149
- package/dist/types-COCuQEMk.d.cts +0 -93
- package/dist/types-COCuQEMk.d.ts +0 -93
- package/dist/visualization-ENMBHWIN.js +0 -2
- package/dist/visualization-TBPFFBFU.cjs +0 -2
- package/dist/visualization-TBPFFBFU.cjs.map +0 -1
- /package/dist/{visualization-ENMBHWIN.js.map → visualization-AQNCY6MG.js.map} +0 -0
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{b as p,c as d,d as l,e as m,h as w,i as z,j as $,m as _,n as q,p as J,r as A}from"./chunk-LKSMZAOW.js";function b(t,e){e||typeof window<"u"&&m().warn(`Warning: ${t} is running on the client side. For better performance and security, consider running this on the server side.`)}var ye="Unable to load grasshopper definition";function ge(t){return t instanceof l&&t.message.includes(ye)}async function x(t,e,r){r.debug&&b("solveGrasshopperDefinition",r.suppressBrowserWarning??r.suppressClientSideWarning);let{response:n}=await N(D(e,t),r);return n}async function H(t,e,r){return r.debug&&b("solveGrasshopperDefinitionWithCacheKey",r.suppressBrowserWarning??r.suppressClientSideWarning),N(D(e,t),r)}async function Y(t,e,r,n){n.debug&&b("solveByCacheKey",n.suppressBrowserWarning??n.suppressClientSideWarning);let s={algo:null,pointer:e,values:t};try{return{...await N(s,n),missed:!1}}catch(o){if(!ge(o))throw o;return{...await N(D(r,t),n),missed:!0}}}async function N(t,e){be(t,e);let r=await w("grasshopper",t,e);if("pointer"in r){let{pointer:n,...s}=r;return{response:s,cacheKey:typeof n=="string"?n:null}}return{response:r,cacheKey:null}}function D(t,e){let r={algo:null,pointer:null,values:e};return t instanceof Uint8Array?r.algo=J(t):/^https?:\/\//i.test(t)?r.pointer=t:q(t)?r.algo=t:r.algo=_(t),r}function be(t,e){e.cachesolve!=null&&(t.cachesolve=e.cachesolve),e.modelunits!=null&&(t.modelunits=e.modelunits),e.angletolerance!=null&&(t.angletolerance=e.angletolerance),e.absolutetolerance!=null&&(t.absolutetolerance=e.absolutetolerance),e.dataversion!=null&&(t.dataversion=e.dataversion)}function Q(t){let e=new WeakSet,r=n=>{if(n==null)return JSON.stringify(n);if(typeof n=="number")return Number.isFinite(n)?String(n):JSON.stringify(null);if(typeof n=="string"||typeof n=="boolean")return JSON.stringify(n);if(typeof n=="bigint")return JSON.stringify(n.toString());if(n instanceof Uint8Array){let s=n.length>64?Array.from(n.slice(0,32)).concat(Array.from(n.slice(-32))):Array.from(n);return JSON.stringify({__u8:!0,len:n.length,sample:s})}return Array.isArray(n)?`[${n.map(r).join(",")}]`:typeof n=="object"?e.has(n)?JSON.stringify("[Circular]"):(e.add(n),`{${Object.keys(n).sort().map(a=>`${JSON.stringify(a)}:${r(n[a])}`).join(",")}}`):JSON.stringify(null)};return r(t)}function X(t,e){let r=2166136261;for(let n=0;n<t;n++)r^=e(n),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r.toString(16).padStart(8,"0")}function Z(t){return X(t.length,e=>t.charCodeAt(e))}function ee(t){return X(t.length,e=>t[e])}function O(t,e){return Z(`${G(t)}|${Q(e)}`)}function G(t){return typeof t=="string"?t:`u8:${t.length}:${ee(t)}`}function Te(t){return t instanceof Uint8Array?!0:!/^https?:\/\//i.test(t)}var T=class{constructor(e,r,n={},s){p(this,"executor");p(this,"baseConfig");p(this,"mode");p(this,"maxConcurrent");p(this,"timeoutMs");p(this,"retry");p(this,"cacheEnabled");p(this,"cacheMax");p(this,"cacheTtl");p(this,"cache",new Map);p(this,"cacheKeyExecutor");p(this,"reuseServerDefinitionCache");p(this,"serverCacheKeys",new Map);p(this,"onStart");p(this,"onSettle");p(this,"onSuperseded");p(this,"subscribers",new Set);p(this,"inFlight",new Set);p(this,"pendingForLatestWins",null);p(this,"fifoQueue",[]);p(this,"_lastResult",null);p(this,"_lastError",null);p(this,"_lastDurationMs",null);p(this,"disposed",!1);this.executor=e,this.cacheKeyExecutor=s,this.baseConfig=r,this.mode=n.mode??"latest-wins",this.maxConcurrent=Math.max(1,n.maxConcurrent??(this.mode==="parallel"?4:1)),this.timeoutMs=n.timeoutMs,this.retry=n.retry;let o=n.cache;this.cacheEnabled=o!==void 0&&o!==!1;let a=typeof o=="object"?o:{};this.cacheMax=a.maxEntries??50,this.cacheTtl=a.ttlMs??0,this.reuseServerDefinitionCache=!!s&&(n.reuseServerDefinitionCache??!0),this.onStart=n.onStart,this.onSettle=n.onSettle,this.onSuperseded=n.onSuperseded}get isSolving(){return this.inFlight.size>0}get hasPending(){return this.pendingForLatestWins!==null||this.fifoQueue.length>0}get inFlightCount(){return this.inFlight.size}get queueDepth(){return this.fifoQueue.length+(this.pendingForLatestWins?1:0)}get lastResult(){return this._lastResult}get lastError(){return this._lastError}get lastDurationMs(){return this._lastDurationMs}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(){for(let e of this.subscribers)try{e()}catch(r){m().error("[SolveScheduler] subscriber threw:",r)}}solve(e,r,n){if(this.disposed)return Promise.reject(new l("SolveScheduler has been disposed and cannot be used",d.INVALID_STATE));let s=O(e,r),o={key:s,enqueuedAt:Date.now(),startedAt:null};if(this.cacheEnabled){let a=this.readCache(s);if(a){let i={status:"success",response:a,durationMs:0,fromCache:!0};return this._lastResult=a,this._lastError=null,this._lastDurationMs=0,this.runHook(this.onStart,o),this.runHook(this.onSettle,o,i),this.notify(),Promise.resolve(a)}}return new Promise((a,i)=>{let u={definition:e,dataTree:r,ctx:o,resolve:a,reject:i,externalSignal:n?.signal};if(u.externalSignal?.aborted){this.settleError(u,this.makeAbortError(o));return}this.enqueue(u)})}enqueue(e){switch(this.mode){case"latest-wins":{this.pendingForLatestWins&&(this.supersede(this.pendingForLatestWins),this.pendingForLatestWins=null);for(let r of this.inFlight)this.supersede(r),r.controller.abort();this.inFlight.size===0?this.execute(e):this.pendingForLatestWins=e;break}case"queue":case"parallel":{this.inFlight.size<this.maxConcurrent?this.execute(e):this.fifoQueue.push(e);break}}this.notify()}async execute(e){let r=new AbortController,n={...e,controller:r};this.inFlight.add(n),e.ctx.startedAt=Date.now();let s=()=>r.abort();e.externalSignal?.addEventListener("abort",s,{once:!0}),this.runHook(this.onStart,e.ctx),this.notify();let o=performance.now();try{let a={...this.baseConfig,signal:r.signal,...this.timeoutMs!==void 0&&{timeoutMs:this.timeoutMs},...this.retry!==void 0&&{retry:this.retry}},i=await this.runExecutor(e.definition,e.dataTree,a),u=performance.now()-o;if(this.cacheEnabled&&this.writeCache(e.ctx.key,i),!this.settleSuccess(e,i))return;this._lastResult=i,this._lastError=null,this._lastDurationMs=u,this.runHook(this.onSettle,e.ctx,{status:"success",response:i,durationMs:u,fromCache:!1})}catch(a){let i=performance.now()-o,u=this.normalizeExecutionError(a,n);this._lastError=u,this._lastDurationMs=i,this.settleError(e,u)&&this.runHook(this.onSettle,e.ctx,{status:"error",error:u,durationMs:i})}finally{e.externalSignal?.removeEventListener("abort",s),this.inFlight.delete(n),this.drainNext(),this.notify()}}async runExecutor(e,r,n){if(!this.cacheKeyExecutor||!this.reuseServerDefinitionCache||!Te(e))return this.executor(e,r,n);let s=G(e),o=this.serverCacheKeys.get(s)??null,a=await this.cacheKeyExecutor(e,r,o,n);return a.cacheKey?this.serverCacheKeys.set(s,a.cacheKey):this.serverCacheKeys.delete(s),a.response}drainNext(){if(!this.disposed){if(this.mode==="latest-wins"){if(this.pendingForLatestWins&&this.inFlight.size===0){let e=this.pendingForLatestWins;this.pendingForLatestWins=null,this.execute(e)}return}for(;this.fifoQueue.length>0&&this.inFlight.size<this.maxConcurrent;){let e=this.fifoQueue.shift();this.execute(e)}}}supersede(e){let r=new l("Superseded by newer solve",d.SUPERSEDED,{context:{key:e.ctx.key,enqueuedAt:e.ctx.enqueuedAt}});this.settleError(e,r)&&this.runHook(this.onSuperseded,e.ctx)}makeAbortError(e){return new l("Request aborted by caller",d.ABORTED,{context:{key:e.key,enqueuedAt:e.enqueuedAt}})}settleError(e,r){return e.settled?!1:(e.settled={error:r},e.reject(r),!0)}settleSuccess(e,r){return e.settled?!1:(e.settled={ok:!0},e.resolve(r),!0)}isAbortLikeError(e){if(e instanceof Error){if(e.name==="AbortError")return!0;if(typeof DOMException<"u"&&e instanceof DOMException)return e.name==="AbortError"}return!1}normalizeExecutionError(e,r){return r.settled&&"error"in r.settled?r.settled.error:e instanceof l?e:this.isAbortLikeError(e)?this.makeAbortError(r.ctx):new l(e instanceof Error?e.message:String(e),d.UNKNOWN_ERROR,{originalError:e instanceof Error?e:new Error(String(e))})}cancelAll(){for(this.pendingForLatestWins&&(this.rejectAsAborted(this.pendingForLatestWins),this.pendingForLatestWins=null);this.fifoQueue.length>0;){let e=this.fifoQueue.shift();this.rejectAsAborted(e)}for(let e of this.inFlight){let r=this.makeAbortError(e.ctx);this.settleError(e,r)&&this.runHook(this.onSettle,e.ctx,{status:"error",error:r,durationMs:e.ctx.startedAt?performance.now()-e.ctx.startedAt:0}),e.controller.abort()}this.notify()}rejectAsAborted(e){this.settleError(e,this.makeAbortError(e.ctx))}readCache(e){if(!this.cacheEnabled)return null;let r=this.cache.get(e);return r?this.cacheTtl>0&&Date.now()-r.insertedAt>this.cacheTtl?(this.cache.delete(e),null):(this.cache.delete(e),this.cache.set(e,r),r.response):null}writeCache(e,r){if(this.cacheEnabled)for(this.cache.set(e,{response:r,insertedAt:Date.now()});this.cache.size>this.cacheMax;){let n=this.cache.keys().next().value;if(n===void 0)break;this.cache.delete(n)}}clearCache(){this.cache.clear()}dispose(){this.disposed||(this.disposed=!0,this.cancelAll(),this.subscribers.clear(),this.cache.clear())}runHook(e,...r){if(e)try{e(...r)}catch(n){m().error("[SolveScheduler] hook threw:",n)}}};var P=class t{constructor(e){p(this,"config");p(this,"serverStats");p(this,"disposed",!1);this.config=this.normalizeComputeConfig(e),this.serverStats=new $(this.config.serverUrl,this.config.apiKey)}static async create(e){let r=new t(e);if(!await r.serverStats.isServerOnline())throw new l("Rhino Compute server is not online",d.NETWORK_ERROR,{context:{serverUrl:r.config.serverUrl}});return r}getConfig(){return this.ensureNotDisposed(),{...this.config}}async getIO(e){return this.ensureNotDisposed(),V(e,this.config)}async getRawIO(e){return this.ensureNotDisposed(),R(e,this.config)}async solve(e,r,n){this.ensureNotDisposed();try{if(typeof e=="string"&&!e?.trim())throw new l("Definition URL/content is required",d.INVALID_INPUT,{context:{receivedUrl:e}});if(e instanceof Uint8Array&&e.length===0)throw new l("Definition content is empty",d.INVALID_INPUT);let s={...this.config,...n?.signal!==void 0&&{signal:n.signal},...n?.timeoutMs!==void 0&&{timeoutMs:n.timeoutMs},...n?.retry!==void 0&&{retry:n.retry}},o=await x(r,e,s);if(o?.errors&&o.errors.length>0)throw new l(o.errors.join("; ")||"Computation failed",d.COMPUTATION_ERROR,{context:{definition:typeof e=="string"&&e.length<200?e:"...content...",inputs:r,errors:o.errors,warnings:o.warnings}});return o}catch(s){throw this.config.debug&&m().error("Compute failed:",s),s instanceof l?s:new l(s instanceof Error?s.message:String(s),d.COMPUTATION_ERROR,{context:{definition:typeof e=="string"&&e.length<200?e:"...content...",inputs:r},originalError:s instanceof Error?s:new Error(String(s))})}}createScheduler(e){this.ensureNotDisposed();let r=(s,o,a)=>x(o,s,a),n=(s,o,a,i)=>a===null?H(o,s,i).then(u=>({...u,missed:!1})):Y(o,a,s,i);return new T(r,this.config,e,n)}async dispose(){this.disposed||(this.disposed=!0,await this.serverStats.dispose())}ensureNotDisposed(){if(this.disposed)throw new l("GrasshopperClient has been disposed and cannot be used",d.INVALID_STATE)}normalizeComputeConfig(e){return{...e,serverUrl:z(e.serverUrl),apiKey:e.apiKey,authToken:e.authToken,debug:e.debug??!1,suppressBrowserWarning:e.suppressBrowserWarning??e.suppressClientSideWarning}}};var F=new Map;function te(t,e){F.set(t,e)}te("Rhino.Geometry.Point3d",(t,e)=>{let r=e;return!r||typeof r.X!="number"?null:new t.Point([r.X,r.Y,r.Z])});te("Rhino.Geometry.Line",(t,e)=>{let r=e;return!r||!r.From||!r.To?null:new t.Line([r.From.X,r.From.Y,r.From.Z],[r.To.X,r.To.Y,r.To.Z])});function Ce(t){if(F.has(t))return F.get(t);for(let[e,r]of F)if(t.startsWith(e))return r}function Ie(t){return!t||typeof t!="object"?null:t.data??t.value??null}function re(t,e,r){let n=Ce(e);if(n)try{return n(r,t)}catch(s){m().warn(`Failed to decode Rhino type ${e}:`,s)}try{let s=Ie(t);if(s)return r.CommonObject.decode(s)}catch(s){return m().warn(`Failed to decode ${e} with CommonObject:`,s),{__decodeError:!0,type:e,raw:t}}return t}var C={STRING:"System.String",INT:"System.Int32",DOUBLE:"System.Double",BOOL:"System.Boolean"},Se="Rhino.Geometry.",xe=["WebDisplay"],De="FileData";function ne(t){return xe.some(e=>t.includes(e))}function se(t){if(typeof t!="string")return t;let e=t.trim();if(!(e.startsWith("{")||e.startsWith("[")||e.startsWith('"')))return t;try{let n=JSON.parse(e);if(typeof n=="string")try{return JSON.parse(n)}catch{return n}return n}catch{return t}}function Pe(t,e,r){switch(e){case C.STRING:return typeof t!="string"?t:t.replace(/^"(.*)"$/,"$1");case C.INT:return Number.parseInt(t,10);case C.DOUBLE:return Number.parseFloat(t);case C.BOOL:return String(t).toLowerCase()==="true";default:return r&&e.startsWith(Se)?re(t,e,r):t}}function oe(t,e,r,n){if(typeof t!="string")return t;let s=r?se(t):t;return Pe(s,e,n)}function Re(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.fileName=="string"&&typeof e.fileType=="string"&&"data"in e&&typeof e.isBase64Encoded=="boolean"&&typeof e.subFolder=="string"}function M(t,e){for(let r of Object.values(t))if(Array.isArray(r))for(let n of r)e(n)}function ae(t,e=!1,r={}){let{parseValues:n=!0,rhino:s,stringOnly:o=!1}=r,a={};for(let i of t.values)M(i.InnerTree,u=>{if(ne(u.type)||o&&u.type!==C.STRING)return;let c=e?u.id:i.ParamName;if(!c)return;let f=oe(u.data,u.type,n,s);a[c]===void 0?a[c]=f:Array.isArray(a[c])?a[c].push(f):a[c]=[a[c],f]});return{values:a}}function ie(t){let e=[];for(let r of t.values)M(r.InnerTree,n=>{if(!n.type.includes(De))return;let s=se(n.data);Re(s)&&e.push(s)});return e}function k(t,e,r={}){let{parseValues:n=!0,rhino:s,stringOnly:o=!1}=r,a;if("byName"in e?a=t.values.find(u=>u.ParamName===e.byName):a=t.values.find(u=>{let c=!1;return M(u.InnerTree,f=>{f.id===e.byId&&(c=!0)}),c}),!a)return;let i=[];if(M(a.InnerTree,u=>{if("byId"in e&&u.id!==e.byId||ne(u.type)||o&&u.type!==C.STRING)return;let c=oe(u.data,u.type,n,s);i.push(c)}),i.length!==0)return i.length===1?i[0]:i}var E=class{constructor(e,r=!1){this.response=e;this.debug=r}getValues(e=!1,r={}){return ae(this.response,e,r)}getValue(e,r){return k(this.response,e,r)}getValueByParamName(e,r){return k(this.response,{byName:e},r)}getValueByParamId(e,r){return k(this.response,{byId:e},r)}async extractMeshesFromResponse(e){let r={debug:this.debug,...e},n;try{({getThreeMeshesFromComputeResponse:n}=await import("./visualization-AQNCY6MG.js"))}catch(s){throw new l("Failed to load three.js visualization module. Ensure three.js is installed as a peer dependency.",d.INVALID_STATE,{context:{originalError:s instanceof Error?s.message:String(s)}})}return n(this.response,r)}getFileData(){return ie(this.response)}getAndDownloadFiles(e,r){let n=this.getFileData();A(n,e,r)}};function ue(t){if(typeof t.default!="object"||t.default===null)return t;if(!("innerTree"in t.default))return m().warn("Unexpected structure in input.default:",t.default),{...t,default:null};let e=t.default.innerTree;if(Object.keys(e).length===0)return{...t,default:void 0};if(t.treeAccess||t.atMost&&t.atMost>1){let n={};for(let[s,o]of Object.entries(e))n[s]=o.map(a=>{if(typeof a.data=="string"){if(a.type==="System.Double"||a.type==="System.Int32"){let i=Number(a.data);return Number.isNaN(i)?a.data:i}if(a.type==="System.Boolean")return a.data.toLowerCase()==="true";if(a.type.startsWith("Rhino.Geometry")||a.type==="System.String")try{return JSON.parse(a.data)}catch{return a.data}}return a.data});return{...t,default:n}}let r=[];for(let n of Object.values(e))Array.isArray(n)&&n.forEach(s=>{s&&typeof s=="object"&&"data"in s&&r.push(s.data)});return r.length===0?{...t,default:void 0}:r.length===1?{...t,default:r[0]}:{...t,default:r}}var W=/^\{([\d;]*)\}$/;function v(t){if(typeof t!="object"||t===null||Array.isArray(t))return!1;let e=Object.entries(t);return e.length>0&&e.every(([r,n])=>W.test(r)&&Array.isArray(n))}function I(t,e,r){if(t==null)return t;if(Array.isArray(t)){let s=t.map(e).filter(o=>o!==null);return s.length>0?s:void 0}let n=e(t);return n!==null?n:r?void 0:t}var Ee=t=>{if(typeof t=="number")return t;if(typeof t=="string"){let e=Number(t.trim());return Number.isNaN(e)?null:e}return null},ve=t=>{if(typeof t=="boolean")return t;if(typeof t=="string"){let e=t.toLowerCase();if(e==="true")return!0;if(e==="false")return!1;throw new Error(`Invalid boolean string: "${t}"`)}return null},we=t=>typeof t=="string"?t.startsWith('"')&&t.endsWith('"')||t.startsWith('"')?t.slice(1,-1):t:null,Ae=t=>{if(typeof t=="string"){let e=t.trim();return e.startsWith('"')&&e.endsWith('"')&&(e=e.slice(1,-1).trim()),e}return null};function le(t){return e=>{if(typeof e=="object"&&e!==null)return e;if(typeof e=="string"&&e.trim()!=="")try{let r=JSON.parse(e);return typeof r=="object"&&r!==null?r:(m().warn(`Parsed value for input ${t} is not an object`),null)}catch(r){return m().warn(`Failed to parse object value "${e}" for input ${t}`,r),null}return null}}function pe(t,e,r){let n=Number(t.toFixed(e));return Math.abs(t-n)<r?n:t}function Ne(t,e){if(!Number.isFinite(t)||t===0)return .1;let r=Math.abs(t);if(r>=1){let h=String(t).split(".")[1];if(h&&h.length>0){let y=Math.min(h.length,12),g=Math.pow(10,-y),K=Number(g.toFixed(y));return Math.abs(K-g)<e?K:g}return 1}let n=String(t),s=n.toLowerCase().match(/e(-?\d+)/);if(s){let S=Number(s[1]);if(S<0||n.toLowerCase().includes("e-")){let h=Math.abs(S),y=Math.pow(10,-h),g=Number(y.toFixed(h));return Math.abs(g-y)<e?g:y}return .1}let o=12,i=r.toFixed(o).replace(/0+$/,""),u=Math.min((i.split(".")[1]||"").length,o);if(u===0)return .1;let c=Math.pow(10,-u),f=Number(c.toFixed(u));return Math.abs(f-c)<e?f:c}function Oe(t,e=1e-8){let r=t.paramType==="Integer";if(v(t.default))return{default:t.default,stepSize:r?1:.1};let n=I(t.default,Ee,!0);if(r)return Array.isArray(n)?n=n.map(f=>typeof f=="number"?Math.round(f):f):typeof n=="number"&&(n=Math.round(n)),{default:n,stepSize:1};let s=Array.isArray(n)?n[0]:n,o;typeof s=="number"&&Number.isFinite(s)&&s!==0?o=s:typeof t.minimum=="number"&&Number.isFinite(t.minimum)&&t.minimum!==0?o=t.minimum:typeof t.maximum=="number"&&Number.isFinite(t.maximum)&&t.maximum!==0&&(o=t.maximum);let a=o!==void 0?Ne(o,e):.1,i=0,u=String(a),c=u.toLowerCase().match(/e(-?\d+)/);if(c?i=Math.abs(Number(c[1])):i=u.split(".")[1]?.length??0,i===0&&typeof s=="number"&&s!==0&&Math.abs(s)<1){let f=Math.ceil(-Math.log10(Math.abs(s)));Number.isFinite(f)&&f>0&&(i=f)}return i=Math.min(Math.max(i,0),12),Array.isArray(n)?n=n.map(f=>typeof f=="number"?pe(f,i,e):f):typeof n=="number"&&(n=pe(n,i,e)),{default:n,stepSize:a}}var Ge={types:["Number","Integer"],parse(t,e){let{default:r,stepSize:n}=Oe(t);return{...e,paramType:t.paramType,minimum:t.minimum,maximum:t.maximum,atLeast:t.atLeast,atMost:t.atMost,stepSize:n,default:r}},fallback(t,e){let r=(t.atMost??1)>1;return{...e,paramType:t.paramType,minimum:t.minimum,maximum:t.maximum,atLeast:t.atLeast,atMost:t.atMost,default:r?[0]:0}}},Ve={types:["Boolean"],parse(t,e){let r;try{r=I(t.default,ve,!1)}catch(n){throw n instanceof Error?new l(n.message):n}return{...e,paramType:"Boolean",default:r}},fallback(t,e){let r=(t.atMost??1)>1;return{...e,paramType:"Boolean",default:r?[!1]:!1}}},Fe={types:["Text"],parse(t,e){let r=I(t.default,we,!1);return{...e,paramType:"Text",default:r}},fallback(t,e){let r=(t.atMost??1)>1;return{...e,paramType:"Text",default:r?[""]:""}}},Me={types:["ValueList"],parse(t,e){if(!t.values||typeof t.values!="object"||Object.keys(t.values).length===0)throw l.missingValues(t.nickname||"unnamed","ValueList");if(t.default!==void 0&&t.default!==null){let r=String(t.default).toLowerCase();Object.keys(t.values).some(s=>s.toLowerCase()===r)||m().warn(`ValueList input "${t.nickname||"unnamed"}" default value "${t.default}" is not in available values`)}return{...e,paramType:"ValueList",values:t.values,default:t.default}},fallback(t,e){let r=(t.atMost??1)>1;return{...e,paramType:"ValueList",values:t.values??{},default:r?[t.default]:t.default}}},ce={types:["Geometry"],parse(t,e){let r=I(t.default,le(t.nickname||"unnamed"),!0);return{...e,paramType:"Geometry",default:r}},fallback(t,e){let r=(t.atMost??1)>1;return{...e,paramType:"Geometry",default:r?[null]:null}}},ke={types:["File"],parse(t,e){let r=I(t.default,le(t.nickname||"unnamed"),!0);return{...e,paramType:"File",acceptedFormats:t.acceptedFormats,default:r}},fallback(t,e){let r=(t.atMost??1)>1;return{...e,paramType:"File",default:r?[null]:null}}},Le={types:["Color"],parse(t,e){let r=I(t.default,Ae,!1);return{...e,paramType:"Color",default:r}},fallback(t,e){let r=(t.atMost??1)>1;return{...e,paramType:"Color",default:r?["0, 0, 0"]:"0, 0, 0"}}},Be=[Ge,Ve,Fe,Me,ce,ke,Le],j=new Map(Be.flatMap(t=>t.types.map(e=>[e,t]))),fe=ce;var We=new Map([...j.keys()].map(t=>[t.toLowerCase(),t]));function je(t){return We.get(t?.toLowerCase())??t}function me(t){return U(t).input}function U(t){let e={description:t.description,name:t.name,nickname:t.nickname,treeAccess:t.treeAccess,groupName:t.groupName??"",id:t.id},r=je(t.paramType),n=ue({...t,paramType:r}),s=j.get(r);try{if(!s)throw l.unknownParamType(r,t.name);return{input:s.parse(n,e)}}catch(o){if(o instanceof l)return m().error(`Validation error for input ${t.name||"unknown"}:`,o.message),{input:(s??fe).fallback(n,e),error:{inputName:t.name||"unknown",paramType:r,message:o.message,code:o.code}};throw new l(o instanceof Error?o.message:String(o),"VALIDATION_ERROR",{context:{paramName:t.name,paramType:r},originalError:o instanceof Error?o:new Error(String(o))})}}function de(t){return L(t).inputs}function L(t){let e=[],r=[];for(let n of t){let{input:s,error:o}=U(n);e.push(s),o&&r.push(o)}return{inputs:e,parseErrors:r}}async function R(t,e){let r=D(t,[]),n={};if(r.algo&&(n.algo=r.algo),r.pointer&&(n.pointer=r.pointer),!n.algo&&!n.pointer)throw new l("Definition must resolve to either a URL pointer or base64 algo",d.INVALID_INPUT,{context:{definition:t}});let s=await w("io",n,e);if(!s||typeof s!="object")throw new l("Invalid IO response structure",d.INVALID_INPUT,{context:{response:s,definition:t}});let o=he(s.warnings),a=he(s.errors);return{inputs:s.inputs,outputs:s.outputs,...o&&{loadWarnings:o},...a&&{loadErrors:a}}}function he(t){if(!Array.isArray(t))return;let e=t.filter(r=>typeof r=="string"&&r.trim().length>0);return e.length>0?e:void 0}async function V(t,e){b("fetchParsedDefinitionIO",e.suppressBrowserWarning??e.suppressClientSideWarning);let{inputs:r,outputs:n,loadWarnings:s,loadErrors:o}=await R(t,e),{inputs:a,parseErrors:i}=L(r);return{inputs:a,outputs:n,...i.length>0&&{parseErrors:i},...s&&{loadWarnings:s},...o&&{loadErrors:o}}}var B=class t{constructor(e){p(this,"innerTree");p(this,"paramName");this.paramName=e,this.innerTree={}}append(e,r){let n=t.formatPathString(e);this.innerTree[n]||(this.innerTree[n]=[]);let s=r.map(o=>({data:t.serializeValue(o)}));return this.innerTree[n].push(...s),this}appendSingle(e,r){return this.append(e,[r])}fromDataTreeDefault(e){this.innerTree={};for(let[r,n]of Object.entries(e)){if(!Array.isArray(n))continue;let s=t.parsePathString(r);this.append(s,n)}return this}appendFlat(e){let r=Array.isArray(e)?e:[e];return this.append([0],r)}flatten(){let e=[];for(let r of Object.values(this.innerTree))if(Array.isArray(r))for(let n of r)e.push(t.deserializeValue(n.data));return e}getPaths(){return Object.keys(this.innerTree)}getPath(e){let r=t.formatPathString(e),n=this.innerTree[r];if(n)return n.map(s=>t.deserializeValue(s.data))}toComputeFormat(){return{ParamName:this.paramName,InnerTree:this.innerTree}}getInnerTree(){return this.innerTree}getParamName(){return this.paramName}static fromInputParams(e){return e.filter(r=>t.hasValidValue(r.default)).map(r=>{let n=new t(r.nickname||"unnamed"),s=r.default;if(r.treeAccess&&v(s))n.fromDataTreeDefault(s),t.isNumericInput(r)&&n.applyNumericConstraints(r.minimum,r.maximum,r.nickname||"unnamed");else{let o=Array.isArray(s)?s:[s],a=t.processValues(o,r);n.appendFlat(a)}return n.toComputeFormat()})}static fromInputParam(e){return t.hasValidValue(e.default)?t.fromInputParams([e])[0]:void 0}static replaceTreeValue(e,r,n){let s=e.length>0&&e[0]instanceof t,o=t.buildFromValue(r,n);if(s){let c=e,f=c.findIndex(S=>S.getParamName()===r);return f!==-1?c[f]=o:c.push(o),c}let a=e,i=o.toComputeFormat(),u=a.findIndex(c=>c.ParamName===r);return u!==-1?a[u]=i:a.push(i),a}static buildFromValue(e,r){let n=new t(e);return v(r)?n.fromDataTreeDefault(r):n.appendFlat(r),n}static getTreeValue(e,r){let s=e.length>0&&e[0]instanceof t?t.readFromBuilders(e,r):t.readFromDataTrees(e,r);return s===null||s.length===0?null:s.length===1?s[0]:s}static readFromBuilders(e,r){let n=e.find(s=>s.getParamName()===r);return n?n.flatten():null}static readFromDataTrees(e,r){let n=e.find(a=>a.ParamName===r);if(!n?.InnerTree)return null;let s=Object.keys(n.InnerTree)[0];if(!s)return null;let o=n.InnerTree[s];return Array.isArray(o)?o.map(a=>a?.data!==void 0?t.deserializeValue(a.data):null).filter(a=>a!==null):o?.data!==void 0?[t.deserializeValue(o.data)]:o!==void 0?[o]:null}static parsePathString(e){let r=e.match(W);return r?r[1]===""?[]:r[1].split(";").map(Number):(m().warn(`Invalid TreeBuilder path format: ${e}, using [0]`),[0])}static formatPathString(e){return`{${e.join(";")}}`}applyNumericConstraints(e,r,n){for(let s of Object.values(this.innerTree))if(Array.isArray(s))for(let o of s){let a=t.deserializeValue(o.data);if(typeof a=="number"){let i=t.clampValue(a,e,r,n);o.data=t.serializeValue(i)}}}static serializeValue(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:typeof e=="object"&&e!==null?JSON.stringify(e):String(e)}static deserializeValue(e){if(typeof e=="boolean"||typeof e=="number"||typeof e!="string")return e;if(e.startsWith("{")||e.startsWith("["))try{return JSON.parse(e)}catch{return e}return isNaN(Number(e))?e==="true"?!0:e==="false"?!1:e:Number(e)}static hasValidValue(e){return e==null?!1:typeof e=="string"?!0:!(Array.isArray(e)&&e.length===0||typeof e=="object"&&!Array.isArray(e)&&Object.keys(e).length===0)}static isNumericInput(e){return e.paramType==="Number"||e.paramType==="Integer"}static processValues(e,r){return e.map(n=>t.isNumericInput(r)&&typeof n=="number"?t.clampValue(n,r.minimum,r.maximum,r.nickname||"unnamed"):n).filter(n=>n!=null)}static clampValue(e,r,n,s){let o=e;return r!=null&&o<r&&(m().warn(`${s}: ${e} below min ${r}, clamping`),o=r),n!=null&&o>n&&(m().warn(`${s}: ${e} above max ${n}, clamping`),o=n),o}};export{x as a,O as b,T as c,P as d,E as e,me as f,de as g,R as h,V as i,B as j};
|
|
2
|
+
//# sourceMappingURL=chunk-3N5WXYQZ.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/core/utils/warnings.ts","../src/features/grasshopper/solve.ts","../src/features/grasshopper/scheduler/stable-hash.ts","../src/features/grasshopper/scheduler/solve-scheduler.ts","../src/features/grasshopper/client/grasshopper-client.ts","../src/features/grasshopper/io/output/rhino-decoder.ts","../src/features/grasshopper/io/output/response-processors.ts","../src/features/grasshopper/client/grasshopper-response-processor.ts","../src/features/grasshopper/io/input/normalize-default.ts","../src/features/grasshopper/data-tree/tree-path.ts","../src/features/grasshopper/io/input/input-type-parsers.ts","../src/features/grasshopper/io/input/input-processors.ts","../src/features/grasshopper/io/definition-io.ts","../src/features/grasshopper/data-tree/data-tree.ts"],"sourcesContent":["import { getLogger } from './logger';\n\nexport function warnIfClientSide(functionName: string, suppress?: boolean): void {\n\tif (suppress) {\n\t\treturn;\n\t}\n\n\tif (typeof window !== 'undefined') {\n\t\tgetLogger().warn(\n\t\t\t`Warning: ${functionName} is running on the client side. For better performance and security, consider running this on the server side.`\n\t\t);\n\t}\n}\n","import { fetchRhinoCompute, RhinoComputeError } from '@/core';\nimport { base64ByteArray, encodeStringToBase64, isBase64 } from '@/core/utils/encoding';\nimport { warnIfClientSide } from '@/core/utils/warnings';\n\nimport {\n\tGrasshopperRequestSchema,\n\tGrasshopperComputeConfig,\n\tGrasshopperComputeResponse,\n\tDataTree\n} from './types';\n\n/**\n * The exact message the server throws when it can neither resolve a `pointer`\n * nor a base64 `algo` to a definition (ResthopperEndpoints.cs). This is the\n * signal that a cache-key pointer missed the server's definition cache (GC'd, or\n * a different child in the pool), so the caller should retry with the full\n * definition. Matched as a substring because the server wraps it with a category\n * prefix in its exception handler.\n */\nconst DEFINITION_LOAD_FAILED = 'Unable to load grasshopper definition';\n\n/** Does this error look like a server-side definition-load miss? */\nfunction isDefinitionLoadMiss(error: unknown): boolean {\n\treturn error instanceof RhinoComputeError && error.message.includes(DEFINITION_LOAD_FAILED);\n}\n\n/**\n * Result of a solve that also reports the definition's server-side cache key.\n *\n * `cacheKey` is the `md5_…` identifier the server assigned to the (base64)\n * definition — stable for identical content. A caller that holds it can solve\n * the same definition again by reference (`pointer: cacheKey`) instead of\n * re-uploading the full base64, which matters a lot for large (multi-MB)\n * definitions on a live UI. `null` when the server didn't return one (e.g. a\n * URL-pointer solve).\n */\nexport interface SolveWithCacheKey {\n\tresponse: GrasshopperComputeResponse;\n\tcacheKey: string | null;\n}\n\n/**\n * Runs a Rhino Compute job using the provided tree prototypes and Grasshopper definition.\n *\n * @public Use this for direct compute control. For high-level API, use `GrasshopperClient.solve()`.\n *\n * @param dataTree - An array of `DataTree` objects representing the input data for the compute job.\n * @param definition - The Grasshopper definition, which can be:\n * - A URL string (e.g., 'https://example.com/definition.gh')\n * - A base64-encoded string of the .gh file\n * - A plain string (will be base64-encoded)\n * - A Uint8Array of the .gh file (will be base64-encoded)\n * @param config - Compute configuration (server URL, API key, etc. along with optional timeout, units, etc.)\n * @returns An object containing the compute result and extracted file data.\n *\n * @example\n * // Using a URL\n * await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);\n *\n * // Using a base64 string\n * await solveGrasshopperDefinition(trees, 'UEsDBBQAAAAIAL...', config);\n *\n * // Using binary data\n * const fileData = new Uint8Array([...]);\n * await solveGrasshopperDefinition(trees, fileData, config);\n */\nexport async function solveGrasshopperDefinition(\n\tdataTree: DataTree[],\n\tdefinition: string | Uint8Array,\n\tconfig: GrasshopperComputeConfig\n): Promise<GrasshopperComputeResponse> {\n\tif (config.debug) {\n\t\twarnIfClientSide(\n\t\t\t'solveGrasshopperDefinition',\n\t\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t);\n\t}\n\n\tconst { response } = await runSolve(prepareGrasshopperArgs(definition, dataTree), config);\n\treturn response;\n}\n\n/**\n * Solve while reporting the server's definition cache key.\n *\n * Behaves like {@link solveGrasshopperDefinition} but returns the `cacheKey` the\n * server assigned, so a caller (e.g. the scheduler) can later solve the same\n * definition by reference instead of re-uploading it. The cache key is only\n * meaningful for base64/binary definitions; a URL-pointer solve returns the URL.\n *\n * @internal\n */\nexport async function solveGrasshopperDefinitionWithCacheKey(\n\tdataTree: DataTree[],\n\tdefinition: string | Uint8Array,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tif (config.debug) {\n\t\twarnIfClientSide(\n\t\t\t'solveGrasshopperDefinitionWithCacheKey',\n\t\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t);\n\t}\n\n\treturn runSolve(prepareGrasshopperArgs(definition, dataTree), config);\n}\n\n/**\n * Solve a definition by its server-side cache key (`pointer: cacheKey`),\n * skipping the (potentially multi-MB) base64 upload. If the key has been evicted\n * from the server's definition cache — `DEFINITION_LOAD_FAILED` — transparently\n * retry once with the full `definition` and report the fresh cache key so the\n * caller can update its mapping.\n *\n * @returns The solve result plus the (possibly refreshed) cache key, and whether\n * the fast path missed (so callers can record the new key / track hit rate).\n * @internal\n */\nexport async function solveByCacheKey(\n\tdataTree: DataTree[],\n\tcacheKey: string,\n\tdefinition: string | Uint8Array,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey & { missed: boolean }> {\n\tif (config.debug) {\n\t\twarnIfClientSide(\n\t\t\t'solveByCacheKey',\n\t\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t);\n\t}\n\n\tconst pointerArgs: GrasshopperRequestSchema = { algo: null, pointer: cacheKey, values: dataTree };\n\n\ttry {\n\t\tconst fast = await runSolve(pointerArgs, config);\n\t\treturn { ...fast, missed: false };\n\t} catch (error) {\n\t\tif (!isDefinitionLoadMiss(error)) throw error;\n\t\t// Cache miss — fall back to the full upload and capture the fresh key.\n\t\tconst full = await runSolve(prepareGrasshopperArgs(definition, dataTree), config);\n\t\treturn { ...full, missed: true };\n\t}\n}\n\n/**\n * Shared solve body: apply optional settings, POST, and split the server's\n * `pointer` (its cache key) off the response. Stripping via shallow copy rather\n * than `delete` keeps any already-observed response object unmutated.\n */\nasync function runSolve(\n\targs: GrasshopperRequestSchema,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tapplyOptionalComputeSettings(args, config);\n\n\tconst result = await fetchRhinoCompute<GrasshopperComputeResponse>('grasshopper', args, config);\n\n\tif ('pointer' in result) {\n\t\tconst { pointer, ...rest } = result as GrasshopperComputeResponse & { pointer?: unknown };\n\t\treturn {\n\t\t\tresponse: rest as GrasshopperComputeResponse,\n\t\t\tcacheKey: typeof pointer === 'string' ? pointer : null\n\t\t};\n\t}\n\n\treturn { response: result, cacheKey: null };\n}\n\n// ============================================================================\n// Grasshopper Arguments\n// ============================================================================\n\n/**\n * Prepares Grasshopper arguments from a definition and data tree.\n * Automatically detects the definition format and converts it appropriately.\n *\n * @param definition - Can be a URL, base64 string, plain string, or Uint8Array\n * @param dataTree - Array of DataTree objects for compute inputs\n * @internal\n */\nexport function prepareGrasshopperArgs(\n\tdefinition: string | Uint8Array,\n\tdataTree: DataTree[]\n): GrasshopperRequestSchema {\n\tconst args: GrasshopperRequestSchema = {\n\t\talgo: null,\n\t\tpointer: null,\n\t\tvalues: dataTree\n\t};\n\n\tif (definition instanceof Uint8Array) {\n\t\t// Binary data → convert to base64\n\t\targs.algo = base64ByteArray(definition);\n\t} else if (/^https?:\\/\\//i.test(definition)) {\n\t\t// URL → use as pointer reference\n\t\targs.pointer = definition;\n\t} else if (isBase64(definition)) {\n\t\t// Already base64 → use as-is\n\t\targs.algo = definition;\n\t} else {\n\t\t// Plain string → encode to base64\n\t\targs.algo = encodeStringToBase64(definition);\n\t}\n\n\treturn args;\n}\n\n/**\n * @internal\n */\nexport function applyOptionalComputeSettings(\n\targlist: GrasshopperRequestSchema,\n\toptions: GrasshopperComputeConfig\n): void {\n\tif (options.cachesolve != null) arglist.cachesolve = options.cachesolve;\n\tif (options.modelunits != null) arglist.modelunits = options.modelunits;\n\tif (options.angletolerance != null) arglist.angletolerance = options.angletolerance;\n\tif (options.absolutetolerance != null) arglist.absolutetolerance = options.absolutetolerance;\n\tif (options.dataversion != null) arglist.dataversion = options.dataversion;\n}\n","/**\n * Stable hashing for solve deduplication and caching.\n * @internal\n */\n\n/**\n * Deterministic stringify with sorted keys. {a:1,b:2} and {b:2,a:1} produce\n * the same string. Safely handles circular references and non-finite numbers.\n */\nexport function stableStringify(value: unknown): string {\n\tconst seen = new WeakSet<object>();\n\n\tconst stringify = (v: unknown): string => {\n\t\tif (v === null || v === undefined) return JSON.stringify(v);\n\t\tif (typeof v === 'number') {\n\t\t\treturn Number.isFinite(v) ? String(v) : JSON.stringify(null);\n\t\t}\n\t\tif (typeof v === 'string' || typeof v === 'boolean') return JSON.stringify(v);\n\t\tif (typeof v === 'bigint') return JSON.stringify(v.toString());\n\t\tif (v instanceof Uint8Array) {\n\t\t\t// Use length + sample instead of full buffer to avoid stringifying large data\n\t\t\tconst sample =\n\t\t\t\tv.length > 64 ? Array.from(v.slice(0, 32)).concat(Array.from(v.slice(-32))) : Array.from(v);\n\t\t\treturn JSON.stringify({ __u8: true, len: v.length, sample });\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\treturn `[${v.map(stringify).join(',')}]`;\n\t\t}\n\t\tif (typeof v === 'object') {\n\t\t\tif (seen.has(v as object)) return JSON.stringify('[Circular]');\n\t\t\tseen.add(v as object);\n\t\t\tconst keys = Object.keys(v as object).sort();\n\t\t\tconst parts = keys.map((k) => `${JSON.stringify(k)}:${stringify((v as any)[k])}`);\n\t\t\treturn `{${parts.join(',')}}`;\n\t\t}\n\t\t// Fallback for functions, symbols, etc.\n\t\treturn JSON.stringify(null);\n\t};\n\n\treturn stringify(value);\n}\n\n/**\n * 32-bit FNV-1a core over a sequence of byte/char codes. Returns unsigned hex.\n * Shared by the string and byte hashers so they stay the same algorithm.\n */\nfunction fnv1aCore(length: number, codeAt: (i: number) => number): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < length; i++) {\n\t\thash ^= codeAt(i);\n\t\thash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0;\n\t}\n\treturn hash.toString(16).padStart(8, '0');\n}\n\n/**\n * 32-bit FNV-1a— fast, no dependencies. Returns unsigned hex string.\n */\nexport function fnv1a(input: string): string {\n\treturn fnv1aCore(input.length, (i) => input.charCodeAt(i));\n}\n\n/**\n * 32-bit FNV-1a over raw bytes. Returns unsigned hex string.\n */\nexport function fnv1aBytes(bytes: Uint8Array): string {\n\treturn fnv1aCore(bytes.length, (i) => bytes[i]);\n}\n\n/**\n * Hash definition and data tree into a stable cache key.\n *\n * The definition is the *identity* of what we solve, so a binary definition is\n * hashed over its full content (`fnv1aBytes`) — a length-only or sampled key\n * would let two different `.gh` files collide and serve one's cached solve for\n * the other. `.gh` files are small enough that a single linear pass is\n * negligible. (Note this differs from `stableStringify`'s sampled handling of a\n * `Uint8Array` found *inside* the dataTree, where sampling is a deliberate\n * per-solve perf tradeoff.)\n */\nexport function hashSolveInput(definition: string | Uint8Array, dataTree: unknown): string {\n\treturn fnv1a(`${hashDefinition(definition)}|${stableStringify(dataTree)}`);\n}\n\n/**\n * Stable identity of a definition alone (no inputs) — used to key the\n * server-cache-key map so the same definition reuses its `pointer` across solves\n * with different inputs. Same full-content hashing as {@link hashSolveInput}: a\n * binary definition is hashed over all its bytes so two distinct `.gh` files of\n * equal length can't share a cache key.\n */\nexport function hashDefinition(definition: string | Uint8Array): string {\n\treturn typeof definition === 'string'\n\t\t? definition\n\t\t: `u8:${definition.length}:${fnv1aBytes(definition)}`;\n}\n","import { RhinoComputeError, ErrorCodes } from '@/core/errors';\nimport type { RetryPolicy } from '@/core/types';\nimport { getLogger } from '@/core/utils/logger';\n\nimport type { DataTree, GrasshopperComputeResponse, GrasshopperComputeConfig } from '../types';\nimport { hashSolveInput, hashDefinition } from './stable-hash';\n\n/**\n * Scheduling mode — controls how concurrent `solve()` calls interact.\n *\n * - `latest-wins`: One in flight at a time. New calls supersede any pending\n * call (in-flight one is aborted). Optimal for slider scrubs / live UIs.\n * - `queue`: FIFO queue. Each solve runs to completion. Concurrency capped\n * by `maxConcurrent`. Use for \"submit job\" flows where every request matters.\n * - `parallel`: No scheduling — calls run concurrently up to `maxConcurrent`.\n * Closest to plain `client.solve()` but with shared cancel/state.\n */\nexport type SchedulerMode = 'latest-wins' | 'queue' | 'parallel';\n\nexport interface CacheOptions {\n\t/** Maximum entries kept in the LRU. Default: 50. */\n\tmaxEntries?: number;\n\t/** Time-to-live in ms. Set to `0` for no expiry (default). */\n\tttlMs?: number;\n}\n\nexport interface SolveSchedulerOptions {\n\tmode?: SchedulerMode;\n\tmaxConcurrent?: number;\n\ttimeoutMs?: number;\n\tretry?: RetryPolicy;\n\t/** Enable response caching keyed by hash of (definition, dataTree). */\n\tcache?: boolean | CacheOptions;\n\t/**\n\t * Reuse the server's definition cache key so a large (base64/binary)\n\t * definition is uploaded once and subsequent solves reference it by\n\t * `pointer` instead of re-sending the full payload. Hugely cheaper for\n\t * multi-MB definitions on a live UI (slider scrubs, etc.).\n\t *\n\t * Requires a `cacheKeyExecutor` to be supplied (the client wires one). Has no\n\t * effect for URL-pointer definitions (already a reference). On a server-side\n\t * cache miss the executor transparently falls back to a full upload, so this\n\t * is safe to leave on. Default: `true` when a `cacheKeyExecutor` is present.\n\t */\n\treuseServerDefinitionCache?: boolean;\n\t/** Lifecycle hooks — fired in order. Errors thrown by hooks are logged, not rethrown. */\n\tonStart?: (ctx: SolveContext) => void;\n\tonSettle?: (ctx: SolveContext, result: SolveResult) => void;\n\tonSuperseded?: (ctx: SolveContext) => void;\n}\n\nexport interface SolveContext {\n\t/** Stable hash of (definition, dataTree). */\n\tkey: string;\n\t/** Timestamp when scheduler.solve() was called. */\n\tenqueuedAt: number;\n\t/** Timestamp when execution actually started (after queueing). */\n\tstartedAt: number | null;\n}\n\nexport type SolveResult =\n\t| {\n\t\t\tstatus: 'success';\n\t\t\tresponse: GrasshopperComputeResponse;\n\t\t\tdurationMs: number;\n\t\t\tfromCache: boolean;\n\t }\n\t| { status: 'error'; error: RhinoComputeError; durationMs: number }\n\t| { status: 'superseded' };\n\ninterface CacheEntry {\n\tresponse: GrasshopperComputeResponse;\n\tinsertedAt: number;\n}\n\ninterface PendingItem {\n\tdefinition: string | Uint8Array;\n\tdataTree: DataTree[];\n\tctx: SolveContext;\n\tresolve: (response: GrasshopperComputeResponse) => void;\n\treject: (error: RhinoComputeError) => void;\n\texternalSignal?: AbortSignal;\n\t/** Set once the promise has been settled, so a late executor rejection becomes a no-op. */\n\tsettled?: { error: RhinoComputeError } | { ok: true };\n}\n\ninterface InFlightItem extends PendingItem {\n\tcontroller: AbortController;\n}\n\n/**\n * Adapter for the underlying solve function. Lets the scheduler be tested\n * without a real Compute server, and decouples it from the client class.\n */\nexport type SolveExecutor = (\n\tdefinition: string | Uint8Array,\n\tdataTree: DataTree[],\n\tconfig: GrasshopperComputeConfig\n) => Promise<GrasshopperComputeResponse>;\n\n/**\n * Cache-key-aware executor. When `cacheKey` is provided, the executor solves by\n * reference (`pointer: cacheKey`) and falls back to a full upload on a server\n * cache miss. Always reports the (possibly refreshed) `cacheKey` so the\n * scheduler can update its definition→key map, plus whether the fast path\n * `missed` (for telemetry). When `cacheKey` is null it's a first solve — upload\n * fully and capture the key the server assigns.\n *\n * Supplied by the client (which owns the solve primitives); the scheduler stays\n * decoupled from the transport.\n */\nexport type CacheKeyExecutor = (\n\tdefinition: string | Uint8Array,\n\tdataTree: DataTree[],\n\tcacheKey: string | null,\n\tconfig: GrasshopperComputeConfig\n) => Promise<{ response: GrasshopperComputeResponse; cacheKey: string | null; missed: boolean }>;\n\n/**\n * Whether a definition is worth solving by server cache key. Binary and\n * base64/plain-string definitions are uploaded in full, so referencing them by\n * key on later solves saves the (potentially huge) payload. An `http(s)://` URL\n * is already a reference — the server keys it by URL and there's nothing to\n * re-upload — so the fast path adds no value there.\n */\nfunction isReusableDefinition(definition: string | Uint8Array): boolean {\n\tif (definition instanceof Uint8Array) return true;\n\treturn !/^https?:\\/\\//i.test(definition);\n}\n\n/**\n * Robust scheduler for Grasshopper solves.\n *\n * Sits between your application code and the underlying compute call,\n * adding:\n * - Configurable scheduling (latest-wins for sliders, queue for jobs)\n * - In-flight cancellation (per-call signal + cancelAll)\n * - Optional response caching for repeated inputs\n * - Lifecycle hooks for UI indicators (start / settle / superseded)\n * - State observability via subscribe()\n *\n * Multiple schedulers can share a single GrasshopperClient — typically one\n * per UI surface (e.g. one for slider scrubs, one for long-running submits).\n *\n * @example\n * ```ts\n * const scheduler = client.createScheduler({ mode: 'latest-wins', timeoutMs: 30_000 });\n *\n * // From a slider handler:\n * scheduler.solve(definition, tree).then((result) => {\n * updateMeshes(result);\n * }).catch((err) => {\n * if (err.code !== 'SUPERSEDED') showError(err);\n * });\n *\n * // From a UI binding:\n * scheduler.subscribe(() => {\n * showSpinner = scheduler.isSolving;\n * });\n * ```\n */\nexport class SolveScheduler {\n\tprivate readonly executor: SolveExecutor;\n\tprivate readonly baseConfig: GrasshopperComputeConfig;\n\n\tprivate readonly mode: SchedulerMode;\n\tprivate readonly maxConcurrent: number;\n\tprivate readonly timeoutMs: number | undefined;\n\tprivate readonly retry: RetryPolicy | undefined;\n\n\tprivate readonly cacheEnabled: boolean;\n\tprivate readonly cacheMax: number;\n\tprivate readonly cacheTtl: number;\n\tprivate readonly cache = new Map<string, CacheEntry>();\n\n\t/** Optional cache-key-aware executor and whether server-def-cache reuse is on. */\n\tprivate readonly cacheKeyExecutor?: CacheKeyExecutor;\n\tprivate readonly reuseServerDefinitionCache: boolean;\n\t/** definition identity → server cache key (`pointer`) learned from past solves. */\n\tprivate readonly serverCacheKeys = new Map<string, string>();\n\n\tprivate readonly onStart?: SolveSchedulerOptions['onStart'];\n\tprivate readonly onSettle?: SolveSchedulerOptions['onSettle'];\n\tprivate readonly onSuperseded?: SolveSchedulerOptions['onSuperseded'];\n\n\tprivate readonly subscribers = new Set<() => void>();\n\n\tprivate readonly inFlight = new Set<InFlightItem>();\n\tprivate pendingForLatestWins: PendingItem | null = null;\n\tprivate readonly fifoQueue: PendingItem[] = [];\n\n\tprivate _lastResult: GrasshopperComputeResponse | null = null;\n\tprivate _lastError: RhinoComputeError | null = null;\n\tprivate _lastDurationMs: number | null = null;\n\n\tprivate disposed = false;\n\n\tconstructor(\n\t\texecutor: SolveExecutor,\n\t\tbaseConfig: GrasshopperComputeConfig,\n\t\toptions: SolveSchedulerOptions = {},\n\t\tcacheKeyExecutor?: CacheKeyExecutor\n\t) {\n\t\tthis.executor = executor;\n\t\tthis.cacheKeyExecutor = cacheKeyExecutor;\n\t\tthis.baseConfig = baseConfig;\n\t\tthis.mode = options.mode ?? 'latest-wins';\n\t\tthis.maxConcurrent = Math.max(1, options.maxConcurrent ?? (this.mode === 'parallel' ? 4 : 1));\n\t\tthis.timeoutMs = options.timeoutMs;\n\t\tthis.retry = options.retry;\n\n\t\tconst cacheOpt = options.cache;\n\t\tthis.cacheEnabled = cacheOpt !== undefined && cacheOpt !== false;\n\t\tconst cacheConfig = typeof cacheOpt === 'object' ? cacheOpt : {};\n\t\tthis.cacheMax = cacheConfig.maxEntries ?? 50;\n\t\tthis.cacheTtl = cacheConfig.ttlMs ?? 0;\n\n\t\t// On by default when the client wired a cache-key executor — it's a pure\n\t\t// win for reusable definitions and falls back safely on a miss.\n\t\tthis.reuseServerDefinitionCache =\n\t\t\t!!cacheKeyExecutor && (options.reuseServerDefinitionCache ?? true);\n\n\t\tthis.onStart = options.onStart;\n\t\tthis.onSettle = options.onSettle;\n\t\tthis.onSuperseded = options.onSuperseded;\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Public state\n\t// --------------------------------------------------------------------------\n\n\tget isSolving(): boolean {\n\t\treturn this.inFlight.size > 0;\n\t}\n\n\tget hasPending(): boolean {\n\t\treturn this.pendingForLatestWins !== null || this.fifoQueue.length > 0;\n\t}\n\n\tget inFlightCount(): number {\n\t\treturn this.inFlight.size;\n\t}\n\n\tget queueDepth(): number {\n\t\treturn this.fifoQueue.length + (this.pendingForLatestWins ? 1 : 0);\n\t}\n\n\tget lastResult(): GrasshopperComputeResponse | null {\n\t\treturn this._lastResult;\n\t}\n\n\tget lastError(): RhinoComputeError | null {\n\t\treturn this._lastError;\n\t}\n\n\tget lastDurationMs(): number | null {\n\t\treturn this._lastDurationMs;\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Subscribe — minimal observable. Called whenever observable state changes.\n\t// --------------------------------------------------------------------------\n\n\tsubscribe(listener: () => void): () => void {\n\t\tthis.subscribers.add(listener);\n\t\treturn () => this.subscribers.delete(listener);\n\t}\n\n\tprivate notify(): void {\n\t\tfor (const listener of this.subscribers) {\n\t\t\ttry {\n\t\t\t\tlistener();\n\t\t\t} catch (err) {\n\t\t\t\tgetLogger().error('[SolveScheduler] subscriber threw:', err);\n\t\t\t}\n\t\t}\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// solve()\n\t// --------------------------------------------------------------------------\n\n\t/**\n\t * Schedule a solve. Returns a promise that:\n\t * - Resolves with the compute response on success.\n\t * - Rejects with `RhinoComputeError` on failure.\n\t * - Rejects with `code: ErrorCodes.SUPERSEDED` when the call was canceled because\n\t * newer values arrived (latest-wins mode).\n\t * - Rejects with `code: ErrorCodes.ABORTED` when the call was canceled via\n\t * caller-supplied signal or `cancelAll()`.\n\t *\n\t * Caller-supplied `signal` cancels just this call (rejects with `ABORTED`).\n\t */\n\tsolve(\n\t\tdefinition: string | Uint8Array,\n\t\tdataTree: DataTree[],\n\t\toptions?: { signal?: AbortSignal }\n\t): Promise<GrasshopperComputeResponse> {\n\t\tif (this.disposed) {\n\t\t\treturn Promise.reject(\n\t\t\t\tnew RhinoComputeError(\n\t\t\t\t\t'SolveScheduler has been disposed and cannot be used',\n\t\t\t\t\tErrorCodes.INVALID_STATE\n\t\t\t\t)\n\t\t\t);\n\t\t}\n\n\t\tconst key = hashSolveInput(definition, dataTree);\n\t\tconst ctx: SolveContext = {\n\t\t\tkey,\n\t\t\tenqueuedAt: Date.now(),\n\t\t\tstartedAt: null\n\t\t};\n\n\t\t// Cache hit — return synchronously-resolved promise\n\t\tif (this.cacheEnabled) {\n\t\t\tconst cached = this.readCache(key);\n\t\t\tif (cached) {\n\t\t\t\tconst result: SolveResult = {\n\t\t\t\t\tstatus: 'success',\n\t\t\t\t\tresponse: cached,\n\t\t\t\t\tdurationMs: 0,\n\t\t\t\t\tfromCache: true\n\t\t\t\t};\n\t\t\t\tthis._lastResult = cached;\n\t\t\t\tthis._lastError = null;\n\t\t\t\tthis._lastDurationMs = 0;\n\t\t\t\tthis.runHook(this.onStart, ctx);\n\t\t\t\tthis.runHook(this.onSettle, ctx, result);\n\t\t\t\tthis.notify();\n\t\t\t\treturn Promise.resolve(cached);\n\t\t\t}\n\t\t}\n\n\t\treturn new Promise<GrasshopperComputeResponse>((resolve, reject) => {\n\t\t\tconst item: PendingItem = {\n\t\t\t\tdefinition,\n\t\t\t\tdataTree,\n\t\t\t\tctx,\n\t\t\t\tresolve,\n\t\t\t\treject,\n\t\t\t\texternalSignal: options?.signal\n\t\t\t};\n\n\t\t\t// External signal cancellation — reject immediately if already aborted\n\t\t\tif (item.externalSignal?.aborted) {\n\t\t\t\tthis.settleError(item, this.makeAbortError(ctx));\n\t\t\t\treturn;\n\t\t\t}\n\n\t\t\tthis.enqueue(item);\n\t\t});\n\t}\n\n\tprivate enqueue(item: PendingItem): void {\n\t\tswitch (this.mode) {\n\t\t\tcase 'latest-wins': {\n\t\t\t\t// Reject any pending one as superseded\n\t\t\t\tif (this.pendingForLatestWins) {\n\t\t\t\t\tthis.supersede(this.pendingForLatestWins);\n\t\t\t\t\tthis.pendingForLatestWins = null;\n\t\t\t\t}\n\t\t\t\t// Abort any in-flight one as superseded\n\t\t\t\tfor (const inflight of this.inFlight) {\n\t\t\t\t\tthis.supersede(inflight);\n\t\t\t\t\tinflight.controller.abort();\n\t\t\t\t}\n\t\t\t\t// Run immediately if no slot is taken\n\t\t\t\tif (this.inFlight.size === 0) {\n\t\t\t\t\tthis.execute(item);\n\t\t\t\t} else {\n\t\t\t\t\tthis.pendingForLatestWins = item;\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\n\t\t\tcase 'queue':\n\t\t\tcase 'parallel': {\n\t\t\t\t// Same dispatch logic — the modes differ only in `maxConcurrent`'s\n\t\t\t\t// default (1 for queue, 4 for parallel), set in the constructor.\n\t\t\t\tif (this.inFlight.size < this.maxConcurrent) {\n\t\t\t\t\tthis.execute(item);\n\t\t\t\t} else {\n\t\t\t\t\tthis.fifoQueue.push(item);\n\t\t\t\t}\n\t\t\t\tbreak;\n\t\t\t}\n\t\t}\n\t\tthis.notify();\n\t}\n\n\tprivate async execute(item: PendingItem): Promise<void> {\n\t\tconst controller = new AbortController();\n\t\tconst inflight: InFlightItem = { ...item, controller };\n\t\tthis.inFlight.add(inflight);\n\t\titem.ctx.startedAt = Date.now();\n\n\t\tconst externalAbortHandler = () => controller.abort();\n\t\titem.externalSignal?.addEventListener('abort', externalAbortHandler, { once: true });\n\n\t\tthis.runHook(this.onStart, item.ctx);\n\t\tthis.notify();\n\n\t\tconst startTime = performance.now();\n\t\ttry {\n\t\t\tconst config: GrasshopperComputeConfig = {\n\t\t\t\t...this.baseConfig,\n\t\t\t\tsignal: controller.signal,\n\t\t\t\t...(this.timeoutMs !== undefined && { timeoutMs: this.timeoutMs }),\n\t\t\t\t...(this.retry !== undefined && { retry: this.retry })\n\t\t\t};\n\n\t\t\tconst response = await this.runExecutor(item.definition, item.dataTree, config);\n\t\t\tconst durationMs = performance.now() - startTime;\n\n\t\t\tif (this.cacheEnabled) this.writeCache(item.ctx.key, response);\n\n\t\t\t// Already superseded mid-flight — drop the late success silently.\n\t\t\tif (!this.settleSuccess(item, response)) return;\n\n\t\t\tthis._lastResult = response;\n\t\t\tthis._lastError = null;\n\t\t\tthis._lastDurationMs = durationMs;\n\n\t\t\tthis.runHook(this.onSettle, item.ctx, {\n\t\t\t\tstatus: 'success',\n\t\t\t\tresponse,\n\t\t\t\tdurationMs,\n\t\t\t\tfromCache: false\n\t\t\t});\n\t\t} catch (error) {\n\t\t\tconst durationMs = performance.now() - startTime;\n\t\t\t// Resolve the error against the (possibly already-settled) item *before*\n\t\t\t// settling: if this was superseded mid-flight, normalizeExecutionError\n\t\t\t// returns the original cause, and _lastError must reflect it either way.\n\t\t\tconst err = this.normalizeExecutionError(error, inflight);\n\n\t\t\tthis._lastError = err;\n\t\t\tthis._lastDurationMs = durationMs;\n\n\t\t\tif (this.settleError(item, err)) {\n\t\t\t\tthis.runHook(this.onSettle, item.ctx, { status: 'error', error: err, durationMs });\n\t\t\t}\n\t\t} finally {\n\t\t\titem.externalSignal?.removeEventListener('abort', externalAbortHandler);\n\t\t\tthis.inFlight.delete(inflight);\n\t\t\tthis.drainNext();\n\t\t\tthis.notify();\n\t\t}\n\t}\n\n\t/**\n\t * Run the solve, using the server-definition-cache fast path when it's\n\t * enabled and the definition is reusable. Learns/updates the definition's\n\t * server cache key from the result so later solves can reference it.\n\t */\n\tprivate async runExecutor(\n\t\tdefinition: string | Uint8Array,\n\t\tdataTree: DataTree[],\n\t\tconfig: GrasshopperComputeConfig\n\t): Promise<GrasshopperComputeResponse> {\n\t\tif (\n\t\t\t!this.cacheKeyExecutor ||\n\t\t\t!this.reuseServerDefinitionCache ||\n\t\t\t!isReusableDefinition(definition)\n\t\t) {\n\t\t\treturn this.executor(definition, dataTree, config);\n\t\t}\n\n\t\tconst defKey = hashDefinition(definition);\n\t\tconst knownKey = this.serverCacheKeys.get(defKey) ?? null;\n\n\t\tconst result = await this.cacheKeyExecutor(definition, dataTree, knownKey, config);\n\n\t\t// Record the server's (possibly refreshed) key for next time; drop a stale\n\t\t// one if the server stopped returning a key.\n\t\tif (result.cacheKey) this.serverCacheKeys.set(defKey, result.cacheKey);\n\t\telse this.serverCacheKeys.delete(defKey);\n\n\t\treturn result.response;\n\t}\n\n\tprivate drainNext(): void {\n\t\tif (this.disposed) return;\n\n\t\t// latest-wins: promote pending if no in-flight\n\t\tif (this.mode === 'latest-wins') {\n\t\t\tif (this.pendingForLatestWins && this.inFlight.size === 0) {\n\t\t\t\tconst next = this.pendingForLatestWins;\n\t\t\t\tthis.pendingForLatestWins = null;\n\t\t\t\tthis.execute(next);\n\t\t\t}\n\t\t\treturn;\n\t\t}\n\n\t\t// queue / parallel: pull from FIFO until at capacity\n\t\twhile (this.fifoQueue.length > 0 && this.inFlight.size < this.maxConcurrent) {\n\t\t\tconst next = this.fifoQueue.shift()!;\n\t\t\tthis.execute(next);\n\t\t}\n\t}\n\n\tprivate supersede(item: PendingItem): void {\n\t\tconst err = new RhinoComputeError('Superseded by newer solve', ErrorCodes.SUPERSEDED, {\n\t\t\tcontext: { key: item.ctx.key, enqueuedAt: item.ctx.enqueuedAt }\n\t\t});\n\t\tif (this.settleError(item, err)) {\n\t\t\tthis.runHook(this.onSuperseded, item.ctx);\n\t\t}\n\t}\n\n\tprivate makeAbortError(ctx: SolveContext): RhinoComputeError {\n\t\treturn new RhinoComputeError('Request aborted by caller', ErrorCodes.ABORTED, {\n\t\t\tcontext: { key: ctx.key, enqueuedAt: ctx.enqueuedAt }\n\t\t});\n\t}\n\n\t/**\n\t * Settle a pending/in-flight item exactly once with an error.\n\t *\n\t * A solve promise can be settled from four concurrent sources — the executor\n\t * resolving, the executor rejecting, `supersede`, and `cancelAll` — and a JS\n\t * promise silently ignores a second settle. This guard is the single place the\n\t * settle-once invariant lives: it makes the *first* settle win and reports\n\t * whether this call was that winner, so callers fire their own hook only when\n\t * they actually settled. Any new settle path must go through here (or\n\t * {@link settleSuccess}) so the guard can't be forgotten.\n\t *\n\t * @returns `true` if this call settled the item; `false` if it was already settled.\n\t */\n\tprivate settleError(item: PendingItem, err: RhinoComputeError): boolean {\n\t\tif (item.settled) return false;\n\t\titem.settled = { error: err };\n\t\titem.reject(err);\n\t\treturn true;\n\t}\n\n\t/**\n\t * Settle a pending/in-flight item exactly once with a successful response.\n\t * The success counterpart to {@link settleError}; see it for the invariant.\n\t *\n\t * @returns `true` if this call settled the item; `false` if it was already settled.\n\t */\n\tprivate settleSuccess(item: PendingItem, response: GrasshopperComputeResponse): boolean {\n\t\tif (item.settled) return false;\n\t\titem.settled = { ok: true };\n\t\titem.resolve(response);\n\t\treturn true;\n\t}\n\n\tprivate isAbortLikeError(error: unknown): boolean {\n\t\tif (error instanceof Error) {\n\t\t\tif (error.name === 'AbortError') return true;\n\t\t\tif (typeof DOMException !== 'undefined' && error instanceof DOMException) {\n\t\t\t\treturn error.name === 'AbortError';\n\t\t\t}\n\t\t}\n\t\treturn false;\n\t}\n\n\tprivate normalizeExecutionError(error: unknown, item: InFlightItem): RhinoComputeError {\n\t\t// If the item was already settled (e.g. by supersede), return that error so\n\t\t// _lastError reflects the original cause rather than the downstream abort.\n\t\tif (item.settled && 'error' in item.settled) {\n\t\t\treturn item.settled.error;\n\t\t}\n\n\t\tif (error instanceof RhinoComputeError) return error;\n\n\t\tif (this.isAbortLikeError(error)) {\n\t\t\treturn this.makeAbortError(item.ctx);\n\t\t}\n\n\t\treturn new RhinoComputeError(\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\tErrorCodes.UNKNOWN_ERROR,\n\t\t\t{ originalError: error instanceof Error ? error : new Error(String(error)) }\n\t\t);\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Cancellation\n\t// --------------------------------------------------------------------------\n\n\t/** Cancel everything — in-flight and pending. */\n\tcancelAll(): void {\n\t\t// Reject pending\n\t\tif (this.pendingForLatestWins) {\n\t\t\tthis.rejectAsAborted(this.pendingForLatestWins);\n\t\t\tthis.pendingForLatestWins = null;\n\t\t}\n\t\twhile (this.fifoQueue.length > 0) {\n\t\t\tconst item = this.fifoQueue.shift()!;\n\t\t\tthis.rejectAsAborted(item);\n\t\t}\n\t\t// Abort in-flight — their finally blocks will reject their promises\n\t\tfor (const inflight of this.inFlight) {\n\t\t\tconst err = this.makeAbortError(inflight.ctx);\n\t\t\tif (this.settleError(inflight, err)) {\n\t\t\t\tthis.runHook(this.onSettle, inflight.ctx, {\n\t\t\t\t\tstatus: 'error',\n\t\t\t\t\terror: err,\n\t\t\t\t\tdurationMs: inflight.ctx.startedAt ? performance.now() - inflight.ctx.startedAt : 0\n\t\t\t\t});\n\t\t\t}\n\t\t\tinflight.controller.abort();\n\t\t}\n\t\tthis.notify();\n\t}\n\n\tprivate rejectAsAborted(item: PendingItem): void {\n\t\tthis.settleError(item, this.makeAbortError(item.ctx));\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Cache\n\t// --------------------------------------------------------------------------\n\n\tprivate readCache(key: string): GrasshopperComputeResponse | null {\n\t\tif (!this.cacheEnabled) return null;\n\t\tconst entry = this.cache.get(key);\n\t\tif (!entry) return null;\n\t\tif (this.cacheTtl > 0 && Date.now() - entry.insertedAt > this.cacheTtl) {\n\t\t\tthis.cache.delete(key);\n\t\t\treturn null;\n\t\t}\n\t\t// LRU touch\n\t\tthis.cache.delete(key);\n\t\tthis.cache.set(key, entry);\n\t\treturn entry.response;\n\t}\n\n\tprivate writeCache(key: string, response: GrasshopperComputeResponse): void {\n\t\tif (!this.cacheEnabled) return;\n\t\tthis.cache.set(key, { response, insertedAt: Date.now() });\n\t\twhile (this.cache.size > this.cacheMax) {\n\t\t\tconst oldest = this.cache.keys().next().value;\n\t\t\tif (oldest === undefined) break;\n\t\t\tthis.cache.delete(oldest);\n\t\t}\n\t}\n\n\tclearCache(): void {\n\t\tthis.cache.clear();\n\t}\n\n\t// --------------------------------------------------------------------------\n\t// Lifecycle\n\t// --------------------------------------------------------------------------\n\n\tdispose(): void {\n\t\tif (this.disposed) return;\n\t\tthis.disposed = true;\n\t\tthis.cancelAll();\n\t\tthis.subscribers.clear();\n\t\tthis.cache.clear();\n\t}\n\n\tprivate runHook<H extends (...args: any[]) => void>(\n\t\thook: H | undefined,\n\t\t...args: Parameters<H>\n\t): void {\n\t\tif (!hook) return;\n\t\ttry {\n\t\t\thook(...args);\n\t\t} catch (err) {\n\t\t\tgetLogger().error('[SolveScheduler] hook threw:', err);\n\t\t}\n\t}\n}\n","import { ErrorCodes, RhinoComputeError } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\nimport ComputeServerStats from '@/core/server/compute-server-stats';\nimport { validateServerUrl } from '@/core/server/validate-server-url';\nimport { ComputeConfig, RetryPolicy } from '@/core/types';\n\nimport { fetchDefinitionIO, fetchParsedDefinitionIO, solveGrasshopperDefinition } from '..';\nimport { solveByCacheKey, solveGrasshopperDefinitionWithCacheKey } from '../solve';\nimport { GrasshopperComputeConfig, GrasshopperComputeResponse, DataTree } from '../types';\nimport {\n\tSolveScheduler,\n\tSolveSchedulerOptions,\n\tCacheKeyExecutor\n} from '../scheduler/solve-scheduler';\n\n/**\n * Per-call options that override the client's default ComputeConfig values.\n *\n * Use these for per-request control without mutating the client config:\n * - `signal` — cancel a specific solve (e.g. when a slider value is superseded)\n * - `timeoutMs` — extend timeout for a long-running solve, or pass `0` to disable\n * - `retry` — override retry policy for this call only\n */\nexport interface SolveOptions {\n\tsignal?: AbortSignal;\n\ttimeoutMs?: number;\n\tretry?: RetryPolicy;\n}\n\n/**\n * GrasshopperClient provides a simple API for interacting with a Rhino Compute server and grasshopper.\n *\n * @public This is the recommended high-level API for Rhino Compute operations.\n *\n * **Security Warning:**\n * Using this client in a browser environment exposes your server URL and API key to users.\n * For production, use this library server-side or proxy requests through your own backend.\n *\n * @example\n * ```typescript\n * const client = await GrasshopperClient.create({\n * serverUrl: 'http://localhost:6500',\n * apiKey: 'your-api-key'\n * });\n *\n * try {\n * const result = await client.solve(definitionUrl, { x: 1, y: 2 });\n * } finally {\n * await client.dispose(); // Clean up resources\n * }\n * ```\n */\nexport default class GrasshopperClient {\n\tprivate readonly config: GrasshopperComputeConfig;\n\tpublic readonly serverStats: ComputeServerStats;\n\tprivate disposed = false;\n\n\tprivate constructor(config: GrasshopperComputeConfig) {\n\t\tthis.config = this.normalizeComputeConfig(config);\n\t\tthis.serverStats = new ComputeServerStats(this.config.serverUrl, this.config.apiKey);\n\t}\n\n\t/**\n\t * Creates and initializes a GrasshopperClient with server validation.\n\t *\n\t * @throws {RhinoComputeError} with code NETWORK_ERROR if server is offline\n\t * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid\n\t */\n\tstatic async create(config: GrasshopperComputeConfig): Promise<GrasshopperClient> {\n\t\tconst client = new GrasshopperClient(config);\n\n\t\t// Check server is online before returning\n\t\tif (!(await client.serverStats.isServerOnline())) {\n\t\t\tthrow new RhinoComputeError('Rhino Compute server is not online', ErrorCodes.NETWORK_ERROR, {\n\t\t\t\tcontext: { serverUrl: client.config.serverUrl }\n\t\t\t});\n\t\t}\n\n\t\treturn client;\n\t}\n\n\t/**\n\t * Gets the client's configuration.\n\t * Useful for passing to lower-level functions.\n\t */\n\tpublic getConfig(): GrasshopperComputeConfig {\n\t\tthis.ensureNotDisposed();\n\t\treturn { ...this.config };\n\t}\n\n\t/**\n\t * Get input/output parameters of a Grasshopper definition.\n\t */\n\tpublic async getIO(definition: string | Uint8Array) {\n\t\tthis.ensureNotDisposed();\n\t\treturn fetchParsedDefinitionIO(definition, this.config);\n\t}\n\n\tpublic async getRawIO(definition: string | Uint8Array) {\n\t\tthis.ensureNotDisposed();\n\t\treturn fetchDefinitionIO(definition, this.config);\n\t}\n\n\t/**\n\t * Run a compute job with a Grasshopper definition.\n\t *\n\t * @throws {RhinoComputeError} with code INVALID_INPUT if definition is empty\n\t * @throws {RhinoComputeError} with code NETWORK_ERROR if server is offline\n\t * @throws {RhinoComputeError} with code COMPUTATION_ERROR if computation fails\n\t */\n\tpublic async solve(\n\t\tdefinition: string | Uint8Array,\n\t\tdataTree: DataTree[],\n\t\toptions?: SolveOptions\n\t): Promise<GrasshopperComputeResponse> {\n\t\tthis.ensureNotDisposed();\n\n\t\ttry {\n\t\t\t// Validate inputs\n\t\t\tif (typeof definition === 'string' && !definition?.trim()) {\n\t\t\t\tthrow new RhinoComputeError(\n\t\t\t\t\t'Definition URL/content is required',\n\t\t\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t\t\t{\n\t\t\t\t\t\tcontext: { receivedUrl: definition }\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t} else if (definition instanceof Uint8Array && definition.length === 0) {\n\t\t\t\tthrow new RhinoComputeError('Definition content is empty', ErrorCodes.INVALID_INPUT);\n\t\t\t}\n\n\t\t\t// Per-call options override the client's stored config for this request only\n\t\t\tconst effectiveConfig: GrasshopperComputeConfig = {\n\t\t\t\t...this.config,\n\t\t\t\t...(options?.signal !== undefined && { signal: options.signal }),\n\t\t\t\t...(options?.timeoutMs !== undefined && { timeoutMs: options.timeoutMs }),\n\t\t\t\t...(options?.retry !== undefined && { retry: options.retry })\n\t\t\t};\n\n\t\t\t// Skip the redundant pre-flight healthcheck — fetchRhinoCompute already surfaces\n\t\t\t// network failures with a NETWORK_ERROR code, so adding a roundtrip here only\n\t\t\t// doubles latency on every solve.\n\t\t\tconst result = await solveGrasshopperDefinition(dataTree, definition, effectiveConfig);\n\n\t\t\t// Compute may return a partial-success response (HTTP 500 with a body\n\t\t\t// containing both `values` and `errors`/`warnings`). Surface that as a\n\t\t\t// COMPUTATION_ERROR so callers don't silently consume a broken result.\n\t\t\tif (result?.errors && result.errors.length > 0) {\n\t\t\t\tthrow new RhinoComputeError(\n\t\t\t\t\tresult.errors.join('; ') || 'Computation failed',\n\t\t\t\t\tErrorCodes.COMPUTATION_ERROR,\n\t\t\t\t\t{\n\t\t\t\t\t\tcontext: {\n\t\t\t\t\t\t\tdefinition:\n\t\t\t\t\t\t\t\ttypeof definition === 'string' && definition.length < 200\n\t\t\t\t\t\t\t\t\t? definition\n\t\t\t\t\t\t\t\t\t: '...content...',\n\t\t\t\t\t\t\tinputs: dataTree,\n\t\t\t\t\t\t\terrors: result.errors,\n\t\t\t\t\t\t\twarnings: result.warnings\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t);\n\t\t\t}\n\n\t\t\treturn result;\n\t\t} catch (error) {\n\t\t\tif (this.config.debug) {\n\t\t\t\tgetLogger().error('Compute failed:', error);\n\t\t\t}\n\n\t\t\tif (error instanceof RhinoComputeError) {\n\t\t\t\tthrow error;\n\t\t\t}\n\n\t\t\tthrow new RhinoComputeError(\n\t\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t\tErrorCodes.COMPUTATION_ERROR,\n\t\t\t\t{\n\t\t\t\t\tcontext: {\n\t\t\t\t\t\tdefinition:\n\t\t\t\t\t\t\ttypeof definition === 'string' && definition.length < 200\n\t\t\t\t\t\t\t\t? definition\n\t\t\t\t\t\t\t\t: '...content...',\n\t\t\t\t\t\tinputs: dataTree\n\t\t\t\t\t},\n\t\t\t\t\toriginalError: error instanceof Error ? error : new Error(String(error))\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Create a scheduler bound to this client. Use a scheduler for any UI surface\n\t * that fires solves frequently (sliders, live editors) or that needs cancel\n\t * semantics, response caching, or state observability.\n\t *\n\t * Multiple schedulers can be created from a single client — typically one per\n\t * UI surface so their queues stay independent.\n\t *\n\t * @example\n\t * ```ts\n\t * const sliderScheduler = client.createScheduler({ mode: 'latest-wins' });\n\t * const submitScheduler = client.createScheduler({ mode: 'queue', timeoutMs: 0, retry: { attempts: 1 } });\n\t * ```\n\t */\n\tpublic createScheduler(options?: SolveSchedulerOptions): SolveScheduler {\n\t\tthis.ensureNotDisposed();\n\t\tconst executor = (\n\t\t\tdefinition: string | Uint8Array,\n\t\t\tdataTree: DataTree[],\n\t\t\tconfig: GrasshopperComputeConfig\n\t\t) => solveGrasshopperDefinition(dataTree, definition, config);\n\n\t\t// Cache-key-aware executor: solve by `pointer: cacheKey` when known (skips\n\t\t// re-uploading large definitions), capturing/refreshing the key and\n\t\t// falling back to a full upload on a server cache miss.\n\t\tconst cacheKeyExecutor: CacheKeyExecutor = (definition, dataTree, cacheKey, config) =>\n\t\t\tcacheKey === null\n\t\t\t\t? solveGrasshopperDefinitionWithCacheKey(dataTree, definition, config).then((r) => ({\n\t\t\t\t\t\t...r,\n\t\t\t\t\t\tmissed: false\n\t\t\t\t\t}))\n\t\t\t\t: solveByCacheKey(dataTree, cacheKey, definition, config);\n\n\t\treturn new SolveScheduler(executor, this.config, options, cacheKeyExecutor);\n\t}\n\n\t/**\n\t * Disposes of client resources.\n\t * Call this when you're done using the client.\n\t */\n\tpublic async dispose(): Promise<void> {\n\t\tif (this.disposed) return;\n\n\t\tthis.disposed = true;\n\t\tawait this.serverStats.dispose();\n\t}\n\n\t/**\n\t * Ensures the client hasn't been disposed.\n\t */\n\tprivate ensureNotDisposed(): void {\n\t\tif (this.disposed) {\n\t\t\tthrow new RhinoComputeError(\n\t\t\t\t'GrasshopperClient has been disposed and cannot be used',\n\t\t\t\tErrorCodes.INVALID_STATE\n\t\t\t);\n\t\t}\n\t}\n\n\t/**\n\t * Validates and normalizes a compute configuration.\n\t *\n\t * @throws {RhinoComputeError} with code INVALID_CONFIG if configuration is invalid\n\t */\n\tprivate normalizeComputeConfig<T extends ComputeConfig | GrasshopperComputeConfig>(config: T): T {\n\t\treturn {\n\t\t\t...config,\n\t\t\tserverUrl: validateServerUrl(config.serverUrl),\n\t\t\tapiKey: config.apiKey,\n\t\t\tauthToken: config.authToken,\n\t\t\tdebug: config.debug ?? false,\n\t\t\tsuppressBrowserWarning: config.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t} as T;\n\t}\n}\n","import type { RhinoModule } from 'rhino3dm';\nimport { getLogger } from '@/core';\n\n// -----------------------------------------------------------------------------\n// Decoder Types\n// -----------------------------------------------------------------------------\n\ntype RhinoDecoder = (rhino: RhinoModule, data: unknown) => unknown;\n\nconst decoderRegistry = new Map<string, RhinoDecoder>();\n\n// -----------------------------------------------------------------------------\n// Registration\n// -----------------------------------------------------------------------------\n\nexport function registerDecoder(typeName: string, decoder: RhinoDecoder): void {\n\tdecoderRegistry.set(typeName, decoder);\n}\n\nregisterDecoder('Rhino.Geometry.Point3d', (rhino, data) => {\n\tconst d = data as any;\n\tif (!d || typeof d.X !== 'number') return null;\n\treturn new rhino.Point([d.X, d.Y, d.Z]);\n});\n\nregisterDecoder('Rhino.Geometry.Line', (rhino, data) => {\n\tconst d = data as any;\n\tif (!d || !d.From || !d.To) return null;\n\treturn new rhino.Line([d.From.X, d.From.Y, d.From.Z], [d.To.X, d.To.Y, d.To.Z]);\n});\n\n// -----------------------------------------------------------------------------\n// Utility Functions\n// -----------------------------------------------------------------------------\n\nfunction findDecoder(rhinoType: string): RhinoDecoder | undefined {\n\tif (decoderRegistry.has(rhinoType)) return decoderRegistry.get(rhinoType);\n\tfor (const [key, dec] of decoderRegistry) {\n\t\tif (rhinoType.startsWith(key)) return dec;\n\t}\n\treturn undefined;\n}\n\nfunction extractPayload(parsedData: any): any {\n\tif (!parsedData || typeof parsedData !== 'object') return null;\n\treturn (parsedData as any).data ?? (parsedData as any).value ?? null;\n}\n\n// -----------------------------------------------------------------------------\n// Geometry Decoding\n// -----------------------------------------------------------------------------\n\nexport function decodeRhinoGeometry(\n\tparsedData: unknown,\n\trhinoType: string,\n\trhino: RhinoModule\n): unknown {\n\tconst decoder = findDecoder(rhinoType);\n\tif (decoder) {\n\t\ttry {\n\t\t\treturn decoder(rhino, parsedData);\n\t\t} catch (error) {\n\t\t\tgetLogger().warn(`Failed to decode Rhino type ${rhinoType}:`, error);\n\t\t}\n\t}\n\n\t// Fallback using CommonObject.decode\n\ttry {\n\t\tconst payload = extractPayload(parsedData);\n\t\tif (payload) return rhino.CommonObject.decode(payload);\n\t} catch (error) {\n\t\tgetLogger().warn(`Failed to decode ${rhinoType} with CommonObject:`, error);\n\t\treturn { __decodeError: true, type: rhinoType, raw: parsedData };\n\t}\n\n\treturn parsedData;\n}\n\n// -----------------------------------------------------------------------------\n// Object Decoder\n// -----------------------------------------------------------------------------\n\nexport interface DecodeRhinoOptions {\n\tkeys?: string[];\n\tskipKeys?: string[];\n\tdeep?: boolean;\n}\n\nexport function decodeRhinoObject<T extends Record<string, unknown>>(\n\tobj: T,\n\trhino: RhinoModule,\n\toptions: DecodeRhinoOptions = {}\n): T {\n\tconst { keys, skipKeys, deep } = options;\n\tconst out: Record<string, unknown> = { ...obj };\n\n\tconst shouldProcessKey = (k: string) => {\n\t\tif (skipKeys?.includes(k)) return false;\n\t\tif (keys && !keys.includes(k)) return false;\n\t\treturn true;\n\t};\n\n\tfor (const [key, value] of Object.entries(obj)) {\n\t\tif (!shouldProcessKey(key)) continue;\n\t\tif (!value || typeof value !== 'object') continue;\n\n\t\tconst v: any = value;\n\t\tconst maybeType = typeof v.type === 'string' ? v.type : undefined;\n\n\t\tif (maybeType) {\n\t\t\tout[key] = decodeRhinoGeometry(v, maybeType, rhino);\n\t\t\tcontinue;\n\t\t}\n\n\t\tif (deep && typeof v === 'object') {\n\t\t\tout[key] = decodeRhinoObject(v as any, rhino, options);\n\t\t}\n\t}\n\n\treturn out as T;\n}\n","import { FileData } from '@/core/files/types';\nimport { GrasshopperComputeResponse, DataItem } from '../../types';\nimport { decodeRhinoGeometry } from './rhino-decoder';\n\nexport interface ParsedContext {\n\t[key: string]: any;\n}\n\nexport interface GetValuesOptions {\n\tparseValues?: boolean;\n\trhino?: any;\n\t/**\n\t * If true, only include values of type System.String in the result.\n\t * Non-string types are filtered out.\n\t */\n\tstringOnly?: boolean;\n}\n\nexport interface GetValuesResult<T = ParsedContext> {\n\tvalues: T;\n}\n\n// -----------------------------------------------------------------------------\n// Constants\n// -----------------------------------------------------------------------------\n\nconst SYSTEM_TYPES = {\n\tSTRING: 'System.String',\n\tINT: 'System.Int32',\n\tDOUBLE: 'System.Double',\n\tBOOL: 'System.Boolean'\n};\n\nconst RHINO_GEOMETRY_PREFIX = 'Rhino.Geometry.';\n\n// Only relevant is Selva plugin is used\nconst EXCLUDED_TYPES = ['WebDisplay'];\nconst FILE_DATA_TYPE = 'FileData';\n\n// -----------------------------------------------------------------------------\n// Utilities\n// -----------------------------------------------------------------------------\n\n/**\n * Checks if a given type string should be excluded by verifying if it contains\n * any of the substrings defined in the `EXCLUDED_TYPES` list.\n *\n * @param type - The string representation of the type to check.\n * @returns `true` if the type matches any excluded pattern; otherwise, `false`.\n */\nfunction isExcludedType(type: string): boolean {\n\treturn EXCLUDED_TYPES.some((t) => type.includes(t));\n}\n\nfunction tryDecodeJSON(value: string): any {\n\tif (typeof value !== 'string') return value;\n\n\tconst trimmed = value.trim();\n\tconst looksJson = trimmed.startsWith('{') || trimmed.startsWith('[') || trimmed.startsWith('\"');\n\tif (!looksJson) return value;\n\n\ttry {\n\t\tconst first = JSON.parse(trimmed);\n\t\tif (typeof first === 'string') {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(first);\n\t\t\t} catch {\n\t\t\t\treturn first;\n\t\t\t}\n\t\t}\n\t\treturn first;\n\t} catch {\n\t\treturn value;\n\t}\n}\n\nfunction decodeBySystemType(raw: any, type: string, rhino?: any): any {\n\tswitch (type) {\n\t\tcase SYSTEM_TYPES.STRING:\n\t\t\tif (typeof raw !== 'string') return raw;\n\t\t\treturn raw.replace(/^\"(.*)\"$/, '$1');\n\n\t\tcase SYSTEM_TYPES.INT:\n\t\t\treturn Number.parseInt(raw, 10);\n\n\t\tcase SYSTEM_TYPES.DOUBLE:\n\t\t\treturn Number.parseFloat(raw);\n\n\t\tcase SYSTEM_TYPES.BOOL: {\n\t\t\tconst str = String(raw).toLowerCase();\n\t\t\treturn str === 'true';\n\t\t}\n\n\t\tdefault:\n\t\t\tif (rhino && type.startsWith(RHINO_GEOMETRY_PREFIX)) {\n\t\t\t\treturn decodeRhinoGeometry(raw, type, rhino);\n\t\t\t}\n\t\t\treturn raw;\n\t}\n}\n\n// Main extractor — assumes type has already been filtered through isExcludedType\n// at the call site. Returning a sentinel from here would pollute the aggregated\n// arrays in getValues / getValue when multiple branches are mixed.\nfunction extractItemValue(data: any, type: string, parseValues: boolean, rhino?: any): any {\n\tif (typeof data !== 'string') return data;\n\n\tconst raw = parseValues ? tryDecodeJSON(data) : data;\n\treturn decodeBySystemType(raw, type, rhino);\n}\n\n/**\n * Type guard for {@link FileData}. The Compute server emits these as JSON\n * blobs inside `FileData`-typed values; this checks that the parsed shape\n * has every required field before we trust it.\n */\nfunction isFileData(value: unknown): value is FileData {\n\tif (!value || typeof value !== 'object') return false;\n\tconst v = value as Record<string, unknown>;\n\treturn (\n\t\ttypeof v.fileName === 'string' &&\n\t\ttypeof v.fileType === 'string' &&\n\t\t'data' in v &&\n\t\ttypeof v.isBase64Encoded === 'boolean' &&\n\t\ttypeof v.subFolder === 'string'\n\t);\n}\n\n// Traversal helper\n/**\n * Iterates over every data item within a Grasshopper tree structure.\n *\n * @param tree - The Grasshopper tree structure containing branches of items.\n * @param handler - A callback function invoked for each {@link DataItem} found within the tree branches.\n */\nfunction forEachTreeItem(\n\ttree: GrasshopperComputeResponse['values'][0]['InnerTree'],\n\thandler: (item: DataItem) => void\n) {\n\tfor (const list of Object.values(tree)) {\n\t\tif (Array.isArray(list)) {\n\t\t\tfor (const item of list) handler(item);\n\t\t}\n\t}\n}\n\n// -----------------------------------------------------------------------------\n// Public API\n// -----------------------------------------------------------------------------\n\n/**\n * Extracts and processes values from a Grasshopper Compute response object.\n *\n * This function iterates through the internal tree structure of the response parameters,\n * extracts individual data items, and aggregates them into a structured result object.\n * Values can be mapped by their parameter names or unique identifiers.\n *\n * @template T - The type of the resulting parsed context values.\n * @param response - The raw response object received from the Grasshopper Compute service.\n * @param byId - Whether to use the parameter's unique ID as the key (true) or its name (false).\n * @param options - Configuration options for value extraction.\n * @param options.parseValues - Whether to attempt parsing complex data types into JavaScript objects.\n * @param options.rhino - An optional Rhino3dm instance used for geometry decoding.\n * @param options.stringOnly - If true, only items identified as strings will be included in the output.\n * @returns A result object containing the mapped values, where duplicate keys are aggregated into arrays.\n */\nexport function getValues<T = ParsedContext>(\n\tresponse: GrasshopperComputeResponse,\n\tbyId: boolean = false,\n\toptions: GetValuesOptions = {}\n): GetValuesResult<T> {\n\tconst { parseValues = true, rhino, stringOnly = false } = options;\n\tconst result: ParsedContext = {};\n\n\tfor (const param of response.values) {\n\t\tforEachTreeItem(param.InnerTree, (item) => {\n\t\t\t// Skip excluded types (e.g. WebDisplay) entirely — leaving them in\n\t\t\t// would write null into the aggregated result.\n\t\t\tif (isExcludedType(item.type)) return;\n\t\t\t// Skip non-string types if stringOnly is enabled\n\t\t\tif (stringOnly && item.type !== SYSTEM_TYPES.STRING) return;\n\n\t\t\tconst key = byId ? item.id : param.ParamName;\n\t\t\tif (!key) return;\n\n\t\t\tconst value = extractItemValue(item.data, item.type, parseValues, rhino);\n\n\t\t\tif (result[key] === undefined) {\n\t\t\t\tresult[key] = value;\n\t\t\t} else if (Array.isArray(result[key])) {\n\t\t\t\tresult[key].push(value);\n\t\t\t} else {\n\t\t\t\tresult[key] = [result[key], value];\n\t\t\t}\n\t\t});\n\t}\n\n\treturn { values: result as T };\n}\n\n/**\n * Extracts and decodes file data from a Grasshopper Compute response.\n *\n * This function iterates through all parameter values in the compute response,\n * identifies items that match the file data type, and attempts to decode their\n * JSON content into {@link FileData} objects.\n *\n * @param response - The response object received from a Grasshopper Compute request.\n * @returns An array of valid {@link FileData} objects extracted from the response trees.\n */\nexport function extractFileData(response: GrasshopperComputeResponse): FileData[] {\n\tconst output: FileData[] = [];\n\n\tfor (const param of response.values) {\n\t\tforEachTreeItem(param.InnerTree, (item) => {\n\t\t\tif (!item.type.includes(FILE_DATA_TYPE)) return;\n\n\t\t\tconst parsed = tryDecodeJSON(item.data);\n\t\t\tif (isFileData(parsed)) {\n\t\t\t\toutput.push(parsed);\n\t\t\t}\n\t\t});\n\t}\n\n\treturn output;\n}\n\n/**\n * Extracts a value or collection of values from a Grasshopper Compute response based on the provided criteria.\n *\n * This function searches through the `InnerTree` structures of the response values. If searching `byName`,\n * it returns all values (or a single value) within that parameter's tree. If searching `byId`, it specifically\n * targets items matching that unique identifier.\n *\n * @param response - The compute response object containing the results of a Grasshopper definition execution.\n * @param options - Search criteria, either a `{ byName: string }` to match a `ParamName`, or `{ byId: string }` to match a specific item ID.\n * @param parseOptions - Optional configuration for how values are extracted and filtered.\n * @param parseOptions.parseValues - Whether to process raw data into formatted values (defaults to `true`).\n * @param parseOptions.rhino - Optional Rhino/OpenNURBS instance used for geometry decoding.\n * @param parseOptions.stringOnly - If `true`, non-string types will be filtered out (defaults to `false`).\n *\n * @returns\n * - `undefined` if no matching parameter or items are found.\n * - A single extracted value if only one matching item exists.\n * - An array of extracted values if multiple matching items are found.\n */\nexport function getValue(\n\tresponse: GrasshopperComputeResponse,\n\toptions: { byName: string } | { byId: string },\n\tparseOptions: GetValuesOptions = {}\n): any {\n\tconst { parseValues = true, rhino, stringOnly = false } = parseOptions;\n\n\tlet targetParam: GrasshopperComputeResponse['values'][0] | undefined;\n\n\tif ('byName' in options) {\n\t\ttargetParam = response.values.find((p) => p.ParamName === options.byName);\n\t} else {\n\t\ttargetParam = response.values.find((p) => {\n\t\t\tlet found = false;\n\t\t\tforEachTreeItem(p.InnerTree, (item) => {\n\t\t\t\tif (item.id === options.byId) found = true;\n\t\t\t});\n\t\t\treturn found;\n\t\t});\n\t}\n\n\tif (!targetParam) return undefined;\n\n\tconst collected: any[] = [];\n\n\tforEachTreeItem(targetParam.InnerTree, (item) => {\n\t\tif ('byId' in options && item.id !== options.byId) return;\n\t\t// Skip excluded types (e.g. WebDisplay) entirely.\n\t\tif (isExcludedType(item.type)) return;\n\t\t// Skip non-string types if stringOnly is enabled\n\t\tif (stringOnly && item.type !== SYSTEM_TYPES.STRING) return;\n\t\tconst v = extractItemValue(item.data, item.type, parseValues, rhino);\n\t\tcollected.push(v);\n\t});\n\n\tif (collected.length === 0) return undefined;\n\tif (collected.length === 1) return collected[0];\n\treturn collected;\n}\n","import { downloadFileData } from '@/core/files/handle-files';\nimport { FileBaseInfo, FileData } from '@/core/files/types';\nimport type { MeshExtractionOptions } from '@/features/visualization/webdisplay/types';\nimport { RhinoComputeError, ErrorCodes } from '@/core/errors';\n\nimport { GrasshopperComputeResponse } from '../types';\n\nimport {\n\textractFileData,\n\tgetValue,\n\tgetValues,\n\tGetValuesOptions,\n\tGetValuesResult,\n\tParsedContext\n} from '../io/output/response-processors';\n\n/**\n * High-level wrapper for interacting with Grasshopper Compute responses.\n *\n * This class exposes a clean, consistent API for accessing parsed values,\n * geometry, and produced files. It is designed to be the primary interface\n * when working with Grasshopper results in client applications.\n */\nexport default class GrasshopperResponseProcessor {\n\t/**\n\t * Store the compute response for reuse.\n\t */\n\tconstructor(\n\t\tprivate readonly response: GrasshopperComputeResponse,\n\t\tprivate readonly debug: boolean = false\n\t) {}\n\n\t/**\n\t * Extract all values in the response.\n\t *\n\t * @typeParam T - Expected structure of the return value. Defaults to a simple key/value map. (later cast as needed)\n\t * @param byId - If true, keys are parameter IDs; if false, keys are parameter names.\n\t * @param options - Controls parsing behavior such as Rhino geometry decoding.\n\t * @returns Parsed Grasshopper output values.\n\t *\n\t * **Note:** Using `byId` only works with the custom VektorNode rhino.compute branch.\n\t *\n\t * @example\n\t * ```ts\n\t * const processor = new GrasshopperResponseProcessor(response);\n\t * const { values } = processor.getValues();\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * const { values } = processor.getValues(true); // keyed by param ID\n\t * ```\n\t */\n\tpublic getValues<T = ParsedContext>(\n\t\tbyId: boolean = false,\n\t\toptions: GetValuesOptions = {}\n\t): GetValuesResult<T> {\n\t\treturn getValues<T>(this.response, byId, options);\n\t}\n\n\t/**\n\t * Retrieve a specific value by parameter name or ID.\n\t *\n\t * @param selector - `{ byName }` for the human-readable name, `{ byId }` for the parameter GUID.\n\t * @param options - Parsing configuration (e.g. disable parsing or enable Rhino).\n\t * @returns Single parsed value, array of values, or undefined if the parameter is absent.\n\t *\n\t * @example\n\t * ```ts\n\t * const schema = processor.getValue({ byName: 'Schema' });\n\t * const output = processor.getValue({ byId: 'a4be1c1e-23f9-4c27-b942-7f3bb2c45c6f' });\n\t * ```\n\t *\n\t * **Note:** `byId` only works with the custom VektorNode rhino.compute branch.\n\t */\n\tpublic getValue(\n\t\tselector: { byName: string } | { byId: string },\n\t\toptions?: GetValuesOptions\n\t): any {\n\t\treturn getValue(this.response, selector, options);\n\t}\n\n\t/**\n\t * @deprecated Use `getValue({ byName })` instead.\n\t */\n\tpublic getValueByParamName(paramName: string, options?: GetValuesOptions): any {\n\t\treturn getValue(this.response, { byName: paramName }, options);\n\t}\n\n\t/**\n\t * @deprecated Use `getValue({ byId })` instead.\n\t */\n\tpublic getValueByParamId(paramId: string, options?: GetValuesOptions): any {\n\t\treturn getValue(this.response, { byId: paramId }, options);\n\t}\n\n\t/**\n\t * Convert all geometry results into Three.js mesh objects.\n\t *\n\t * This uses internal helpers to decode Rhino geometry into Three.js\n\t * primitives such as meshes and lines, making them ready for rendering.\n\t *\n\t * All processing options (scaling, positioning, compression, etc.) can be customized.\n\t * The processor's debug flag is merged with options - explicit options take precedence.\n\t *\n\t * **Note:** This only works when using the **Selva Display** component in Grasshopper, and requires the custom branch of rhino.compute from VektorNode. This method dynamically imports three.js visualization modules. Ensure three.js is installed as a peer dependency if you use this feature.\n\t *\n\t * @param options - Configuration for mesh extraction and parsing. Overrides processor's debug flag if provided.\n\t * @returns Promise resolving to an array of Three.js mesh objects.\n\t * @throws {RhinoComputeError} If three.js visualization module cannot be loaded.\n\t *\n\t * @example\n\t * ```ts\n\t * const meshes = await processor.extractMeshesFromResponse();\n\t * scene.add(...meshes);\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * const meshes = await processor.extractMeshesFromResponse({\n\t * debug: true,\n\t * allowScaling: true,\n\t * allowAutoPosition: false,\n\t * parsing: {\n\t * mergeByMaterial: false,\n\t * applyTransforms: true,\n\t * debug: true,\n\t * },\n\t * });\n\t * ```\n\t */\n\tpublic async extractMeshesFromResponse(options?: MeshExtractionOptions) {\n\t\tconst mergedOptions: MeshExtractionOptions = {\n\t\t\tdebug: this.debug,\n\t\t\t...options\n\t\t};\n\n\t\t// Dynamically import visualization module to avoid coupling three.js at module load time.\n\t\t// Narrow the try/catch to the import only — errors from mesh extraction itself should\n\t\t// propagate so callers can debug them, not get re-wrapped as \"failed to load\".\n\t\tlet getThreeMeshesFromComputeResponse: typeof import('@/features/visualization').getThreeMeshesFromComputeResponse;\n\t\ttry {\n\t\t\t({ getThreeMeshesFromComputeResponse } = await import('@/features/visualization'));\n\t\t} catch (error) {\n\t\t\tthrow new RhinoComputeError(\n\t\t\t\t'Failed to load three.js visualization module. Ensure three.js is installed as a peer dependency.',\n\t\t\t\tErrorCodes.INVALID_STATE,\n\t\t\t\t{\n\t\t\t\t\tcontext: { originalError: error instanceof Error ? error.message : String(error) }\n\t\t\t\t}\n\t\t\t);\n\t\t}\n\n\t\treturn getThreeMeshesFromComputeResponse(this.response, mergedOptions);\n\t}\n\n\t/**\n\t * Extract internal file data structures from the response.\n\t * This includes Grasshopper-generated textures, JSON exports,\n\t * CAD formats, or any file structure packaged in the response.\n\t *\n\t * **Note:** This only works when using the **Block to File** and **Geometry To File** components from the Selva plugin in Grasshopper, and requires the custom branch of rhino.compute from VektorNode.\n\t *\n\t * @returns Raw file data entries.\n\t */\n\tprivate getFileData(): FileData[] {\n\t\treturn extractFileData(this.response);\n\t}\n\n\t/**\n\t * Download all files generated by Grasshopper, optionally including\n\t * additional user-provided files.\n\t *\n\t * Files are grouped under the specified folder name when downloaded.\n\t *\n\t * @param folderName - Name for the download directory.\n\t * @param additionalFiles - Extra files to package (single file, array, or null).\n\t *\n\t * @example\n\t * ```ts\n\t * processor.getAndDownloadFiles('gh-output');\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * const extra = { name: 'notes.txt', data: 'Example' };\n\t * processor.getAndDownloadFiles('project', extra);\n\t * ```\n\t */\n\tpublic getAndDownloadFiles(\n\t\tfolderName: string,\n\t\tadditionalFiles?: FileBaseInfo[] | FileBaseInfo | null\n\t) {\n\t\tconst files = this.getFileData();\n\t\tdownloadFileData(files, folderName, additionalFiles);\n\t}\n}\n","import { getLogger } from '@/core';\nimport type { InputParamSchema } from '../../types';\n\n/**\n * @internal Shared, type-independent normalization of a raw input's `default`.\n *\n * This is the first step of the input-type parser pipeline: it flattens the\n * raw Grasshopper `innerTree` default into the shape the per-type parsers\n * expect, BEFORE type dispatch. The flat-vs-tree decision depends only on\n * `treeAccess` / `atMost`, never on the param type — which is why it lives here\n * as one shared step rather than inside each parser.\n *\n * Pure: returns a new schema with a normalized `default`; never mutates the\n * input. Replaces the old in-place `preProcessInputDefault`.\n *\n * Behavior (pinned by characterization tests — keep identical):\n * - Non-object / null default → returned unchanged.\n * - Object without `innerTree` → default becomes `null` (and warns).\n * - Empty `innerTree` → default becomes `undefined`.\n * - tree-access (`treeAccess` or `atMost > 1`) → default becomes a\n * `Record<branch, parsed[]>` with per-item type-aware parsing.\n * - otherwise → flatten all branch items: 0 → `undefined`, 1 → the value,\n * N → the array.\n */\nexport function normalizeDefault(input: InputParamSchema): InputParamSchema {\n\tif (typeof input.default !== 'object' || input.default === null) {\n\t\treturn input;\n\t}\n\n\tif (!('innerTree' in input.default)) {\n\t\tgetLogger().warn('Unexpected structure in input.default:', input.default);\n\t\treturn { ...input, default: null };\n\t}\n\n\tconst innerTree = (input.default as any).innerTree;\n\n\t// If innerTree is empty, set default to undefined\n\tif (Object.keys(innerTree).length === 0) {\n\t\treturn { ...input, default: undefined };\n\t}\n\n\t// If treeAccess is true or atMost > 1, preserve the tree structure\n\tif (input.treeAccess || (input.atMost && input.atMost > 1)) {\n\t\t// Convert each branch to an array of parsed data\n\t\tconst tree: Record<string, any[]> = {};\n\t\tfor (const [branch, items] of Object.entries(innerTree)) {\n\t\t\ttree[branch] = (items as any[]).map((item) => {\n\t\t\t\t// Try to parse numbers, booleans, or JSON if possible\n\t\t\t\tif (typeof item.data === 'string') {\n\t\t\t\t\tif (item.type === 'System.Double' || item.type === 'System.Int32') {\n\t\t\t\t\t\tconst num = Number(item.data);\n\t\t\t\t\t\treturn Number.isNaN(num) ? item.data : num;\n\t\t\t\t\t}\n\t\t\t\t\tif (item.type === 'System.Boolean') {\n\t\t\t\t\t\treturn item.data.toLowerCase() === 'true';\n\t\t\t\t\t}\n\t\t\t\t\tif (item.type.startsWith('Rhino.Geometry') || item.type === 'System.String') {\n\t\t\t\t\t\ttry {\n\t\t\t\t\t\t\treturn JSON.parse(item.data);\n\t\t\t\t\t\t} catch {\n\t\t\t\t\t\t\treturn item.data;\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\treturn item.data;\n\t\t\t});\n\t\t}\n\t\treturn { ...input, default: tree };\n\t}\n\n\t// Otherwise, flatten all values as before\n\tconst allValues: any[] = [];\n\tfor (const items of Object.values(innerTree)) {\n\t\tif (Array.isArray(items)) {\n\t\t\titems.forEach((item) => {\n\t\t\t\tif (item && typeof item === 'object' && 'data' in item) {\n\t\t\t\t\tallValues.push(item.data);\n\t\t\t\t}\n\t\t\t});\n\t\t}\n\t}\n\tif (allValues.length === 0) {\n\t\treturn { ...input, default: undefined };\n\t} else if (allValues.length === 1) {\n\t\treturn { ...input, default: allValues[0] };\n\t} else {\n\t\treturn { ...input, default: allValues };\n\t}\n}\n","import type { DataTreeDefault } from '../types';\n\n/**\n * Canonical matcher for a Grasshopper branch path key like `{0}`, `{0;1}`, or\n * the root path `{}`. This is the single source of truth for the branch-path\n * shape (`DataTreePath`); anything testing \"does this string name a branch?\"\n * should use this rather than re-inlining the regex.\n */\nexport const TREE_PATH_RE = /^\\{([\\d;]*)\\}$/;\n\n/**\n * Membership test for a {@link DataTreeDefault}: an object keyed entirely by\n * branch paths, each mapping to an array of values. This is the one predicate\n * both the input-type parsers (to pass a tree-access default through untouched)\n * and `TreeBuilder` (to dispatch it to `fromDataTreeDefault`) ask — so the two\n * agree by construction on exactly which values are trees.\n */\nexport function isDataTreeDefault(value: unknown): value is DataTreeDefault {\n\tif (typeof value !== 'object' || value === null || Array.isArray(value)) return false;\n\tconst entries = Object.entries(value);\n\treturn (\n\t\tentries.length > 0 &&\n\t\tentries.every(([key, val]) => TREE_PATH_RE.test(key) && Array.isArray(val))\n\t);\n}\n","import { RhinoComputeError } from '@/core/errors';\nimport { getLogger } from '@/core';\nimport { isDataTreeDefault } from '../../data-tree/tree-path';\nimport type {\n\tBaseInputType,\n\tBooleanInputType,\n\tColorInputType,\n\tFileInputType,\n\tGeometryInputType,\n\tInputParam,\n\tInputParamSchema,\n\tNumericInputType,\n\tTextInputType,\n\tValueListInputType\n} from '../../types';\n\n/**\n * @internal The input-type parser seam.\n *\n * One adapter per Grasshopper param type. A parser owns EVERYTHING about its\n * type: value coercion, type-specific fields (e.g. numeric step size), the\n * typed-param construction, and its own safe fallback when input is bad. New\n * param types plug in by adding an entry to {@link INPUT_TYPE_PARSERS}.\n *\n * Parsers are pure: they read from a (already-`normalizeDefault`'d) schema and\n * return a typed param. They do not mutate the schema. `parse` throws a\n * {@link RhinoComputeError} on recoverable bad input; the registry boundary\n * catches it and pairs it with `fallback`.\n */\nexport interface InputTypeParser<T extends InputParam = InputParam> {\n\t/** Canonical paramType(s) this parser owns, e.g. ['Number','Integer']. */\n\treadonly types: readonly string[];\n\t/** Schema (with normalized default) → typed param. Throws on bad input. */\n\tparse(schema: InputParamSchema, base: BaseInputType): T;\n\t/** This type's safe fallback param when {@link parse} throws. */\n\tfallback(schema: InputParamSchema, base: BaseInputType): T;\n}\n\n// ============================================================================\n// Value transformers (ported verbatim from the old input-parsers.ts)\n// ============================================================================\n\ntype ValueTransformer<T> = (value: unknown) => T | null;\n\n/**\n * Coerce a schema's `default` through a transformer, mirroring the old\n * `processInputValue`: arrays map+filter (empty → undefined), scalars\n * transform-or-(undefined|preserve). Returns the new default value rather than\n * mutating.\n */\nfunction coerceDefault<T>(\n\tvalue: unknown,\n\ttransform: ValueTransformer<T>,\n\tsetUndefinedOnEmpty: boolean\n): unknown {\n\tif (value === undefined || value === null) {\n\t\treturn value;\n\t}\n\n\tif (Array.isArray(value)) {\n\t\tconst processed = value.map(transform).filter((v): v is T => v !== null);\n\t\treturn processed.length > 0 ? processed : undefined;\n\t}\n\n\tconst transformed = transform(value);\n\tif (transformed !== null) {\n\t\treturn transformed;\n\t}\n\treturn setUndefinedOnEmpty ? undefined : value;\n}\n\nconst numericTransformer: ValueTransformer<number> = (value) => {\n\tif (typeof value === 'number') return value;\n\tif (typeof value === 'string') {\n\t\tconst parsed = Number(value.trim());\n\t\treturn Number.isNaN(parsed) ? null : parsed;\n\t}\n\treturn null;\n};\n\nconst booleanTransformer: ValueTransformer<boolean> = (value) => {\n\tif (typeof value === 'boolean') return value;\n\tif (typeof value === 'string') {\n\t\tconst lower = value.toLowerCase();\n\t\tif (lower === 'true') return true;\n\t\tif (lower === 'false') return false;\n\t\tthrow new Error(`Invalid boolean string: \"${value}\"`);\n\t}\n\treturn null;\n};\n\nconst textTransformer: ValueTransformer<string> = (value) => {\n\tif (typeof value === 'string') {\n\t\tif (value.startsWith('\"') && value.endsWith('\"')) return value.slice(1, -1);\n\t\tif (value.startsWith('\"')) return value.slice(1, -1);\n\t\treturn value;\n\t}\n\treturn null;\n};\n\nconst colorTransformer: ValueTransformer<string> = (value) => {\n\tif (typeof value === 'string') {\n\t\tlet cleaned = value.trim();\n\t\tif (cleaned.startsWith('\"') && cleaned.endsWith('\"')) {\n\t\t\tcleaned = cleaned.slice(1, -1).trim();\n\t\t}\n\t\treturn cleaned;\n\t}\n\treturn null;\n};\n\nfunction objectTransformer(inputName: string): ValueTransformer<object> {\n\treturn (value) => {\n\t\tif (typeof value === 'object' && value !== null) return value;\n\t\tif (typeof value === 'string' && value.trim() !== '') {\n\t\t\ttry {\n\t\t\t\tconst parsed = JSON.parse(value);\n\t\t\t\tif (typeof parsed === 'object' && parsed !== null) return parsed;\n\t\t\t\tgetLogger().warn(`Parsed value for input ${inputName} is not an object`);\n\t\t\t\treturn null;\n\t\t\t} catch (err) {\n\t\t\t\tgetLogger().warn(`Failed to parse object value \"${value}\" for input ${inputName}`, err);\n\t\t\t\treturn null;\n\t\t\t}\n\t\t}\n\t\treturn null;\n\t};\n}\n\n// ============================================================================\n// Numeric step-size + precision (ported verbatim)\n// ============================================================================\n\nfunction applyRounding(value: number, decimalPlaces: number, tolerance: number): number {\n\tconst rounded = Number(value.toFixed(decimalPlaces));\n\tif (Math.abs(value - rounded) < tolerance) return rounded;\n\treturn value;\n}\n\nfunction getInputStepSize(value: number, roundingTolerance: number): number {\n\tif (!Number.isFinite(value)) return 0.1;\n\tif (value === 0) return 0.1;\n\n\tconst abs = Math.abs(value);\n\n\tif (abs >= 1) {\n\t\tconst str = String(value);\n\t\tconst decimalPart = str.split('.')[1];\n\t\tif (decimalPart && decimalPart.length > 0) {\n\t\t\tconst decimals = Math.min(decimalPart.length, 12);\n\t\t\tconst step = Math.pow(10, -decimals);\n\t\t\tconst rounded = Number(step.toFixed(decimals));\n\t\t\treturn Math.abs(rounded - step) < roundingTolerance ? rounded : step;\n\t\t}\n\t\treturn 1;\n\t}\n\n\t// Handle exponential notation\n\tconst s = String(value);\n\tconst expMatch = s.toLowerCase().match(/e(-?\\d+)/);\n\tif (expMatch) {\n\t\tconst exp = Number(expMatch[1]);\n\t\tif (exp < 0 || s.toLowerCase().includes('e-')) {\n\t\t\tconst absExp = Math.abs(exp);\n\t\t\tconst step = Math.pow(10, -absExp);\n\t\t\tconst rounded = Number(step.toFixed(absExp));\n\t\t\treturn Math.abs(rounded - step) < roundingTolerance ? rounded : step;\n\t\t}\n\t\treturn 0.1;\n\t}\n\n\t// Handle standard decimal notation\n\tconst MAX_DECIMALS = 12;\n\tconst fixed = abs.toFixed(MAX_DECIMALS);\n\tconst trimmed = fixed.replace(/0+$/, '');\n\tconst decimals = Math.min((trimmed.split('.')[1] || '').length, MAX_DECIMALS);\n\n\tif (decimals === 0) return 0.1;\n\n\tconst step = Math.pow(10, -decimals);\n\tconst rounded = Number(step.toFixed(decimals));\n\treturn Math.abs(rounded - step) < roundingTolerance ? rounded : step;\n}\n\n/**\n * Computes the coerced default + stepSize for a Number/Integer input.\n * Mirrors the old `processNumericInput` exactly.\n */\nfunction computeNumeric(\n\tschema: InputParamSchema,\n\troundingTolerance = 1e-8\n): { default: NumericInputType['default']; stepSize: number } {\n\tconst isIntegerType = schema.paramType === 'Integer';\n\n\t// A tree-access default is a DataTreeDefault keyed by branch paths; pass it\n\t// through untouched (numeric constraints are applied later by TreeBuilder).\n\t// Without this guard the scalar numericTransformer mangles the tree object to\n\t// `undefined`, silently dropping a tree-access slider's default. Sharing\n\t// `isDataTreeDefault` with TreeBuilder guarantees we pass through exactly the\n\t// values it will treat as trees — no looser, no stricter.\n\tif (isDataTreeDefault(schema.default)) {\n\t\treturn {\n\t\t\tdefault: schema.default as NumericInputType['default'],\n\t\t\tstepSize: isIntegerType ? 1 : 0.1\n\t\t};\n\t}\n\n\tlet value = coerceDefault(schema.default, numericTransformer, true);\n\n\tif (isIntegerType) {\n\t\tif (Array.isArray(value)) {\n\t\t\tvalue = value.map((val) => (typeof val === 'number' ? Math.round(val) : val));\n\t\t} else if (typeof value === 'number') {\n\t\t\tvalue = Math.round(value);\n\t\t}\n\t\treturn { default: value as NumericInputType['default'], stepSize: 1 };\n\t}\n\n\tconst firstValue = Array.isArray(value) ? value[0] : value;\n\n\tlet stepSource: number | undefined;\n\tif (typeof firstValue === 'number' && Number.isFinite(firstValue) && firstValue !== 0) {\n\t\tstepSource = firstValue;\n\t} else if (\n\t\ttypeof schema.minimum === 'number' &&\n\t\tNumber.isFinite(schema.minimum) &&\n\t\tschema.minimum !== 0\n\t) {\n\t\tstepSource = schema.minimum;\n\t} else if (\n\t\ttypeof schema.maximum === 'number' &&\n\t\tNumber.isFinite(schema.maximum) &&\n\t\tschema.maximum !== 0\n\t) {\n\t\tstepSource = schema.maximum;\n\t}\n\n\tconst stepSize = stepSource !== undefined ? getInputStepSize(stepSource, roundingTolerance) : 0.1;\n\n\t// Apply precision to all numeric values\n\tlet decimalPlaces = 0;\n\tconst stepStr = String(stepSize);\n\tconst expMatch = stepStr.toLowerCase().match(/e(-?\\d+)/);\n\tif (expMatch) {\n\t\tdecimalPlaces = Math.abs(Number(expMatch[1]));\n\t} else {\n\t\tdecimalPlaces = stepStr.split('.')[1]?.length ?? 0;\n\t}\n\n\tif (\n\t\tdecimalPlaces === 0 &&\n\t\ttypeof firstValue === 'number' &&\n\t\tfirstValue !== 0 &&\n\t\tMath.abs(firstValue) < 1\n\t) {\n\t\tconst inferred = Math.ceil(-Math.log10(Math.abs(firstValue)));\n\t\tif (Number.isFinite(inferred) && inferred > 0) {\n\t\t\tdecimalPlaces = inferred;\n\t\t}\n\t}\n\n\tdecimalPlaces = Math.min(Math.max(decimalPlaces, 0), 12);\n\n\tif (Array.isArray(value)) {\n\t\tvalue = value.map((val) =>\n\t\t\ttypeof val === 'number' ? applyRounding(val, decimalPlaces, roundingTolerance) : val\n\t\t);\n\t} else if (typeof value === 'number') {\n\t\tvalue = applyRounding(value, decimalPlaces, roundingTolerance);\n\t}\n\n\treturn { default: value as NumericInputType['default'], stepSize };\n}\n\n// ============================================================================\n// Parsers — one per type\n// ============================================================================\n\nconst numericParser: InputTypeParser<NumericInputType> = {\n\ttypes: ['Number', 'Integer'],\n\tparse(schema, base) {\n\t\tconst { default: def, stepSize } = computeNumeric(schema);\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: schema.paramType as 'Number' | 'Integer',\n\t\t\tminimum: schema.minimum,\n\t\t\tmaximum: schema.maximum,\n\t\t\tatLeast: schema.atLeast,\n\t\t\tatMost: schema.atMost,\n\t\t\tstepSize,\n\t\t\tdefault: def\n\t\t};\n\t},\n\tfallback(schema, base) {\n\t\tconst isList = (schema.atMost ?? 1) > 1;\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: schema.paramType as 'Number' | 'Integer',\n\t\t\tminimum: schema.minimum,\n\t\t\tmaximum: schema.maximum,\n\t\t\tatLeast: schema.atLeast,\n\t\t\tatMost: schema.atMost,\n\t\t\tdefault: isList ? [0] : 0\n\t\t};\n\t}\n};\n\nconst booleanParser: InputTypeParser<BooleanInputType> = {\n\ttypes: ['Boolean'],\n\tparse(schema, base) {\n\t\tlet value: unknown;\n\t\ttry {\n\t\t\tvalue = coerceDefault(schema.default, booleanTransformer, false);\n\t\t} catch (error) {\n\t\t\t// Mirror old processBooleanInput: re-throw as RhinoComputeError.\n\t\t\tif (error instanceof Error) throw new RhinoComputeError(error.message);\n\t\t\tthrow error;\n\t\t}\n\t\treturn { ...base, paramType: 'Boolean', default: value as BooleanInputType['default'] };\n\t},\n\tfallback(schema, base) {\n\t\tconst isList = (schema.atMost ?? 1) > 1;\n\t\treturn { ...base, paramType: 'Boolean', default: isList ? [false] : false };\n\t}\n};\n\nconst textParser: InputTypeParser<TextInputType> = {\n\ttypes: ['Text'],\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(schema.default, textTransformer, false);\n\t\treturn { ...base, paramType: 'Text', default: value as TextInputType['default'] };\n\t},\n\tfallback(schema, base) {\n\t\tconst isList = (schema.atMost ?? 1) > 1;\n\t\treturn { ...base, paramType: 'Text', default: isList ? [''] : '' };\n\t}\n};\n\nconst valueListParser: InputTypeParser<ValueListInputType> = {\n\ttypes: ['ValueList'],\n\tparse(schema, base) {\n\t\tif (\n\t\t\t!schema.values ||\n\t\t\ttypeof schema.values !== 'object' ||\n\t\t\tObject.keys(schema.values).length === 0\n\t\t) {\n\t\t\tthrow RhinoComputeError.missingValues(schema.nickname || 'unnamed', 'ValueList');\n\t\t}\n\n\t\t// Out-of-range default only warns — it still succeeds (pinned behavior).\n\t\tif (schema.default !== undefined && schema.default !== null) {\n\t\t\tconst defaultLower = String(schema.default).toLowerCase();\n\t\t\tconst exists = Object.keys(schema.values).some((key) => key.toLowerCase() === defaultLower);\n\t\t\tif (!exists) {\n\t\t\t\tgetLogger().warn(\n\t\t\t\t\t`ValueList input \"${schema.nickname || 'unnamed'}\" default value \"${schema.default}\" is not in available values`\n\t\t\t\t);\n\t\t\t}\n\t\t}\n\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'ValueList',\n\t\t\tvalues: schema.values as Record<string, string>,\n\t\t\tdefault: schema.default as string | undefined\n\t\t};\n\t},\n\tfallback(schema, base) {\n\t\tconst isList = (schema.atMost ?? 1) > 1;\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'ValueList',\n\t\t\tvalues: schema.values ?? {},\n\t\t\tdefault: isList ? ([schema.default] as any) : schema.default\n\t\t};\n\t}\n};\n\nconst geometryParser: InputTypeParser<GeometryInputType> = {\n\ttypes: ['Geometry'],\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(\n\t\t\tschema.default,\n\t\t\tobjectTransformer(schema.nickname || 'unnamed'),\n\t\t\ttrue\n\t\t);\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'Geometry',\n\t\t\tdefault: value as GeometryInputType['default']\n\t\t};\n\t},\n\tfallback(schema, base) {\n\t\tconst isList = (schema.atMost ?? 1) > 1;\n\t\treturn { ...base, paramType: 'Geometry', default: isList ? [null] : (null as any) };\n\t}\n};\n\nconst fileParser: InputTypeParser<FileInputType> = {\n\ttypes: ['File'],\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(\n\t\t\tschema.default,\n\t\t\tobjectTransformer(schema.nickname || 'unnamed'),\n\t\t\ttrue\n\t\t);\n\t\treturn {\n\t\t\t...base,\n\t\t\tparamType: 'File',\n\t\t\tacceptedFormats: schema.acceptedFormats,\n\t\t\tdefault: value as FileInputType['default']\n\t\t};\n\t},\n\tfallback(schema, base) {\n\t\tconst isList = (schema.atMost ?? 1) > 1;\n\t\treturn { ...base, paramType: 'File', default: isList ? [null] : (null as any) };\n\t}\n};\n\nconst colorParser: InputTypeParser<ColorInputType> = {\n\ttypes: ['Color'],\n\tparse(schema, base) {\n\t\tconst value = coerceDefault(schema.default, colorTransformer, false);\n\t\treturn { ...base, paramType: 'Color', default: value as ColorInputType['default'] };\n\t},\n\tfallback(schema, base) {\n\t\tconst isList = (schema.atMost ?? 1) > 1;\n\t\treturn { ...base, paramType: 'Color', default: isList ? ['0, 0, 0'] : '0, 0, 0' };\n\t}\n};\n\n// ============================================================================\n// Registry\n// ============================================================================\n\nconst ALL_PARSERS: InputTypeParser[] = [\n\tnumericParser,\n\tbooleanParser,\n\ttextParser,\n\tvalueListParser,\n\tgeometryParser,\n\tfileParser,\n\tcolorParser\n];\n\n/** Registry keyed by canonical paramType. */\nexport const INPUT_TYPE_PARSERS: ReadonlyMap<string, InputTypeParser> = new Map(\n\tALL_PARSERS.flatMap((parser) => parser.types.map((type) => [type, parser] as const))\n);\n\n/**\n * The Geometry parser is the registry's fallback for an unknown paramType,\n * matching the old `createSafeDefault` default branch (geometry-shaped null).\n */\nexport const UNKNOWN_TYPE_FALLBACK: InputTypeParser = geometryParser;\n","import { RhinoComputeError } from '@/core/errors';\nimport { getLogger } from '@/core/utils/logger';\n\nimport { normalizeDefault } from './normalize-default';\nimport { INPUT_TYPE_PARSERS, UNKNOWN_TYPE_FALLBACK } from './input-type-parsers';\n\nimport type { BaseInputType, InputParam, InputParamSchema, InputParseError } from '../../types';\n\n/** Canonical paramType for each supported type, keyed by its lowercased form. */\nconst CANONICAL_PARAM_TYPES = new Map(\n\t[...INPUT_TYPE_PARSERS.keys()].map((key) => [key.toLowerCase(), key])\n);\n\n/**\n * Returns the canonical casing for a paramType (e.g. \"valuelist\" → \"ValueList\"),\n * or the original value unchanged when it isn't a known type so the\n * unknown-paramType error still surfaces downstream.\n */\nfunction canonicalizeParamType(paramType: string): string {\n\treturn CANONICAL_PARAM_TYPES.get(paramType?.toLowerCase()) ?? paramType;\n}\n\n/**\n * Processes a raw input parameter schema and converts it into a typed InputParam object.\n *\n * @internal This is an internal processor. Use `fetchParsedDefinitionIO()` to get processed inputs instead.\n *\n * This function handles the transformation of raw input parameter data from Grasshopper into\n * a structured, type-safe format. It performs validation, type-specific processing, and error\n * handling for various parameter types including numeric, boolean, text, geometry, point, and line inputs.\n *\n * @param rawInput - The raw input parameter schema to process\n * @returns A fully processed and typed InputParam object with appropriate type-specific properties\n *\n * @throws {RhinoComputeError} When an unknown paramType is encountered\n * @throws {Error} Re-throws any non-RhinoComputeError exceptions\n *\n * @remarks\n * The function performs the following operations:\n * - Extracts base properties common to all input types\n * - Preprocesses the raw input data\n * - Applies type-specific validation and transformation\n * - Handles errors gracefully by creating safe default values for validation errors\n *\n * Supported parameter types:\n * - `Number` and `Integer`: Numeric inputs with optional min/max constraints\n * - `Boolean`: Boolean flag inputs\n * - `Text`: String inputs\n * - `Geometry`: Generic geometry objects\n * - `Point`: 3D point objects\n * - `Line`: Line objects\n *\n * @example\n * ```typescript\n * const rawInput = {\n * name: 'Length',\n * paramType: 'Number',\n * minimum: 0,\n * maximum: 100,\n * default: 50\n * };\n * const processedInput = processInput(rawInput);\n * ```\n */\nexport function processInput(rawInput: InputParamSchema): InputParam {\n\treturn processInputWithError(rawInput).input;\n}\n\n/**\n * Like {@link processInput}, but reports validation failures back to the caller\n * instead of swallowing them with a logger warning.\n *\n * On success: `{ input, error: undefined }`.\n * On a recoverable validation failure: `{ input: <safe default>, error: {...} }`.\n *\n * Unexpected (non-RhinoComputeError) failures still throw — they indicate a\n * programming bug, not bad user input.\n *\n * @internal Used by {@link processInputsWithErrors} / {@link fetchParsedDefinitionIO}.\n */\nexport function processInputWithError(rawInput: InputParamSchema): {\n\tinput: InputParam;\n\terror?: InputParseError;\n} {\n\tconst baseInput: BaseInputType = {\n\t\tdescription: rawInput.description,\n\t\tname: rawInput.name,\n\t\tnickname: rawInput.nickname,\n\t\ttreeAccess: rawInput.treeAccess,\n\t\tgroupName: rawInput.groupName ?? '',\n\t\tid: rawInput.id\n\t};\n\n\t// Normalize paramType to its canonical casing so callers can send any case\n\t// (e.g. Selva schemas emit lowercase \"valueList\" while the plugin reports\n\t// \"ValueList\"). The registry is keyed by canonical type.\n\tconst paramType = canonicalizeParamType(rawInput.paramType);\n\n\t// Shared, type-independent step: flatten the raw innerTree default into the\n\t// shape the per-type parsers expect (pure — does not mutate rawInput).\n\tconst schema = normalizeDefault({ ...rawInput, paramType });\n\tconst parser = INPUT_TYPE_PARSERS.get(paramType);\n\n\ttry {\n\t\tif (!parser) {\n\t\t\tthrow RhinoComputeError.unknownParamType(paramType, rawInput.name);\n\t\t}\n\t\treturn { input: parser.parse(schema, baseInput) };\n\t} catch (error) {\n\t\tif (error instanceof RhinoComputeError) {\n\t\t\tgetLogger().error(`Validation error for input ${rawInput.name || 'unknown'}:`, error.message);\n\t\t\t// The parser owns its own fallback; an unknown type falls back to the\n\t\t\t// geometry-shaped safe default (matching the old behavior).\n\t\t\treturn {\n\t\t\t\tinput: (parser ?? UNKNOWN_TYPE_FALLBACK).fallback(schema, baseInput),\n\t\t\t\terror: {\n\t\t\t\t\tinputName: rawInput.name || 'unknown',\n\t\t\t\t\tparamType,\n\t\t\t\t\tmessage: error.message,\n\t\t\t\t\tcode: error.code\n\t\t\t\t}\n\t\t\t};\n\t\t}\n\n\t\t// Unexpected failure — surface it.\n\t\tthrow new RhinoComputeError(\n\t\t\terror instanceof Error ? error.message : String(error),\n\t\t\t'VALIDATION_ERROR',\n\t\t\t{\n\t\t\t\tcontext: { paramName: rawInput.name, paramType },\n\t\t\t\toriginalError: error instanceof Error ? error : new Error(String(error))\n\t\t\t}\n\t\t);\n\t}\n}\n\n/**\n * Processes raw Grasshopper input schemas into strongly-typed TypeScript interfaces.\n *\n * @internal This is an internal batch processor. Use `fetchParsedDefinitionIO()` to get processed inputs instead.\n *\n * Transforms each raw input parameter by:\n * - Normalizing default values (flattening data trees, parsing primitives)\n * - Applying type-specific parsing (Number, Text, Boolean, Geometry, etc.)\n * - Validating constraints (min/max, required fields)\n * - Converting to discriminated union types for type safety\n *\n * @param rawInputs - Array of raw input schemas from Rhino Compute API\n * @returns Array of processed, strongly-typed input parameters\n *\n * @remarks\n * - Empty data trees are converted to `undefined`\n * - Single values are extracted from arrays when appropriate\n * - Tree structures are preserved for list/tree access parameters\n * - Invalid inputs fall back to safe defaults with console warnings\n *\n * @example\n * ```typescript\n * const rawInputs = [\n * { paramType: 'Number', name: 'radius', minimum: 0, default: 10 },\n * { paramType: 'Text', name: 'label', default: 'Hello' }\n * ];\n *\n * const processed = processInputs(rawInputs);\n * // Result: [\n * // { paramType: 'Number', name: 'radius', minimum: 0, default: 10, ... },\n * // { paramType: 'Text', name: 'label', default: 'Hello', ... }\n * // ]\n *\n * // Now type-safe:\n * if (processed[0].paramType === 'Number') {\n * console.log(processed[0].minimum); // TypeScript knows this exists\n * }\n * ```\n *\n * @see {@link processInput} for individual input processing logic\n */\nexport function processInputs(rawInputs: InputParamSchema[]): InputParam[] {\n\treturn processInputsWithErrors(rawInputs).inputs;\n}\n\n/**\n * Like {@link processInputs}, but additionally returns a list of inputs that\n * failed validation and were filled with a safe default.\n *\n * @internal Used by {@link fetchParsedDefinitionIO}.\n */\nexport function processInputsWithErrors(rawInputs: InputParamSchema[]): {\n\tinputs: InputParam[];\n\tparseErrors: InputParseError[];\n} {\n\tconst inputs: InputParam[] = [];\n\tconst parseErrors: InputParseError[] = [];\n\tfor (const raw of rawInputs) {\n\t\tconst { input, error } = processInputWithError(raw);\n\t\tinputs.push(input);\n\t\tif (error) parseErrors.push(error);\n\t}\n\treturn { inputs, parseErrors };\n}\n","import { ComputeConfig, RhinoComputeError, ErrorCodes } from '@/core';\nimport { fetchRhinoCompute } from '@/core/compute-fetch/compute-fetch';\nimport { warnIfClientSide } from '@/core/utils/warnings';\nimport { prepareGrasshopperArgs } from '../solve';\n\nimport { GrasshopperParsedIO, GrasshopperParsedIORaw, IoResponseSchema } from '../types';\n\nimport { processInputsWithErrors } from './input/input-processors';\n\n/**\n * Fetches raw input/output schemas from a Grasshopper definition.\n * Returns unprocessed data exactly as received from the Rhino Compute API (camelCased).\n *\n * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)\n * @param config - Compute configuration (server URL, API key, etc.)\n * @returns Raw inputs and outputs with no type processing\n * @throws {RhinoComputeError} If fetch fails or response is invalid\n *\n * @public Use `fetchParsedDefinitionIO()` for processed, type-safe inputs\n */\nexport async function fetchDefinitionIO(\n\tdefinition: string | Uint8Array,\n\tconfig: ComputeConfig\n): Promise<GrasshopperParsedIORaw> {\n\tconst args = prepareGrasshopperArgs(definition, []);\n\tconst payload: { algo?: string | null; pointer?: string | null } = {};\n\tif (args.algo) payload.algo = args.algo;\n\tif (args.pointer) payload.pointer = args.pointer;\n\n\tif (!payload.algo && !payload.pointer) {\n\t\tthrow new RhinoComputeError(\n\t\t\t'Definition must resolve to either a URL pointer or base64 algo',\n\t\t\tErrorCodes.INVALID_INPUT,\n\t\t\t{ context: { definition } }\n\t\t);\n\t}\n\n\tconst response = await fetchRhinoCompute<IoResponseSchema>('io', payload, config);\n\n\tif (!response || typeof response !== 'object') {\n\t\tthrow new RhinoComputeError('Invalid IO response structure', ErrorCodes.INVALID_INPUT, {\n\t\t\tcontext: { response, definition }\n\t\t});\n\t}\n\n\t// The Compute8 server fork already serializes the IO schema in camelCase\n\t// (`[JsonProperty(\"paramType\")]` etc.) — pinned by the seam snapshot in\n\t// tests/contract/server-contract.test.ts. So we read the fields straight\n\t// through. A previous deep `camelcaseKeys` here was not only redundant but\n\t// corrupted value-list `values` keys — user-authored dropdown labels like\n\t// \"Option A\" were mangled to \"optionA\" (regression-pinned in\n\t// definition-io.casing.test.ts).\n\t//\n\t// The server also reports definition-LOAD diagnostics on the IO response\n\t// (`errors`/`warnings` — e.g. a missing plugin that left inputs unresolved).\n\t// Surface them so a degraded input list comes with an explanation instead of\n\t// silently looking empty. Only attach when non-empty to keep the common\n\t// happy-path result clean.\n\tconst loadWarnings = nonEmptyStrings(response.warnings);\n\tconst loadErrors = nonEmptyStrings(response.errors);\n\n\treturn {\n\t\tinputs: response.inputs,\n\t\toutputs: response.outputs,\n\t\t...(loadWarnings && { loadWarnings }),\n\t\t...(loadErrors && { loadErrors })\n\t};\n}\n\n/**\n * Coerce a server `errors`/`warnings` array (typed `any[]`) into a clean\n * `string[]`, or `undefined` when there's nothing to report. Filters non-string\n * and blank entries defensively.\n */\nfunction nonEmptyStrings(value: unknown): string[] | undefined {\n\tif (!Array.isArray(value)) return undefined;\n\tconst cleaned = value.filter((v): v is string => typeof v === 'string' && v.trim().length > 0);\n\treturn cleaned.length > 0 ? cleaned : undefined;\n}\n\n/**\n * Fetches and processes input/output schemas from a Grasshopper definition.\n * Returns strongly-typed, validated input parameters ready for use.\n *\n * @public This is the recommended way to fetch definition I/O schemas.\n *\n * @param definition - The Grasshopper definition (URL, base64 string, or Uint8Array)\n * @param config - Compute configuration (server URL, API key, etc.)\n * @returns Processed inputs with discriminated union types and outputs\n * @throws {RhinoComputeError} If fetch fails or response is invalid\n *\n * @example\n * ```typescript\n * const { inputs, outputs } = await fetchParsedDefinitionIO(\n * 'https://example.com/definition.gh',\n * { serverUrl: 'https://compute.rhino3d.com', apiKey: 'YOUR_KEY' }\n * );\n *\n * // Inputs are now strongly typed\n * inputs.forEach(input => {\n * if (input.paramType === 'Number') {\n * console.log(input.minimum, input.maximum); // TypeScript knows these exist\n * }\n * });\n * ```\n */\nexport async function fetchParsedDefinitionIO(\n\tdefinition: string | Uint8Array,\n\tconfig: ComputeConfig\n): Promise<GrasshopperParsedIO> {\n\twarnIfClientSide(\n\t\t'fetchParsedDefinitionIO',\n\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t);\n\n\tconst {\n\t\tinputs: rawInputs,\n\t\toutputs,\n\t\tloadWarnings,\n\t\tloadErrors\n\t} = await fetchDefinitionIO(definition, config);\n\tconst { inputs, parseErrors } = processInputsWithErrors(rawInputs);\n\n\treturn {\n\t\tinputs,\n\t\toutputs,\n\t\t...(parseErrors.length > 0 && { parseErrors }),\n\t\t...(loadWarnings && { loadWarnings }),\n\t\t...(loadErrors && { loadErrors })\n\t};\n}\n","import { DataTreeDefault, DataTreePath, InputParam, DataTree } from '../types';\nimport { getLogger } from '@/core';\nimport { isDataTreeDefault, TREE_PATH_RE } from './tree-path';\n\n/**\n * Value types that can be stored in a DataTree\n */\nexport type DataTreeValue = string | number | boolean | object | null;\n\n/**\n * Simple data item for compute requests (not to be confused with DataItem interface for responses).\n * Note: While TypeScript defines this as string, Rhino Compute accepts boolean/number primitives in JSON.\n */\ninterface ComputeDataItem {\n\tdata: string | boolean | number;\n}\n\n/**\n * InnerTree data structure for compute requests.\n */\ntype ComputeInnerTreeData = {\n\t[path in DataTreePath]: ComputeDataItem[];\n};\n\n/**\n * Standalone TreeBuilder class for constructing Grasshopper TreeBuilder structures.\n * Does not depend on RhinoCompute library.\n *\n * @example\n * ```ts\n * const tree = new TreeBuilder('MyParam')\n * .append([0], [1, 2, 3])\n * .append([1], [4, 5])\n * .toComputeFormat();\n * ```\n */\nexport class TreeBuilder {\n\tprivate innerTree: ComputeInnerTreeData;\n\tprivate paramName: string;\n\n\tconstructor(paramName: string) {\n\t\tthis.paramName = paramName;\n\t\tthis.innerTree = {} as ComputeInnerTreeData;\n\t}\n\n\t/**\n\t * Append values to a specific path in the tree.\n\t *\n\t * @param path - Array of integers representing the branch path (e.g., [0], [0, 1])\n\t * @param items - Values to append at this path\n\t * @returns this for method chaining\n\t */\n\tpublic append(path: number[], items: DataTreeValue[]): this {\n\t\tconst pathKey = TreeBuilder.formatPathString(path);\n\n\t\tif (!this.innerTree[pathKey]) {\n\t\t\tthis.innerTree[pathKey] = [];\n\t\t}\n\n\t\tconst dataItems: ComputeDataItem[] = items.map((item) => ({\n\t\t\tdata: TreeBuilder.serializeValue(item)\n\t\t}));\n\n\t\tthis.innerTree[pathKey].push(...dataItems);\n\t\treturn this;\n\t}\n\n\t/**\n\t * Append a single value to a path.\n\t *\n\t * @param path - Branch path\n\t * @param item - Single value to append\n\t * @returns this for method chaining\n\t */\n\tpublic appendSingle(path: number[], item: DataTreeValue): this {\n\t\treturn this.append(path, [item]);\n\t}\n\n\t/**\n\t * Set values from a DataTreeDefault structure.\n\t * Replaces any existing tree data.\n\t *\n\t * @param treeData - TreeBuilder structure with path keys like \"{0;1}\"\n\t * @returns this for method chaining\n\t */\n\tpublic fromDataTreeDefault(treeData: DataTreeDefault): this {\n\t\tthis.innerTree = {} as ComputeInnerTreeData;\n\n\t\tfor (const [pathStr, items] of Object.entries(treeData)) {\n\t\t\tif (!Array.isArray(items)) continue;\n\t\t\tconst path = TreeBuilder.parsePathString(pathStr);\n\t\t\tthis.append(path, items);\n\t\t}\n\n\t\treturn this;\n\t}\n\n\t/**\n\t * Append flattened values to path [0].\n\t * Useful for simple flat inputs.\n\t *\n\t * @param values - Single value or array of values\n\t * @returns this for method chaining\n\t */\n\tpublic appendFlat(values: DataTreeValue | DataTreeValue[]): this {\n\t\tconst items = Array.isArray(values) ? values : [values];\n\t\treturn this.append([0], items);\n\t}\n\n\t/**\n\t * Get the flattened list of all values in the tree.\n\t *\n\t * @returns Array of all values across all branches\n\t */\n\tpublic flatten(): DataTreeValue[] {\n\t\tconst result: DataTreeValue[] = [];\n\n\t\tfor (const items of Object.values(this.innerTree)) {\n\t\t\tif (Array.isArray(items)) {\n\t\t\t\tfor (const item of items) {\n\t\t\t\t\tresult.push(TreeBuilder.deserializeValue(item.data));\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\n\t\treturn result;\n\t}\n\n\t/**\n\t * Get all paths in the tree.\n\t *\n\t * @returns Array of path strings\n\t */\n\tpublic getPaths(): DataTreePath[] {\n\t\treturn Object.keys(this.innerTree) as DataTreePath[];\n\t}\n\n\t/**\n\t * Get values at a specific path.\n\t *\n\t * @param path - Path to retrieve values from\n\t * @returns Array of values or undefined if path doesn't exist\n\t */\n\tpublic getPath(path: number[]): DataTreeValue[] | undefined {\n\t\tconst pathKey = TreeBuilder.formatPathString(path);\n\t\tconst items = this.innerTree[pathKey];\n\t\tif (!items) return undefined;\n\t\treturn items.map((item: ComputeDataItem) => TreeBuilder.deserializeValue(item.data));\n\t}\n\n\t/**\n\t * Convert to format compatible with Grasshopper Compute API.\n\t *\n\t * @returns InnerTree object ready for compute\n\t */\n\tpublic toComputeFormat(): DataTree {\n\t\treturn {\n\t\t\tParamName: this.paramName,\n\t\t\tInnerTree: this.innerTree as any // Cast to any because request format differs from response type\n\t\t};\n\t}\n\n\t/**\n\t * Get the raw InnerTree data structure.\n\t *\n\t * @returns InnerTree data\n\t */\n\tpublic getInnerTree(): ComputeInnerTreeData {\n\t\treturn this.innerTree;\n\t}\n\n\t/**\n\t * Get the parameter name.\n\t *\n\t * @returns Parameter name\n\t */\n\tpublic getParamName(): string {\n\t\treturn this.paramName;\n\t}\n\n\t// ============================================================================\n\t// Static Factory Methods\n\t// ============================================================================\n\n\t/**\n\t * Create DataTrees from an array of InputParam definitions.\n\t * Handles tree access, numeric constraints, and value parsing.\n\t *\n\t * @param inputs - Array of input parameter definitions\n\t * @returns Array of InnerTree instances ready for compute\n\t *\n\t * @example\n\t * ```ts\n\t * const trees = TreeBuilder.fromInputParams(inputs);\n\t * ```\n\t */\n\tpublic static fromInputParams(inputs: InputParam[]): DataTree[] {\n\t\treturn inputs\n\t\t\t.filter((input) => TreeBuilder.hasValidValue(input.default))\n\t\t\t.map((input) => {\n\t\t\t\tconst tree = new TreeBuilder(input.nickname || 'unnamed');\n\t\t\t\tconst value = input.default;\n\n\t\t\t\t// Handle tree access (complex TreeBuilder structure)\n\t\t\t\tif (input.treeAccess && isDataTreeDefault(value)) {\n\t\t\t\t\ttree.fromDataTreeDefault(value);\n\n\t\t\t\t\t// Apply numeric constraints to tree items\n\t\t\t\t\tif (TreeBuilder.isNumericInput(input)) {\n\t\t\t\t\t\ttree.applyNumericConstraints(input.minimum, input.maximum, input.nickname || 'unnamed');\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\t// Handle flat inputs\n\t\t\t\telse {\n\t\t\t\t\tconst values = Array.isArray(value) ? value : [value];\n\t\t\t\t\tconst processed = TreeBuilder.processValues(values, input);\n\t\t\t\t\ttree.appendFlat(processed);\n\t\t\t\t}\n\n\t\t\t\treturn tree.toComputeFormat();\n\t\t\t});\n\t}\n\n\t/**\n\t * Create a TreeBuilder from a single InputParam.\n\t *\n\t * @param input - Input parameter definition\n\t * @returns InnerTree ready for compute or undefined if value is invalid\n\t */\n\tpublic static fromInputParam(input: InputParam): DataTree | undefined {\n\t\tif (!TreeBuilder.hasValidValue(input.default)) return undefined;\n\n\t\tconst trees = TreeBuilder.fromInputParams([input]);\n\t\treturn trees[0];\n\t}\n\n\t/**\n\t * Set or replace a parameter value within a TreeBuilder or InnerTree array.\n\t *\n\t * Supports both high-level `DataTree[]` instances and low-level `InnerTree[]` format.\n\t *\n\t * **Architecture Note:**\n\t * - Use with `DataTree[]` when building/modifying before computation\n\t * - Use with `InnerTree[]` when modifying compute API results\n\t * - `DataTree` is the high-level builder; `InnerTree` is the Rhino Compute format\n\t *\n\t * @overload For TreeBuilder instances (high-level builder)\n\t * @param trees - Array of TreeBuilder instances to modify\n\t * @param paramName - The parameter name to set or replace\n\t * @param newValue - The new value (scalar, array, or TreeBuilder structure)\n\t * @returns A new/modified TreeBuilder array with the updated parameter\n\t *\n\t * @overload For compiled InnerTree (low-level API format)\n\t * @param trees - The compiled InnerTree array (typically from `client.solve()`)\n\t * @param paramName - The parameter name to set or replace\n\t * @param newValue - The new value (scalar, array, or TreeBuilder structure)\n\t * @returns A new/modified InnerTree array with the updated parameter\n\t *\n\t * @example\n\t * ```ts\n\t * // With TreeBuilder instances (high-level)\n\t * let trees = [new TreeBuilder('X'), new TreeBuilder('Y')];\n\t * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);\n\t * const result = await client.solve(definitionUrl,\n\t * trees.map(t => t.toComputeFormat())\n\t * );\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * // With InnerTree format (low-level, from API)\n\t * let trees = await client.solve(definitionUrl, initialInputs);\n\t * trees = TreeBuilder.replaceTreeValue(trees, 'X', 42);\n\t * trees = TreeBuilder.replaceTreeValue(trees, 'Y', [1, 2, 3]);\n\t * ```\n\t */\n\tpublic static replaceTreeValue(\n\t\ttrees: TreeBuilder[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): TreeBuilder[];\n\tpublic static replaceTreeValue(\n\t\ttrees: DataTree[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): DataTree[];\n\tpublic static replaceTreeValue(\n\t\ttrees: TreeBuilder[] | DataTree[],\n\t\tparamName: string,\n\t\tnewValue: DataTreeValue\n\t): TreeBuilder[] | DataTree[] {\n\t\tconst isBuilderArray = trees.length > 0 && trees[0] instanceof TreeBuilder;\n\t\tconst builder = TreeBuilder.buildFromValue(paramName, newValue);\n\n\t\tif (isBuilderArray) {\n\t\t\tconst builders = trees as TreeBuilder[];\n\t\t\tconst idx = builders.findIndex((t) => t.getParamName() === paramName);\n\t\t\tif (idx !== -1) builders[idx] = builder;\n\t\t\telse builders.push(builder);\n\t\t\treturn builders;\n\t\t}\n\n\t\t// Empty arrays land here too — see the \"empty array\" characterization\n\t\t// test in data-tree.test.ts: pins the current behavior of returning the\n\t\t// compute-format shape rather than a TreeBuilder.\n\t\tconst dataTrees = trees as DataTree[];\n\t\tconst compiled = builder.toComputeFormat();\n\t\tconst idx = dataTrees.findIndex((t) => t.ParamName === paramName);\n\t\tif (idx !== -1) dataTrees[idx] = compiled;\n\t\telse dataTrees.push(compiled);\n\t\treturn dataTrees;\n\t}\n\n\t/**\n\t * Build a TreeBuilder from a single value, dispatching on shape:\n\t * DataTreeDefault structure, array, or scalar.\n\t */\n\tprivate static buildFromValue(paramName: string, value: DataTreeValue): TreeBuilder {\n\t\tconst tree = new TreeBuilder(paramName);\n\t\tif (isDataTreeDefault(value)) {\n\t\t\ttree.fromDataTreeDefault(value);\n\t\t} else {\n\t\t\ttree.appendFlat(value);\n\t\t}\n\t\treturn tree;\n\t}\n\n\t/**\n\t * Extract a value from a TreeBuilder or InnerTree array by parameter name.\n\t *\n\t * Automatically unwraps single values for convenience.\n\t * Works with both high-level `DataTree[]` instances and low-level `InnerTree[]` format.\n\t *\n\t * **Architecture Note:**\n\t * - Use with `DataTree[]` to read builder instances\n\t * - Use with `InnerTree[]` to read compute API responses\n\t * - Return behavior is consistent across both formats\n\t *\n\t * **Return Value Behavior:**\n\t * - Single value → unwrapped (returns `5` not `[5]`)\n\t * - Multiple values → array of values\n\t * - Not found → `null`\n\t *\n\t * @overload For TreeBuilder instances\n\t * @param trees - Array of TreeBuilder instances to read from\n\t * @param paramName - The parameter name to retrieve\n\t * @returns The unwrapped value, array of values, or null if parameter not found\n\t *\n\t * @overload For compiled InnerTree\n\t * @param trees - The compiled InnerTree array (typically from `client.solve()`)\n\t * @param paramName - The parameter name to retrieve\n\t * @returns The unwrapped value, array of values, or null if parameter not found\n\t *\n\t * @example\n\t * ```ts\n\t * // With TreeBuilder instances\n\t * const trees = [new TreeBuilder('X'), new TreeBuilder('Y')];\n\t * trees[0].appendFlat(42);\n\t * const x = TreeBuilder.getTreeValue(trees, 'X'); // Returns 42\n\t * ```\n\t *\n\t * @example\n\t * ```ts\n\t * // With InnerTree from compute results\n\t * const result = await client.solve(definitionUrl, inputs);\n\t * const x = TreeBuilder.getTreeValue(result, 'X'); // Returns 42 (not [42])\n\t * const points = TreeBuilder.getTreeValue(result, 'Points'); // Returns [point1, point2, ...]\n\t * ```\n\t */\n\tpublic static getTreeValue(trees: TreeBuilder[], paramName: string): DataTreeValue | null;\n\tpublic static getTreeValue(trees: DataTree[], paramName: string): DataTreeValue | null;\n\tpublic static getTreeValue(\n\t\ttrees: TreeBuilder[] | DataTree[],\n\t\tparamName: string\n\t): DataTreeValue | null {\n\t\tconst isBuilderArray = trees.length > 0 && trees[0] instanceof TreeBuilder;\n\n\t\tconst values = isBuilderArray\n\t\t\t? TreeBuilder.readFromBuilders(trees as TreeBuilder[], paramName)\n\t\t\t: TreeBuilder.readFromDataTrees(trees as DataTree[], paramName);\n\n\t\tif (values === null) return null;\n\t\tif (values.length === 0) return null;\n\t\tif (values.length === 1) return values[0];\n\t\treturn values;\n\t}\n\n\t/**\n\t * Read all values for `paramName` across every branch of the matching builder.\n\t * Returns null when the builder isn't found.\n\t */\n\tprivate static readFromBuilders(\n\t\tbuilders: TreeBuilder[],\n\t\tparamName: string\n\t): DataTreeValue[] | null {\n\t\tconst tree = builders.find((t) => t.getParamName() === paramName);\n\t\treturn tree ? tree.flatten() : null;\n\t}\n\n\t/**\n\t * Read values from the first branch of the matching compiled InnerTree\n\t * (multi-branch responses are not flattened — current semantics, pinned by\n\t * the \"reads from the first branch path only\" test).\n\t */\n\tprivate static readFromDataTrees(\n\t\tdataTrees: DataTree[],\n\t\tparamName: string\n\t): DataTreeValue[] | null {\n\t\tconst tree = dataTrees.find((t) => t.ParamName === paramName);\n\t\tif (!tree?.InnerTree) return null;\n\n\t\tconst firstKey = Object.keys(tree.InnerTree)[0];\n\t\tif (!firstKey) return null;\n\n\t\t// @ts-expect-error - Dynamic key access on innerTree\n\t\tconst items = tree.InnerTree[firstKey];\n\n\t\tif (Array.isArray(items)) {\n\t\t\treturn items\n\t\t\t\t.map((item) => (item?.data !== undefined ? TreeBuilder.deserializeValue(item.data) : null))\n\t\t\t\t.filter((v): v is DataTreeValue => v !== null);\n\t\t}\n\n\t\tif (items?.data !== undefined) return [TreeBuilder.deserializeValue(items.data)];\n\t\treturn items !== undefined ? [items as DataTreeValue] : null;\n\t}\n\n\t/**\n\t * Parse a TreeBuilder path string like \"{0;1;2}\" into [0, 1, 2].\n\t *\n\t * @param pathStr - Path string\n\t * @returns Array of path indices\n\t */\n\tpublic static parsePathString(pathStr: string): number[] {\n\t\t// Allow the legitimate root path \"{}\" alongside \"{0;1;2}\"\n\t\tconst match = pathStr.match(TREE_PATH_RE);\n\t\tif (!match) {\n\t\t\tgetLogger().warn(`Invalid TreeBuilder path format: ${pathStr}, using [0]`);\n\t\t\treturn [0];\n\t\t}\n\t\tif (match[1] === '') return [];\n\t\treturn match[1].split(';').map(Number);\n\t}\n\n\t/**\n\t * Format a path array into TreeBuilder path string format.\n\t *\n\t * @param path - Path as number array\n\t * @returns Formatted path string like \"{0;1;2}\"\n\t */\n\tpublic static formatPathString(path: number[]): DataTreePath {\n\t\treturn `{${path.join(';')}}` as DataTreePath;\n\t}\n\n\t// ============================================================================\n\t// Private Helper Methods\n\t// ============================================================================\n\n\t/**\n\t * Apply numeric constraints to all tree values.\n\t */\n\tprivate applyNumericConstraints(\n\t\tmin: number | null | undefined,\n\t\tmax: number | null | undefined,\n\t\tinputName: string\n\t): void {\n\t\tfor (const items of Object.values(this.innerTree)) {\n\t\t\tif (!Array.isArray(items)) continue;\n\n\t\t\tfor (const item of items) {\n\t\t\t\tconst value = TreeBuilder.deserializeValue(item.data);\n\t\t\t\tif (typeof value === 'number') {\n\t\t\t\t\tconst clamped = TreeBuilder.clampValue(value, min, max, inputName);\n\t\t\t\t\titem.data = TreeBuilder.serializeValue(clamped);\n\t\t\t\t}\n\t\t\t}\n\t\t}\n\t}\n\n\t/**\n\t * Serialize a value for compute requests.\n\t * Preserves booleans and numbers as primitives for proper Grasshopper parameter handling.\n\t */\n\tprivate static serializeValue(value: DataTreeValue): string | boolean | number {\n\t\tif (typeof value === 'boolean') return value;\n\t\tif (typeof value === 'number') return value;\n\t\tif (typeof value === 'string') return value;\n\t\tif (typeof value === 'object' && value !== null) {\n\t\t\treturn JSON.stringify(value);\n\t\t}\n\t\treturn String(value);\n\t}\n\n\t/**\n\t * Deserialize a value back to its original type.\n\t * Handles both string-encoded values and primitive values.\n\t */\n\tprivate static deserializeValue(data: string | boolean | number): DataTreeValue {\n\t\t// If already a primitive type, return as-is\n\t\tif (typeof data === 'boolean') return data;\n\t\tif (typeof data === 'number') return data;\n\n\t\t// Handle string values\n\t\tif (typeof data !== 'string') return data;\n\n\t\t// Try to parse as JSON first\n\t\tif (data.startsWith('{') || data.startsWith('[')) {\n\t\t\ttry {\n\t\t\t\treturn JSON.parse(data);\n\t\t\t} catch {\n\t\t\t\treturn data;\n\t\t\t}\n\t\t}\n\t\t// Try to parse as number\n\t\tif (!isNaN(Number(data))) {\n\t\t\treturn Number(data);\n\t\t}\n\t\t// Try to parse as boolean\n\t\tif (data === 'true') return true;\n\t\tif (data === 'false') return false;\n\t\treturn data;\n\t}\n\n\t/**\n\t * Check if a value is valid for inclusion in a DataTree.\n\t */\n\tprivate static hasValidValue(value: unknown): boolean {\n\t\tif (value === undefined || value === null) return false;\n\t\tif (typeof value === 'string') return true;\n\t\tif (Array.isArray(value) && value.length === 0) return false;\n\t\tif (typeof value === 'object' && !Array.isArray(value) && Object.keys(value).length === 0)\n\t\t\treturn false;\n\t\treturn true;\n\t}\n\n\t/**\n\t * Check if input is numeric type.\n\t */\n\tprivate static isNumericInput(input: InputParam): input is InputParam & {\n\t\tparamType: 'Number' | 'Integer';\n\t\tminimum?: number | null;\n\t\tmaximum?: number | null;\n\t} {\n\t\treturn input.paramType === 'Number' || input.paramType === 'Integer';\n\t}\n\n\t/**\n\t * Process array of values based on input type.\n\t */\n\tprivate static processValues(values: DataTreeValue[], input: InputParam): DataTreeValue[] {\n\t\treturn values\n\t\t\t.map((val) => {\n\t\t\t\t// Apply numeric constraints\n\t\t\t\tif (TreeBuilder.isNumericInput(input) && typeof val === 'number') {\n\t\t\t\t\treturn TreeBuilder.clampValue(\n\t\t\t\t\t\tval,\n\t\t\t\t\t\tinput.minimum,\n\t\t\t\t\t\tinput.maximum,\n\t\t\t\t\t\tinput.nickname || 'unnamed'\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\t// Keep objects and strings as-is (serialization happens in append)\n\t\t\t\treturn val;\n\t\t\t})\n\t\t\t.filter((v) => v !== null && v !== undefined);\n\t}\n\n\t/**\n\t * Clamp numeric value to constraints.\n\t */\n\tprivate static clampValue(\n\t\tvalue: number,\n\t\tmin: number | null | undefined,\n\t\tmax: number | null | undefined,\n\t\tinputName: string\n\t): number {\n\t\tlet result = value;\n\n\t\tif (min !== null && min !== undefined && result < min) {\n\t\t\tgetLogger().warn(`${inputName}: ${value} below min ${min}, clamping`);\n\t\t\tresult = min;\n\t\t}\n\t\tif (max !== null && max !== undefined && result > max) {\n\t\t\tgetLogger().warn(`${inputName}: ${value} above max ${max}, clamping`);\n\t\t\tresult = max;\n\t\t}\n\n\t\treturn result;\n\t}\n}\n"],"mappings":"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","names":["warnIfClientSide","functionName","suppress","getLogger","DEFINITION_LOAD_FAILED","isDefinitionLoadMiss","error","RhinoComputeError","solveGrasshopperDefinition","dataTree","definition","config","warnIfClientSide","response","runSolve","prepareGrasshopperArgs","solveGrasshopperDefinitionWithCacheKey","solveByCacheKey","cacheKey","pointerArgs","args","applyOptionalComputeSettings","result","fetchRhinoCompute","pointer","rest","base64ByteArray","isBase64","encodeStringToBase64","arglist","options","stableStringify","value","seen","stringify","v","sample","k","fnv1aCore","length","codeAt","hash","i","fnv1a","input","fnv1aBytes","bytes","hashSolveInput","definition","dataTree","hashDefinition","isReusableDefinition","definition","SolveScheduler","executor","baseConfig","options","cacheKeyExecutor","__publicField","cacheOpt","cacheConfig","listener","err","getLogger","dataTree","RhinoComputeError","ErrorCodes","key","hashSolveInput","ctx","cached","result","resolve","reject","item","inflight","controller","externalAbortHandler","startTime","config","response","durationMs","error","defKey","hashDefinition","knownKey","next","entry","oldest","hook","args","GrasshopperClient","_GrasshopperClient","config","__publicField","ComputeServerStats","client","RhinoComputeError","ErrorCodes","definition","fetchParsedDefinitionIO","fetchDefinitionIO","dataTree","options","effectiveConfig","result","solveGrasshopperDefinition","error","getLogger","executor","cacheKeyExecutor","cacheKey","solveGrasshopperDefinitionWithCacheKey","r","solveByCacheKey","SolveScheduler","validateServerUrl","decoderRegistry","registerDecoder","typeName","decoder","rhino","data","d","findDecoder","rhinoType","key","dec","extractPayload","parsedData","decodeRhinoGeometry","error","getLogger","payload","SYSTEM_TYPES","RHINO_GEOMETRY_PREFIX","EXCLUDED_TYPES","FILE_DATA_TYPE","isExcludedType","type","t","tryDecodeJSON","value","trimmed","first","decodeBySystemType","raw","rhino","decodeRhinoGeometry","extractItemValue","data","parseValues","isFileData","v","forEachTreeItem","tree","handler","list","item","getValues","response","byId","options","stringOnly","result","param","key","extractFileData","output","parsed","getValue","parseOptions","targetParam","p","found","collected","GrasshopperResponseProcessor","response","debug","byId","options","getValues","selector","getValue","paramName","paramId","mergedOptions","getThreeMeshesFromComputeResponse","error","RhinoComputeError","ErrorCodes","extractFileData","folderName","additionalFiles","files","downloadFileData","normalizeDefault","input","getLogger","innerTree","tree","branch","items","item","num","allValues","TREE_PATH_RE","isDataTreeDefault","value","entries","key","val","coerceDefault","value","transform","setUndefinedOnEmpty","processed","v","transformed","numericTransformer","parsed","booleanTransformer","lower","textTransformer","colorTransformer","cleaned","objectTransformer","inputName","getLogger","err","applyRounding","decimalPlaces","tolerance","rounded","getInputStepSize","roundingTolerance","abs","decimalPart","decimals","step","s","expMatch","exp","absExp","MAX_DECIMALS","trimmed","computeNumeric","schema","isIntegerType","isDataTreeDefault","val","firstValue","stepSource","stepSize","stepStr","inferred","numericParser","base","def","isList","booleanParser","error","RhinoComputeError","textParser","valueListParser","defaultLower","key","geometryParser","fileParser","colorParser","ALL_PARSERS","INPUT_TYPE_PARSERS","parser","type","UNKNOWN_TYPE_FALLBACK","CANONICAL_PARAM_TYPES","INPUT_TYPE_PARSERS","key","canonicalizeParamType","paramType","processInput","rawInput","processInputWithError","baseInput","schema","normalizeDefault","parser","RhinoComputeError","error","getLogger","UNKNOWN_TYPE_FALLBACK","processInputs","rawInputs","processInputsWithErrors","inputs","parseErrors","raw","input","fetchDefinitionIO","definition","config","args","prepareGrasshopperArgs","payload","RhinoComputeError","ErrorCodes","response","fetchRhinoCompute","loadWarnings","nonEmptyStrings","loadErrors","value","cleaned","v","fetchParsedDefinitionIO","warnIfClientSide","rawInputs","outputs","inputs","parseErrors","processInputsWithErrors","TreeBuilder","_TreeBuilder","paramName","__publicField","path","items","pathKey","dataItems","item","treeData","pathStr","values","result","inputs","input","tree","value","isDataTreeDefault","processed","trees","newValue","isBuilderArray","builder","builders","idx","t","dataTrees","compiled","firstKey","v","match","TREE_PATH_RE","getLogger","min","max","inputName","clamped","data","val"]}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }var _chunkMQKJ46HTcjs = require('./chunk-MQKJ46HT.cjs');function b(t,e){e||typeof window<"u"&&_chunkMQKJ46HTcjs.e.call(void 0, ).warn(`Warning: ${t} is running on the client side. For better performance and security, consider running this on the server side.`)}var ye="Unable to load grasshopper definition";function ge(t){return t instanceof _chunkMQKJ46HTcjs.d&&t.message.includes(ye)}async function x(t,e,r){r.debug&&b("solveGrasshopperDefinition",_nullishCoalesce(r.suppressBrowserWarning, () => (r.suppressClientSideWarning)));let{response:n}=await N(D(e,t),r);return n}async function H(t,e,r){return r.debug&&b("solveGrasshopperDefinitionWithCacheKey",_nullishCoalesce(r.suppressBrowserWarning, () => (r.suppressClientSideWarning))),N(D(e,t),r)}async function Y(t,e,r,n){n.debug&&b("solveByCacheKey",_nullishCoalesce(n.suppressBrowserWarning, () => (n.suppressClientSideWarning)));let s={algo:null,pointer:e,values:t};try{return{...await N(s,n),missed:!1}}catch(o){if(!ge(o))throw o;return{...await N(D(r,t),n),missed:!0}}}async function N(t,e){be(t,e);let r=await _chunkMQKJ46HTcjs.h.call(void 0, "grasshopper",t,e);if("pointer"in r){let{pointer:n,...s}=r;return{response:s,cacheKey:typeof n=="string"?n:null}}return{response:r,cacheKey:null}}function D(t,e){let r={algo:null,pointer:null,values:e};return t instanceof Uint8Array?r.algo=_chunkMQKJ46HTcjs.p.call(void 0, t):/^https?:\/\//i.test(t)?r.pointer=t:_chunkMQKJ46HTcjs.n.call(void 0, t)?r.algo=t:r.algo=_chunkMQKJ46HTcjs.m.call(void 0, t),r}function be(t,e){e.cachesolve!=null&&(t.cachesolve=e.cachesolve),e.modelunits!=null&&(t.modelunits=e.modelunits),e.angletolerance!=null&&(t.angletolerance=e.angletolerance),e.absolutetolerance!=null&&(t.absolutetolerance=e.absolutetolerance),e.dataversion!=null&&(t.dataversion=e.dataversion)}function Q(t){let e=new WeakSet,r=n=>{if(n==null)return JSON.stringify(n);if(typeof n=="number")return Number.isFinite(n)?String(n):JSON.stringify(null);if(typeof n=="string"||typeof n=="boolean")return JSON.stringify(n);if(typeof n=="bigint")return JSON.stringify(n.toString());if(n instanceof Uint8Array){let s=n.length>64?Array.from(n.slice(0,32)).concat(Array.from(n.slice(-32))):Array.from(n);return JSON.stringify({__u8:!0,len:n.length,sample:s})}return Array.isArray(n)?`[${n.map(r).join(",")}]`:typeof n=="object"?e.has(n)?JSON.stringify("[Circular]"):(e.add(n),`{${Object.keys(n).sort().map(a=>`${JSON.stringify(a)}:${r(n[a])}`).join(",")}}`):JSON.stringify(null)};return r(t)}function X(t,e){let r=2166136261;for(let n=0;n<t;n++)r^=e(n),r=r+((r<<1)+(r<<4)+(r<<7)+(r<<8)+(r<<24))>>>0;return r.toString(16).padStart(8,"0")}function Z(t){return X(t.length,e=>t.charCodeAt(e))}function ee(t){return X(t.length,e=>t[e])}function O(t,e){return Z(`${G(t)}|${Q(e)}`)}function G(t){return typeof t=="string"?t:`u8:${t.length}:${ee(t)}`}function Te(t){return t instanceof Uint8Array?!0:!/^https?:\/\//i.test(t)}var T=class{constructor(e,r,n={},s){_chunkMQKJ46HTcjs.b.call(void 0, this,"executor");_chunkMQKJ46HTcjs.b.call(void 0, this,"baseConfig");_chunkMQKJ46HTcjs.b.call(void 0, this,"mode");_chunkMQKJ46HTcjs.b.call(void 0, this,"maxConcurrent");_chunkMQKJ46HTcjs.b.call(void 0, this,"timeoutMs");_chunkMQKJ46HTcjs.b.call(void 0, this,"retry");_chunkMQKJ46HTcjs.b.call(void 0, this,"cacheEnabled");_chunkMQKJ46HTcjs.b.call(void 0, this,"cacheMax");_chunkMQKJ46HTcjs.b.call(void 0, this,"cacheTtl");_chunkMQKJ46HTcjs.b.call(void 0, this,"cache",new Map);_chunkMQKJ46HTcjs.b.call(void 0, this,"cacheKeyExecutor");_chunkMQKJ46HTcjs.b.call(void 0, this,"reuseServerDefinitionCache");_chunkMQKJ46HTcjs.b.call(void 0, this,"serverCacheKeys",new Map);_chunkMQKJ46HTcjs.b.call(void 0, this,"onStart");_chunkMQKJ46HTcjs.b.call(void 0, this,"onSettle");_chunkMQKJ46HTcjs.b.call(void 0, this,"onSuperseded");_chunkMQKJ46HTcjs.b.call(void 0, this,"subscribers",new Set);_chunkMQKJ46HTcjs.b.call(void 0, this,"inFlight",new Set);_chunkMQKJ46HTcjs.b.call(void 0, this,"pendingForLatestWins",null);_chunkMQKJ46HTcjs.b.call(void 0, this,"fifoQueue",[]);_chunkMQKJ46HTcjs.b.call(void 0, this,"_lastResult",null);_chunkMQKJ46HTcjs.b.call(void 0, this,"_lastError",null);_chunkMQKJ46HTcjs.b.call(void 0, this,"_lastDurationMs",null);_chunkMQKJ46HTcjs.b.call(void 0, this,"disposed",!1);this.executor=e,this.cacheKeyExecutor=s,this.baseConfig=r,this.mode=_nullishCoalesce(n.mode, () => ("latest-wins")),this.maxConcurrent=Math.max(1,_nullishCoalesce(n.maxConcurrent, () => ((this.mode==="parallel"?4:1)))),this.timeoutMs=n.timeoutMs,this.retry=n.retry;let o=n.cache;this.cacheEnabled=o!==void 0&&o!==!1;let a=typeof o=="object"?o:{};this.cacheMax=_nullishCoalesce(a.maxEntries, () => (50)),this.cacheTtl=_nullishCoalesce(a.ttlMs, () => (0)),this.reuseServerDefinitionCache=!!s&&(_nullishCoalesce(n.reuseServerDefinitionCache, () => (!0))),this.onStart=n.onStart,this.onSettle=n.onSettle,this.onSuperseded=n.onSuperseded}get isSolving(){return this.inFlight.size>0}get hasPending(){return this.pendingForLatestWins!==null||this.fifoQueue.length>0}get inFlightCount(){return this.inFlight.size}get queueDepth(){return this.fifoQueue.length+(this.pendingForLatestWins?1:0)}get lastResult(){return this._lastResult}get lastError(){return this._lastError}get lastDurationMs(){return this._lastDurationMs}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(){for(let e of this.subscribers)try{e()}catch(r){_chunkMQKJ46HTcjs.e.call(void 0, ).error("[SolveScheduler] subscriber threw:",r)}}solve(e,r,n){if(this.disposed)return Promise.reject(new (0, _chunkMQKJ46HTcjs.d)("SolveScheduler has been disposed and cannot be used",_chunkMQKJ46HTcjs.c.INVALID_STATE));let s=O(e,r),o={key:s,enqueuedAt:Date.now(),startedAt:null};if(this.cacheEnabled){let a=this.readCache(s);if(a){let i={status:"success",response:a,durationMs:0,fromCache:!0};return this._lastResult=a,this._lastError=null,this._lastDurationMs=0,this.runHook(this.onStart,o),this.runHook(this.onSettle,o,i),this.notify(),Promise.resolve(a)}}return new Promise((a,i)=>{let u={definition:e,dataTree:r,ctx:o,resolve:a,reject:i,externalSignal:_optionalChain([n, 'optionalAccess', _2 => _2.signal])};if(_optionalChain([u, 'access', _3 => _3.externalSignal, 'optionalAccess', _4 => _4.aborted])){this.settleError(u,this.makeAbortError(o));return}this.enqueue(u)})}enqueue(e){switch(this.mode){case"latest-wins":{this.pendingForLatestWins&&(this.supersede(this.pendingForLatestWins),this.pendingForLatestWins=null);for(let r of this.inFlight)this.supersede(r),r.controller.abort();this.inFlight.size===0?this.execute(e):this.pendingForLatestWins=e;break}case"queue":case"parallel":{this.inFlight.size<this.maxConcurrent?this.execute(e):this.fifoQueue.push(e);break}}this.notify()}async execute(e){let r=new AbortController,n={...e,controller:r};this.inFlight.add(n),e.ctx.startedAt=Date.now();let s=()=>r.abort();_optionalChain([e, 'access', _5 => _5.externalSignal, 'optionalAccess', _6 => _6.addEventListener, 'call', _7 => _7("abort",s,{once:!0})]),this.runHook(this.onStart,e.ctx),this.notify();let o=performance.now();try{let a={...this.baseConfig,signal:r.signal,...this.timeoutMs!==void 0&&{timeoutMs:this.timeoutMs},...this.retry!==void 0&&{retry:this.retry}},i=await this.runExecutor(e.definition,e.dataTree,a),u=performance.now()-o;if(this.cacheEnabled&&this.writeCache(e.ctx.key,i),!this.settleSuccess(e,i))return;this._lastResult=i,this._lastError=null,this._lastDurationMs=u,this.runHook(this.onSettle,e.ctx,{status:"success",response:i,durationMs:u,fromCache:!1})}catch(a){let i=performance.now()-o,u=this.normalizeExecutionError(a,n);this._lastError=u,this._lastDurationMs=i,this.settleError(e,u)&&this.runHook(this.onSettle,e.ctx,{status:"error",error:u,durationMs:i})}finally{_optionalChain([e, 'access', _8 => _8.externalSignal, 'optionalAccess', _9 => _9.removeEventListener, 'call', _10 => _10("abort",s)]),this.inFlight.delete(n),this.drainNext(),this.notify()}}async runExecutor(e,r,n){if(!this.cacheKeyExecutor||!this.reuseServerDefinitionCache||!Te(e))return this.executor(e,r,n);let s=G(e),o=_nullishCoalesce(this.serverCacheKeys.get(s), () => (null)),a=await this.cacheKeyExecutor(e,r,o,n);return a.cacheKey?this.serverCacheKeys.set(s,a.cacheKey):this.serverCacheKeys.delete(s),a.response}drainNext(){if(!this.disposed){if(this.mode==="latest-wins"){if(this.pendingForLatestWins&&this.inFlight.size===0){let e=this.pendingForLatestWins;this.pendingForLatestWins=null,this.execute(e)}return}for(;this.fifoQueue.length>0&&this.inFlight.size<this.maxConcurrent;){let e=this.fifoQueue.shift();this.execute(e)}}}supersede(e){let r=new (0, _chunkMQKJ46HTcjs.d)("Superseded by newer solve",_chunkMQKJ46HTcjs.c.SUPERSEDED,{context:{key:e.ctx.key,enqueuedAt:e.ctx.enqueuedAt}});this.settleError(e,r)&&this.runHook(this.onSuperseded,e.ctx)}makeAbortError(e){return new (0, _chunkMQKJ46HTcjs.d)("Request aborted by caller",_chunkMQKJ46HTcjs.c.ABORTED,{context:{key:e.key,enqueuedAt:e.enqueuedAt}})}settleError(e,r){return e.settled?!1:(e.settled={error:r},e.reject(r),!0)}settleSuccess(e,r){return e.settled?!1:(e.settled={ok:!0},e.resolve(r),!0)}isAbortLikeError(e){if(e instanceof Error){if(e.name==="AbortError")return!0;if(typeof DOMException<"u"&&e instanceof DOMException)return e.name==="AbortError"}return!1}normalizeExecutionError(e,r){return r.settled&&"error"in r.settled?r.settled.error:e instanceof _chunkMQKJ46HTcjs.d?e:this.isAbortLikeError(e)?this.makeAbortError(r.ctx):new (0, _chunkMQKJ46HTcjs.d)(e instanceof Error?e.message:String(e),_chunkMQKJ46HTcjs.c.UNKNOWN_ERROR,{originalError:e instanceof Error?e:new Error(String(e))})}cancelAll(){for(this.pendingForLatestWins&&(this.rejectAsAborted(this.pendingForLatestWins),this.pendingForLatestWins=null);this.fifoQueue.length>0;){let e=this.fifoQueue.shift();this.rejectAsAborted(e)}for(let e of this.inFlight){let r=this.makeAbortError(e.ctx);this.settleError(e,r)&&this.runHook(this.onSettle,e.ctx,{status:"error",error:r,durationMs:e.ctx.startedAt?performance.now()-e.ctx.startedAt:0}),e.controller.abort()}this.notify()}rejectAsAborted(e){this.settleError(e,this.makeAbortError(e.ctx))}readCache(e){if(!this.cacheEnabled)return null;let r=this.cache.get(e);return r?this.cacheTtl>0&&Date.now()-r.insertedAt>this.cacheTtl?(this.cache.delete(e),null):(this.cache.delete(e),this.cache.set(e,r),r.response):null}writeCache(e,r){if(this.cacheEnabled)for(this.cache.set(e,{response:r,insertedAt:Date.now()});this.cache.size>this.cacheMax;){let n=this.cache.keys().next().value;if(n===void 0)break;this.cache.delete(n)}}clearCache(){this.cache.clear()}dispose(){this.disposed||(this.disposed=!0,this.cancelAll(),this.subscribers.clear(),this.cache.clear())}runHook(e,...r){if(e)try{e(...r)}catch(n){_chunkMQKJ46HTcjs.e.call(void 0, ).error("[SolveScheduler] hook threw:",n)}}};var P=class t{constructor(e){_chunkMQKJ46HTcjs.b.call(void 0, this,"config");_chunkMQKJ46HTcjs.b.call(void 0, this,"serverStats");_chunkMQKJ46HTcjs.b.call(void 0, this,"disposed",!1);this.config=this.normalizeComputeConfig(e),this.serverStats=new (0, _chunkMQKJ46HTcjs.j)(this.config.serverUrl,this.config.apiKey)}static async create(e){let r=new t(e);if(!await r.serverStats.isServerOnline())throw new (0, _chunkMQKJ46HTcjs.d)("Rhino Compute server is not online",_chunkMQKJ46HTcjs.c.NETWORK_ERROR,{context:{serverUrl:r.config.serverUrl}});return r}getConfig(){return this.ensureNotDisposed(),{...this.config}}async getIO(e){return this.ensureNotDisposed(),V(e,this.config)}async getRawIO(e){return this.ensureNotDisposed(),R(e,this.config)}async solve(e,r,n){this.ensureNotDisposed();try{if(typeof e=="string"&&!_optionalChain([e, 'optionalAccess', _11 => _11.trim, 'call', _12 => _12()]))throw new (0, _chunkMQKJ46HTcjs.d)("Definition URL/content is required",_chunkMQKJ46HTcjs.c.INVALID_INPUT,{context:{receivedUrl:e}});if(e instanceof Uint8Array&&e.length===0)throw new (0, _chunkMQKJ46HTcjs.d)("Definition content is empty",_chunkMQKJ46HTcjs.c.INVALID_INPUT);let s={...this.config,..._optionalChain([n, 'optionalAccess', _13 => _13.signal])!==void 0&&{signal:n.signal},..._optionalChain([n, 'optionalAccess', _14 => _14.timeoutMs])!==void 0&&{timeoutMs:n.timeoutMs},..._optionalChain([n, 'optionalAccess', _15 => _15.retry])!==void 0&&{retry:n.retry}},o=await x(r,e,s);if(_optionalChain([o, 'optionalAccess', _16 => _16.errors])&&o.errors.length>0)throw new (0, _chunkMQKJ46HTcjs.d)(o.errors.join("; ")||"Computation failed",_chunkMQKJ46HTcjs.c.COMPUTATION_ERROR,{context:{definition:typeof e=="string"&&e.length<200?e:"...content...",inputs:r,errors:o.errors,warnings:o.warnings}});return o}catch(s){throw this.config.debug&&_chunkMQKJ46HTcjs.e.call(void 0, ).error("Compute failed:",s),s instanceof _chunkMQKJ46HTcjs.d?s:new (0, _chunkMQKJ46HTcjs.d)(s instanceof Error?s.message:String(s),_chunkMQKJ46HTcjs.c.COMPUTATION_ERROR,{context:{definition:typeof e=="string"&&e.length<200?e:"...content...",inputs:r},originalError:s instanceof Error?s:new Error(String(s))})}}createScheduler(e){this.ensureNotDisposed();let r=(s,o,a)=>x(o,s,a),n=(s,o,a,i)=>a===null?H(o,s,i).then(u=>({...u,missed:!1})):Y(o,a,s,i);return new T(r,this.config,e,n)}async dispose(){this.disposed||(this.disposed=!0,await this.serverStats.dispose())}ensureNotDisposed(){if(this.disposed)throw new (0, _chunkMQKJ46HTcjs.d)("GrasshopperClient has been disposed and cannot be used",_chunkMQKJ46HTcjs.c.INVALID_STATE)}normalizeComputeConfig(e){return{...e,serverUrl:_chunkMQKJ46HTcjs.i.call(void 0, e.serverUrl),apiKey:e.apiKey,authToken:e.authToken,debug:_nullishCoalesce(e.debug, () => (!1)),suppressBrowserWarning:_nullishCoalesce(e.suppressBrowserWarning, () => (e.suppressClientSideWarning))}}};var F=new Map;function te(t,e){F.set(t,e)}te("Rhino.Geometry.Point3d",(t,e)=>{let r=e;return!r||typeof r.X!="number"?null:new t.Point([r.X,r.Y,r.Z])});te("Rhino.Geometry.Line",(t,e)=>{let r=e;return!r||!r.From||!r.To?null:new t.Line([r.From.X,r.From.Y,r.From.Z],[r.To.X,r.To.Y,r.To.Z])});function Ce(t){if(F.has(t))return F.get(t);for(let[e,r]of F)if(t.startsWith(e))return r}function Ie(t){return!t||typeof t!="object"?null:_nullishCoalesce(_nullishCoalesce(t.data, () => (t.value)), () => (null))}function re(t,e,r){let n=Ce(e);if(n)try{return n(r,t)}catch(s){_chunkMQKJ46HTcjs.e.call(void 0, ).warn(`Failed to decode Rhino type ${e}:`,s)}try{let s=Ie(t);if(s)return r.CommonObject.decode(s)}catch(s){return _chunkMQKJ46HTcjs.e.call(void 0, ).warn(`Failed to decode ${e} with CommonObject:`,s),{__decodeError:!0,type:e,raw:t}}return t}var C={STRING:"System.String",INT:"System.Int32",DOUBLE:"System.Double",BOOL:"System.Boolean"},Se="Rhino.Geometry.",xe=["WebDisplay"],De="FileData";function ne(t){return xe.some(e=>t.includes(e))}function se(t){if(typeof t!="string")return t;let e=t.trim();if(!(e.startsWith("{")||e.startsWith("[")||e.startsWith('"')))return t;try{let n=JSON.parse(e);if(typeof n=="string")try{return JSON.parse(n)}catch (e2){return n}return n}catch (e3){return t}}function Pe(t,e,r){switch(e){case C.STRING:return typeof t!="string"?t:t.replace(/^"(.*)"$/,"$1");case C.INT:return Number.parseInt(t,10);case C.DOUBLE:return Number.parseFloat(t);case C.BOOL:return String(t).toLowerCase()==="true";default:return r&&e.startsWith(Se)?re(t,e,r):t}}function oe(t,e,r,n){if(typeof t!="string")return t;let s=r?se(t):t;return Pe(s,e,n)}function Re(t){if(!t||typeof t!="object")return!1;let e=t;return typeof e.fileName=="string"&&typeof e.fileType=="string"&&"data"in e&&typeof e.isBase64Encoded=="boolean"&&typeof e.subFolder=="string"}function M(t,e){for(let r of Object.values(t))if(Array.isArray(r))for(let n of r)e(n)}function ae(t,e=!1,r={}){let{parseValues:n=!0,rhino:s,stringOnly:o=!1}=r,a={};for(let i of t.values)M(i.InnerTree,u=>{if(ne(u.type)||o&&u.type!==C.STRING)return;let c=e?u.id:i.ParamName;if(!c)return;let f=oe(u.data,u.type,n,s);a[c]===void 0?a[c]=f:Array.isArray(a[c])?a[c].push(f):a[c]=[a[c],f]});return{values:a}}function ie(t){let e=[];for(let r of t.values)M(r.InnerTree,n=>{if(!n.type.includes(De))return;let s=se(n.data);Re(s)&&e.push(s)});return e}function k(t,e,r={}){let{parseValues:n=!0,rhino:s,stringOnly:o=!1}=r,a;if("byName"in e?a=t.values.find(u=>u.ParamName===e.byName):a=t.values.find(u=>{let c=!1;return M(u.InnerTree,f=>{f.id===e.byId&&(c=!0)}),c}),!a)return;let i=[];if(M(a.InnerTree,u=>{if("byId"in e&&u.id!==e.byId||ne(u.type)||o&&u.type!==C.STRING)return;let c=oe(u.data,u.type,n,s);i.push(c)}),i.length!==0)return i.length===1?i[0]:i}var E=class{constructor(e,r=!1){this.response=e;this.debug=r}getValues(e=!1,r={}){return ae(this.response,e,r)}getValue(e,r){return k(this.response,e,r)}getValueByParamName(e,r){return k(this.response,{byName:e},r)}getValueByParamId(e,r){return k(this.response,{byId:e},r)}async extractMeshesFromResponse(e){let r={debug:this.debug,...e},n;try{({getThreeMeshesFromComputeResponse:n}=await Promise.resolve().then(() => _interopRequireWildcard(require("./visualization-PRUUKBYP.cjs"))))}catch(s){throw new (0, _chunkMQKJ46HTcjs.d)("Failed to load three.js visualization module. Ensure three.js is installed as a peer dependency.",_chunkMQKJ46HTcjs.c.INVALID_STATE,{context:{originalError:s instanceof Error?s.message:String(s)}})}return n(this.response,r)}getFileData(){return ie(this.response)}getAndDownloadFiles(e,r){let n=this.getFileData();_chunkMQKJ46HTcjs.r.call(void 0, n,e,r)}};function ue(t){if(typeof t.default!="object"||t.default===null)return t;if(!("innerTree"in t.default))return _chunkMQKJ46HTcjs.e.call(void 0, ).warn("Unexpected structure in input.default:",t.default),{...t,default:null};let e=t.default.innerTree;if(Object.keys(e).length===0)return{...t,default:void 0};if(t.treeAccess||t.atMost&&t.atMost>1){let n={};for(let[s,o]of Object.entries(e))n[s]=o.map(a=>{if(typeof a.data=="string"){if(a.type==="System.Double"||a.type==="System.Int32"){let i=Number(a.data);return Number.isNaN(i)?a.data:i}if(a.type==="System.Boolean")return a.data.toLowerCase()==="true";if(a.type.startsWith("Rhino.Geometry")||a.type==="System.String")try{return JSON.parse(a.data)}catch (e4){return a.data}}return a.data});return{...t,default:n}}let r=[];for(let n of Object.values(e))Array.isArray(n)&&n.forEach(s=>{s&&typeof s=="object"&&"data"in s&&r.push(s.data)});return r.length===0?{...t,default:void 0}:r.length===1?{...t,default:r[0]}:{...t,default:r}}var W=/^\{([\d;]*)\}$/;function v(t){if(typeof t!="object"||t===null||Array.isArray(t))return!1;let e=Object.entries(t);return e.length>0&&e.every(([r,n])=>W.test(r)&&Array.isArray(n))}function I(t,e,r){if(t==null)return t;if(Array.isArray(t)){let s=t.map(e).filter(o=>o!==null);return s.length>0?s:void 0}let n=e(t);return n!==null?n:r?void 0:t}var Ee=t=>{if(typeof t=="number")return t;if(typeof t=="string"){let e=Number(t.trim());return Number.isNaN(e)?null:e}return null},ve=t=>{if(typeof t=="boolean")return t;if(typeof t=="string"){let e=t.toLowerCase();if(e==="true")return!0;if(e==="false")return!1;throw new Error(`Invalid boolean string: "${t}"`)}return null},we=t=>typeof t=="string"?t.startsWith('"')&&t.endsWith('"')||t.startsWith('"')?t.slice(1,-1):t:null,Ae=t=>{if(typeof t=="string"){let e=t.trim();return e.startsWith('"')&&e.endsWith('"')&&(e=e.slice(1,-1).trim()),e}return null};function le(t){return e=>{if(typeof e=="object"&&e!==null)return e;if(typeof e=="string"&&e.trim()!=="")try{let r=JSON.parse(e);return typeof r=="object"&&r!==null?r:(_chunkMQKJ46HTcjs.e.call(void 0, ).warn(`Parsed value for input ${t} is not an object`),null)}catch(r){return _chunkMQKJ46HTcjs.e.call(void 0, ).warn(`Failed to parse object value "${e}" for input ${t}`,r),null}return null}}function pe(t,e,r){let n=Number(t.toFixed(e));return Math.abs(t-n)<r?n:t}function Ne(t,e){if(!Number.isFinite(t)||t===0)return .1;let r=Math.abs(t);if(r>=1){let h=String(t).split(".")[1];if(h&&h.length>0){let y=Math.min(h.length,12),g=Math.pow(10,-y),K=Number(g.toFixed(y));return Math.abs(K-g)<e?K:g}return 1}let n=String(t),s=n.toLowerCase().match(/e(-?\d+)/);if(s){let S=Number(s[1]);if(S<0||n.toLowerCase().includes("e-")){let h=Math.abs(S),y=Math.pow(10,-h),g=Number(y.toFixed(h));return Math.abs(g-y)<e?g:y}return .1}let o=12,i=r.toFixed(o).replace(/0+$/,""),u=Math.min((i.split(".")[1]||"").length,o);if(u===0)return .1;let c=Math.pow(10,-u),f=Number(c.toFixed(u));return Math.abs(f-c)<e?f:c}function Oe(t,e=1e-8){let r=t.paramType==="Integer";if(v(t.default))return{default:t.default,stepSize:r?1:.1};let n=I(t.default,Ee,!0);if(r)return Array.isArray(n)?n=n.map(f=>typeof f=="number"?Math.round(f):f):typeof n=="number"&&(n=Math.round(n)),{default:n,stepSize:1};let s=Array.isArray(n)?n[0]:n,o;typeof s=="number"&&Number.isFinite(s)&&s!==0?o=s:typeof t.minimum=="number"&&Number.isFinite(t.minimum)&&t.minimum!==0?o=t.minimum:typeof t.maximum=="number"&&Number.isFinite(t.maximum)&&t.maximum!==0&&(o=t.maximum);let a=o!==void 0?Ne(o,e):.1,i=0,u=String(a),c=u.toLowerCase().match(/e(-?\d+)/);if(c?i=Math.abs(Number(c[1])):i=_nullishCoalesce(_optionalChain([u, 'access', _17 => _17.split, 'call', _18 => _18("."), 'access', _19 => _19[1], 'optionalAccess', _20 => _20.length]), () => (0)),i===0&&typeof s=="number"&&s!==0&&Math.abs(s)<1){let f=Math.ceil(-Math.log10(Math.abs(s)));Number.isFinite(f)&&f>0&&(i=f)}return i=Math.min(Math.max(i,0),12),Array.isArray(n)?n=n.map(f=>typeof f=="number"?pe(f,i,e):f):typeof n=="number"&&(n=pe(n,i,e)),{default:n,stepSize:a}}var Ge={types:["Number","Integer"],parse(t,e){let{default:r,stepSize:n}=Oe(t);return{...e,paramType:t.paramType,minimum:t.minimum,maximum:t.maximum,atLeast:t.atLeast,atMost:t.atMost,stepSize:n,default:r}},fallback(t,e){let r=(_nullishCoalesce(t.atMost, () => (1)))>1;return{...e,paramType:t.paramType,minimum:t.minimum,maximum:t.maximum,atLeast:t.atLeast,atMost:t.atMost,default:r?[0]:0}}},Ve={types:["Boolean"],parse(t,e){let r;try{r=I(t.default,ve,!1)}catch(n){throw n instanceof Error?new (0, _chunkMQKJ46HTcjs.d)(n.message):n}return{...e,paramType:"Boolean",default:r}},fallback(t,e){let r=(_nullishCoalesce(t.atMost, () => (1)))>1;return{...e,paramType:"Boolean",default:r?[!1]:!1}}},Fe={types:["Text"],parse(t,e){let r=I(t.default,we,!1);return{...e,paramType:"Text",default:r}},fallback(t,e){let r=(_nullishCoalesce(t.atMost, () => (1)))>1;return{...e,paramType:"Text",default:r?[""]:""}}},Me={types:["ValueList"],parse(t,e){if(!t.values||typeof t.values!="object"||Object.keys(t.values).length===0)throw _chunkMQKJ46HTcjs.d.missingValues(t.nickname||"unnamed","ValueList");if(t.default!==void 0&&t.default!==null){let r=String(t.default).toLowerCase();Object.keys(t.values).some(s=>s.toLowerCase()===r)||_chunkMQKJ46HTcjs.e.call(void 0, ).warn(`ValueList input "${t.nickname||"unnamed"}" default value "${t.default}" is not in available values`)}return{...e,paramType:"ValueList",values:t.values,default:t.default}},fallback(t,e){let r=(_nullishCoalesce(t.atMost, () => (1)))>1;return{...e,paramType:"ValueList",values:_nullishCoalesce(t.values, () => ({})),default:r?[t.default]:t.default}}},ce={types:["Geometry"],parse(t,e){let r=I(t.default,le(t.nickname||"unnamed"),!0);return{...e,paramType:"Geometry",default:r}},fallback(t,e){let r=(_nullishCoalesce(t.atMost, () => (1)))>1;return{...e,paramType:"Geometry",default:r?[null]:null}}},ke={types:["File"],parse(t,e){let r=I(t.default,le(t.nickname||"unnamed"),!0);return{...e,paramType:"File",acceptedFormats:t.acceptedFormats,default:r}},fallback(t,e){let r=(_nullishCoalesce(t.atMost, () => (1)))>1;return{...e,paramType:"File",default:r?[null]:null}}},Le={types:["Color"],parse(t,e){let r=I(t.default,Ae,!1);return{...e,paramType:"Color",default:r}},fallback(t,e){let r=(_nullishCoalesce(t.atMost, () => (1)))>1;return{...e,paramType:"Color",default:r?["0, 0, 0"]:"0, 0, 0"}}},Be=[Ge,Ve,Fe,Me,ce,ke,Le],j=new Map(Be.flatMap(t=>t.types.map(e=>[e,t]))),fe=ce;var We=new Map([...j.keys()].map(t=>[t.toLowerCase(),t]));function je(t){return _nullishCoalesce(We.get(_optionalChain([t, 'optionalAccess', _21 => _21.toLowerCase, 'call', _22 => _22()])), () => (t))}function me(t){return U(t).input}function U(t){let e={description:t.description,name:t.name,nickname:t.nickname,treeAccess:t.treeAccess,groupName:_nullishCoalesce(t.groupName, () => ("")),id:t.id},r=je(t.paramType),n=ue({...t,paramType:r}),s=j.get(r);try{if(!s)throw _chunkMQKJ46HTcjs.d.unknownParamType(r,t.name);return{input:s.parse(n,e)}}catch(o){if(o instanceof _chunkMQKJ46HTcjs.d)return _chunkMQKJ46HTcjs.e.call(void 0, ).error(`Validation error for input ${t.name||"unknown"}:`,o.message),{input:(_nullishCoalesce(s, () => (fe))).fallback(n,e),error:{inputName:t.name||"unknown",paramType:r,message:o.message,code:o.code}};throw new (0, _chunkMQKJ46HTcjs.d)(o instanceof Error?o.message:String(o),"VALIDATION_ERROR",{context:{paramName:t.name,paramType:r},originalError:o instanceof Error?o:new Error(String(o))})}}function de(t){return L(t).inputs}function L(t){let e=[],r=[];for(let n of t){let{input:s,error:o}=U(n);e.push(s),o&&r.push(o)}return{inputs:e,parseErrors:r}}async function R(t,e){let r=D(t,[]),n={};if(r.algo&&(n.algo=r.algo),r.pointer&&(n.pointer=r.pointer),!n.algo&&!n.pointer)throw new (0, _chunkMQKJ46HTcjs.d)("Definition must resolve to either a URL pointer or base64 algo",_chunkMQKJ46HTcjs.c.INVALID_INPUT,{context:{definition:t}});let s=await _chunkMQKJ46HTcjs.h.call(void 0, "io",n,e);if(!s||typeof s!="object")throw new (0, _chunkMQKJ46HTcjs.d)("Invalid IO response structure",_chunkMQKJ46HTcjs.c.INVALID_INPUT,{context:{response:s,definition:t}});let o=he(s.warnings),a=he(s.errors);return{inputs:s.inputs,outputs:s.outputs,...o&&{loadWarnings:o},...a&&{loadErrors:a}}}function he(t){if(!Array.isArray(t))return;let e=t.filter(r=>typeof r=="string"&&r.trim().length>0);return e.length>0?e:void 0}async function V(t,e){b("fetchParsedDefinitionIO",_nullishCoalesce(e.suppressBrowserWarning, () => (e.suppressClientSideWarning)));let{inputs:r,outputs:n,loadWarnings:s,loadErrors:o}=await R(t,e),{inputs:a,parseErrors:i}=L(r);return{inputs:a,outputs:n,...i.length>0&&{parseErrors:i},...s&&{loadWarnings:s},...o&&{loadErrors:o}}}var B=class t{constructor(e){_chunkMQKJ46HTcjs.b.call(void 0, this,"innerTree");_chunkMQKJ46HTcjs.b.call(void 0, this,"paramName");this.paramName=e,this.innerTree={}}append(e,r){let n=t.formatPathString(e);this.innerTree[n]||(this.innerTree[n]=[]);let s=r.map(o=>({data:t.serializeValue(o)}));return this.innerTree[n].push(...s),this}appendSingle(e,r){return this.append(e,[r])}fromDataTreeDefault(e){this.innerTree={};for(let[r,n]of Object.entries(e)){if(!Array.isArray(n))continue;let s=t.parsePathString(r);this.append(s,n)}return this}appendFlat(e){let r=Array.isArray(e)?e:[e];return this.append([0],r)}flatten(){let e=[];for(let r of Object.values(this.innerTree))if(Array.isArray(r))for(let n of r)e.push(t.deserializeValue(n.data));return e}getPaths(){return Object.keys(this.innerTree)}getPath(e){let r=t.formatPathString(e),n=this.innerTree[r];if(n)return n.map(s=>t.deserializeValue(s.data))}toComputeFormat(){return{ParamName:this.paramName,InnerTree:this.innerTree}}getInnerTree(){return this.innerTree}getParamName(){return this.paramName}static fromInputParams(e){return e.filter(r=>t.hasValidValue(r.default)).map(r=>{let n=new t(r.nickname||"unnamed"),s=r.default;if(r.treeAccess&&v(s))n.fromDataTreeDefault(s),t.isNumericInput(r)&&n.applyNumericConstraints(r.minimum,r.maximum,r.nickname||"unnamed");else{let o=Array.isArray(s)?s:[s],a=t.processValues(o,r);n.appendFlat(a)}return n.toComputeFormat()})}static fromInputParam(e){return t.hasValidValue(e.default)?t.fromInputParams([e])[0]:void 0}static replaceTreeValue(e,r,n){let s=e.length>0&&e[0]instanceof t,o=t.buildFromValue(r,n);if(s){let c=e,f=c.findIndex(S=>S.getParamName()===r);return f!==-1?c[f]=o:c.push(o),c}let a=e,i=o.toComputeFormat(),u=a.findIndex(c=>c.ParamName===r);return u!==-1?a[u]=i:a.push(i),a}static buildFromValue(e,r){let n=new t(e);return v(r)?n.fromDataTreeDefault(r):n.appendFlat(r),n}static getTreeValue(e,r){let s=e.length>0&&e[0]instanceof t?t.readFromBuilders(e,r):t.readFromDataTrees(e,r);return s===null||s.length===0?null:s.length===1?s[0]:s}static readFromBuilders(e,r){let n=e.find(s=>s.getParamName()===r);return n?n.flatten():null}static readFromDataTrees(e,r){let n=e.find(a=>a.ParamName===r);if(!_optionalChain([n, 'optionalAccess', _23 => _23.InnerTree]))return null;let s=Object.keys(n.InnerTree)[0];if(!s)return null;let o=n.InnerTree[s];return Array.isArray(o)?o.map(a=>_optionalChain([a, 'optionalAccess', _24 => _24.data])!==void 0?t.deserializeValue(a.data):null).filter(a=>a!==null):_optionalChain([o, 'optionalAccess', _25 => _25.data])!==void 0?[t.deserializeValue(o.data)]:o!==void 0?[o]:null}static parsePathString(e){let r=e.match(W);return r?r[1]===""?[]:r[1].split(";").map(Number):(_chunkMQKJ46HTcjs.e.call(void 0, ).warn(`Invalid TreeBuilder path format: ${e}, using [0]`),[0])}static formatPathString(e){return`{${e.join(";")}}`}applyNumericConstraints(e,r,n){for(let s of Object.values(this.innerTree))if(Array.isArray(s))for(let o of s){let a=t.deserializeValue(o.data);if(typeof a=="number"){let i=t.clampValue(a,e,r,n);o.data=t.serializeValue(i)}}}static serializeValue(e){return typeof e=="boolean"||typeof e=="number"||typeof e=="string"?e:typeof e=="object"&&e!==null?JSON.stringify(e):String(e)}static deserializeValue(e){if(typeof e=="boolean"||typeof e=="number"||typeof e!="string")return e;if(e.startsWith("{")||e.startsWith("["))try{return JSON.parse(e)}catch (e5){return e}return isNaN(Number(e))?e==="true"?!0:e==="false"?!1:e:Number(e)}static hasValidValue(e){return e==null?!1:typeof e=="string"?!0:!(Array.isArray(e)&&e.length===0||typeof e=="object"&&!Array.isArray(e)&&Object.keys(e).length===0)}static isNumericInput(e){return e.paramType==="Number"||e.paramType==="Integer"}static processValues(e,r){return e.map(n=>t.isNumericInput(r)&&typeof n=="number"?t.clampValue(n,r.minimum,r.maximum,r.nickname||"unnamed"):n).filter(n=>n!=null)}static clampValue(e,r,n,s){let o=e;return r!=null&&o<r&&(_chunkMQKJ46HTcjs.e.call(void 0, ).warn(`${s}: ${e} below min ${r}, clamping`),o=r),n!=null&&o>n&&(_chunkMQKJ46HTcjs.e.call(void 0, ).warn(`${s}: ${e} above max ${n}, clamping`),o=n),o}};exports.a = x; exports.b = O; exports.c = T; exports.d = P; exports.e = E; exports.f = me; exports.g = de; exports.h = R; exports.i = V; exports.j = B;
|
|
2
|
+
//# sourceMappingURL=chunk-I6BYCRNM.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["/home/runner/work/selva-compute/selva-compute/dist/chunk-I6BYCRNM.cjs","../src/core/utils/warnings.ts","../src/features/grasshopper/solve.ts","../src/features/grasshopper/scheduler/stable-hash.ts"],"names":["warnIfClientSide","functionName","suppress","getLogger","DEFINITION_LOAD_FAILED","isDefinitionLoadMiss","error","RhinoComputeError","solveGrasshopperDefinition","dataTree","definition","config","response","runSolve","prepareGrasshopperArgs","solveGrasshopperDefinitionWithCacheKey","solveByCacheKey","cacheKey","pointerArgs","args","applyOptionalComputeSettings","result","fetchRhinoCompute","pointer","rest","base64ByteArray","isBase64","encodeStringToBase64","arglist","options","stableStringify","value","seen","stringify","v","sample","k"],"mappings":"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","file":"/home/runner/work/selva-compute/selva-compute/dist/chunk-I6BYCRNM.cjs","sourcesContent":[null,"import { getLogger } from './logger';\n\nexport function warnIfClientSide(functionName: string, suppress?: boolean): void {\n\tif (suppress) {\n\t\treturn;\n\t}\n\n\tif (typeof window !== 'undefined') {\n\t\tgetLogger().warn(\n\t\t\t`Warning: ${functionName} is running on the client side. For better performance and security, consider running this on the server side.`\n\t\t);\n\t}\n}\n","import { fetchRhinoCompute, RhinoComputeError } from '@/core';\nimport { base64ByteArray, encodeStringToBase64, isBase64 } from '@/core/utils/encoding';\nimport { warnIfClientSide } from '@/core/utils/warnings';\n\nimport {\n\tGrasshopperRequestSchema,\n\tGrasshopperComputeConfig,\n\tGrasshopperComputeResponse,\n\tDataTree\n} from './types';\n\n/**\n * The exact message the server throws when it can neither resolve a `pointer`\n * nor a base64 `algo` to a definition (ResthopperEndpoints.cs). This is the\n * signal that a cache-key pointer missed the server's definition cache (GC'd, or\n * a different child in the pool), so the caller should retry with the full\n * definition. Matched as a substring because the server wraps it with a category\n * prefix in its exception handler.\n */\nconst DEFINITION_LOAD_FAILED = 'Unable to load grasshopper definition';\n\n/** Does this error look like a server-side definition-load miss? */\nfunction isDefinitionLoadMiss(error: unknown): boolean {\n\treturn error instanceof RhinoComputeError && error.message.includes(DEFINITION_LOAD_FAILED);\n}\n\n/**\n * Result of a solve that also reports the definition's server-side cache key.\n *\n * `cacheKey` is the `md5_…` identifier the server assigned to the (base64)\n * definition — stable for identical content. A caller that holds it can solve\n * the same definition again by reference (`pointer: cacheKey`) instead of\n * re-uploading the full base64, which matters a lot for large (multi-MB)\n * definitions on a live UI. `null` when the server didn't return one (e.g. a\n * URL-pointer solve).\n */\nexport interface SolveWithCacheKey {\n\tresponse: GrasshopperComputeResponse;\n\tcacheKey: string | null;\n}\n\n/**\n * Runs a Rhino Compute job using the provided tree prototypes and Grasshopper definition.\n *\n * @public Use this for direct compute control. For high-level API, use `GrasshopperClient.solve()`.\n *\n * @param dataTree - An array of `DataTree` objects representing the input data for the compute job.\n * @param definition - The Grasshopper definition, which can be:\n * - A URL string (e.g., 'https://example.com/definition.gh')\n * - A base64-encoded string of the .gh file\n * - A plain string (will be base64-encoded)\n * - A Uint8Array of the .gh file (will be base64-encoded)\n * @param config - Compute configuration (server URL, API key, etc. along with optional timeout, units, etc.)\n * @returns An object containing the compute result and extracted file data.\n *\n * @example\n * // Using a URL\n * await solveGrasshopperDefinition(trees, 'https://example.com/definition.gh', config);\n *\n * // Using a base64 string\n * await solveGrasshopperDefinition(trees, 'UEsDBBQAAAAIAL...', config);\n *\n * // Using binary data\n * const fileData = new Uint8Array([...]);\n * await solveGrasshopperDefinition(trees, fileData, config);\n */\nexport async function solveGrasshopperDefinition(\n\tdataTree: DataTree[],\n\tdefinition: string | Uint8Array,\n\tconfig: GrasshopperComputeConfig\n): Promise<GrasshopperComputeResponse> {\n\tif (config.debug) {\n\t\twarnIfClientSide(\n\t\t\t'solveGrasshopperDefinition',\n\t\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t);\n\t}\n\n\tconst { response } = await runSolve(prepareGrasshopperArgs(definition, dataTree), config);\n\treturn response;\n}\n\n/**\n * Solve while reporting the server's definition cache key.\n *\n * Behaves like {@link solveGrasshopperDefinition} but returns the `cacheKey` the\n * server assigned, so a caller (e.g. the scheduler) can later solve the same\n * definition by reference instead of re-uploading it. The cache key is only\n * meaningful for base64/binary definitions; a URL-pointer solve returns the URL.\n *\n * @internal\n */\nexport async function solveGrasshopperDefinitionWithCacheKey(\n\tdataTree: DataTree[],\n\tdefinition: string | Uint8Array,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tif (config.debug) {\n\t\twarnIfClientSide(\n\t\t\t'solveGrasshopperDefinitionWithCacheKey',\n\t\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t);\n\t}\n\n\treturn runSolve(prepareGrasshopperArgs(definition, dataTree), config);\n}\n\n/**\n * Solve a definition by its server-side cache key (`pointer: cacheKey`),\n * skipping the (potentially multi-MB) base64 upload. If the key has been evicted\n * from the server's definition cache — `DEFINITION_LOAD_FAILED` — transparently\n * retry once with the full `definition` and report the fresh cache key so the\n * caller can update its mapping.\n *\n * @returns The solve result plus the (possibly refreshed) cache key, and whether\n * the fast path missed (so callers can record the new key / track hit rate).\n * @internal\n */\nexport async function solveByCacheKey(\n\tdataTree: DataTree[],\n\tcacheKey: string,\n\tdefinition: string | Uint8Array,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey & { missed: boolean }> {\n\tif (config.debug) {\n\t\twarnIfClientSide(\n\t\t\t'solveByCacheKey',\n\t\t\tconfig.suppressBrowserWarning ?? config.suppressClientSideWarning\n\t\t);\n\t}\n\n\tconst pointerArgs: GrasshopperRequestSchema = { algo: null, pointer: cacheKey, values: dataTree };\n\n\ttry {\n\t\tconst fast = await runSolve(pointerArgs, config);\n\t\treturn { ...fast, missed: false };\n\t} catch (error) {\n\t\tif (!isDefinitionLoadMiss(error)) throw error;\n\t\t// Cache miss — fall back to the full upload and capture the fresh key.\n\t\tconst full = await runSolve(prepareGrasshopperArgs(definition, dataTree), config);\n\t\treturn { ...full, missed: true };\n\t}\n}\n\n/**\n * Shared solve body: apply optional settings, POST, and split the server's\n * `pointer` (its cache key) off the response. Stripping via shallow copy rather\n * than `delete` keeps any already-observed response object unmutated.\n */\nasync function runSolve(\n\targs: GrasshopperRequestSchema,\n\tconfig: GrasshopperComputeConfig\n): Promise<SolveWithCacheKey> {\n\tapplyOptionalComputeSettings(args, config);\n\n\tconst result = await fetchRhinoCompute<GrasshopperComputeResponse>('grasshopper', args, config);\n\n\tif ('pointer' in result) {\n\t\tconst { pointer, ...rest } = result as GrasshopperComputeResponse & { pointer?: unknown };\n\t\treturn {\n\t\t\tresponse: rest as GrasshopperComputeResponse,\n\t\t\tcacheKey: typeof pointer === 'string' ? pointer : null\n\t\t};\n\t}\n\n\treturn { response: result, cacheKey: null };\n}\n\n// ============================================================================\n// Grasshopper Arguments\n// ============================================================================\n\n/**\n * Prepares Grasshopper arguments from a definition and data tree.\n * Automatically detects the definition format and converts it appropriately.\n *\n * @param definition - Can be a URL, base64 string, plain string, or Uint8Array\n * @param dataTree - Array of DataTree objects for compute inputs\n * @internal\n */\nexport function prepareGrasshopperArgs(\n\tdefinition: string | Uint8Array,\n\tdataTree: DataTree[]\n): GrasshopperRequestSchema {\n\tconst args: GrasshopperRequestSchema = {\n\t\talgo: null,\n\t\tpointer: null,\n\t\tvalues: dataTree\n\t};\n\n\tif (definition instanceof Uint8Array) {\n\t\t// Binary data → convert to base64\n\t\targs.algo = base64ByteArray(definition);\n\t} else if (/^https?:\\/\\//i.test(definition)) {\n\t\t// URL → use as pointer reference\n\t\targs.pointer = definition;\n\t} else if (isBase64(definition)) {\n\t\t// Already base64 → use as-is\n\t\targs.algo = definition;\n\t} else {\n\t\t// Plain string → encode to base64\n\t\targs.algo = encodeStringToBase64(definition);\n\t}\n\n\treturn args;\n}\n\n/**\n * @internal\n */\nexport function applyOptionalComputeSettings(\n\targlist: GrasshopperRequestSchema,\n\toptions: GrasshopperComputeConfig\n): void {\n\tif (options.cachesolve != null) arglist.cachesolve = options.cachesolve;\n\tif (options.modelunits != null) arglist.modelunits = options.modelunits;\n\tif (options.angletolerance != null) arglist.angletolerance = options.angletolerance;\n\tif (options.absolutetolerance != null) arglist.absolutetolerance = options.absolutetolerance;\n\tif (options.dataversion != null) arglist.dataversion = options.dataversion;\n}\n","/**\n * Stable hashing for solve deduplication and caching.\n * @internal\n */\n\n/**\n * Deterministic stringify with sorted keys. {a:1,b:2} and {b:2,a:1} produce\n * the same string. Safely handles circular references and non-finite numbers.\n */\nexport function stableStringify(value: unknown): string {\n\tconst seen = new WeakSet<object>();\n\n\tconst stringify = (v: unknown): string => {\n\t\tif (v === null || v === undefined) return JSON.stringify(v);\n\t\tif (typeof v === 'number') {\n\t\t\treturn Number.isFinite(v) ? String(v) : JSON.stringify(null);\n\t\t}\n\t\tif (typeof v === 'string' || typeof v === 'boolean') return JSON.stringify(v);\n\t\tif (typeof v === 'bigint') return JSON.stringify(v.toString());\n\t\tif (v instanceof Uint8Array) {\n\t\t\t// Use length + sample instead of full buffer to avoid stringifying large data\n\t\t\tconst sample =\n\t\t\t\tv.length > 64 ? Array.from(v.slice(0, 32)).concat(Array.from(v.slice(-32))) : Array.from(v);\n\t\t\treturn JSON.stringify({ __u8: true, len: v.length, sample });\n\t\t}\n\t\tif (Array.isArray(v)) {\n\t\t\treturn `[${v.map(stringify).join(',')}]`;\n\t\t}\n\t\tif (typeof v === 'object') {\n\t\t\tif (seen.has(v as object)) return JSON.stringify('[Circular]');\n\t\t\tseen.add(v as object);\n\t\t\tconst keys = Object.keys(v as object).sort();\n\t\t\tconst parts = keys.map((k) => `${JSON.stringify(k)}:${stringify((v as any)[k])}`);\n\t\t\treturn `{${parts.join(',')}}`;\n\t\t}\n\t\t// Fallback for functions, symbols, etc.\n\t\treturn JSON.stringify(null);\n\t};\n\n\treturn stringify(value);\n}\n\n/**\n * 32-bit FNV-1a core over a sequence of byte/char codes. Returns unsigned hex.\n * Shared by the string and byte hashers so they stay the same algorithm.\n */\nfunction fnv1aCore(length: number, codeAt: (i: number) => number): string {\n\tlet hash = 0x811c9dc5;\n\tfor (let i = 0; i < length; i++) {\n\t\thash ^= codeAt(i);\n\t\thash = (hash + ((hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 24))) >>> 0;\n\t}\n\treturn hash.toString(16).padStart(8, '0');\n}\n\n/**\n * 32-bit FNV-1a— fast, no dependencies. Returns unsigned hex string.\n */\nexport function fnv1a(input: string): string {\n\treturn fnv1aCore(input.length, (i) => input.charCodeAt(i));\n}\n\n/**\n * 32-bit FNV-1a over raw bytes. Returns unsigned hex string.\n */\nexport function fnv1aBytes(bytes: Uint8Array): string {\n\treturn fnv1aCore(bytes.length, (i) => bytes[i]);\n}\n\n/**\n * Hash definition and data tree into a stable cache key.\n *\n * The definition is the *identity* of what we solve, so a binary definition is\n * hashed over its full content (`fnv1aBytes`) — a length-only or sampled key\n * would let two different `.gh` files collide and serve one's cached solve for\n * the other. `.gh` files are small enough that a single linear pass is\n * negligible. (Note this differs from `stableStringify`'s sampled handling of a\n * `Uint8Array` found *inside* the dataTree, where sampling is a deliberate\n * per-solve perf tradeoff.)\n */\nexport function hashSolveInput(definition: string | Uint8Array, dataTree: unknown): string {\n\treturn fnv1a(`${hashDefinition(definition)}|${stableStringify(dataTree)}`);\n}\n\n/**\n * Stable identity of a definition alone (no inputs) — used to key the\n * server-cache-key map so the same definition reuses its `pointer` across solves\n * with different inputs. Same full-content hashing as {@link hashSolveInput}: a\n * binary definition is hashed over all its bytes so two distinct `.gh` files of\n * equal length can't share a cache key.\n */\nexport function hashDefinition(definition: string | Uint8Array): string {\n\treturn typeof definition === 'string'\n\t\t? definition\n\t\t: `u8:${definition.length}:${fnv1aBytes(definition)}`;\n}\n"]}
|