openalgo-charts
Version:
From-scratch, dependency-free canvas charting engine for OpenAlgo: professional-grade interactive rendering, advanced on-chart trading, and a one-call widget tier that adds the toolbar, drawing rail, dialogs and shortcuts.
3 lines (2 loc) • 44.8 kB
JavaScript
import{parseIndicatorPolicy as e,parsePaneState as t,normalizeDataVariant as n,parseAlertsDocument as r,getIndicator as a,registeredIndicators as o}from"./openalgo-charts.mjs";function i(e){return"string"==typeof e&&("right"===e||"left"===e||""===e||e.startsWith("overlay:"))}const s=5242880;class c extends Error{constructor(e){super(e),this.name="WorkspaceDocumentError"}}const l=new Set(["__proto__","prototype","constructor","apikey","secret","clientsecret","apisecret","password","passwordhash","token","accesstoken","refreshtoken","authtoken","authorization","credentials","credential","auth","armed","oneclick","orders","positions","balances","balance","accountbalance","accountid","account"]);function d(e){if("string"==typeof e){if(e.length>s||(new TextEncoder).encode(e).length>s)throw new c("Document size limit exceeded");try{e=JSON.parse(e)}catch{throw new c("Invalid document JSON")}}let t=0,n=0;const r=new Set,a=(e,o,d=!1,p=!1)=>{if(++t>1e5)throw new c("Document node limit exceeded");if(o>32)throw new c("Document depth limit exceeded");if(null===e||"boolean"==typeof e)return e;if("string"==typeof e){if(n+=e.length,n>s)throw new c("Document size limit exceeded");return e}if("number"==typeof e&&Number.isFinite(e))return e;if("object"!=typeof e)throw new c("Document values must be finite JSON data");if(r.has(e))throw new c("Document contains a cycle");if(!Array.isArray(e)&&![Object.prototype,null].includes(Object.getPrototypeOf(e)))throw new c("Document objects must be plain records");if(d||p)for(const t of Reflect.ownKeys(e)){if(Array.isArray(e)&&"length"===t)continue;const n=Object.getOwnPropertyDescriptor(e,t);if("string"!=typeof t||!n.enumerable||Array.isArray(e)&&(!/^(0|[1-9]\d*)$/.test(t)||Number(t)>=e.length))throw new c((p?"Plot scale map":"Opaque payload")+" properties must survive JSON")}let u;if(r.add(e),Array.isArray(e)){if(e.length>1e5)throw new c("Document array limit exceeded");const t=[];for(let n=0;n<e.length;n++){const r=Object.getOwnPropertyDescriptor(e,String(n));if(!r||!("value"in r))throw new c("Document arrays must contain data values");t.push(a(r.value,o+1,d))}u=t}else{const t={},r=Object.getOwnPropertyDescriptor(e,"settings")?.value,f=!d&&"string"==typeof Object.getOwnPropertyDescriptor(e,"indicatorId")?.value&&"number"==typeof Object.getOwnPropertyDescriptor(e,"paneIndex")?.value&&null!==r&&"object"==typeof r&&!Array.isArray(r);for(const r of Object.keys(e)){const u=r.toLowerCase().replace(/[^a-z0-9_]/g,"").replace(/_/g,"");if(!p&&(l.has(r)||l.has(u))){if(d)throw new c("Opaque payload contains a private workspace field");continue}if(n+=r.length,n>s)throw new c("Document size limit exceeded");const h=Object.getOwnPropertyDescriptor(e,r);if(!("value"in h))throw new c("Document accessors are not allowed");if(p&&!i(h.value))throw new c("Invalid indicator plot priceScaleId");const m=a(h.value,o+1,d||"payload"===r,f&&"plotPriceScaleIds"===r);p?Object.defineProperty(t,r,{value:m,enumerable:!0,configurable:!0,writable:!0}):t[r]=m}u=t}return r.delete(e),u},o=a(e,0);if((new TextEncoder).encode(JSON.stringify(o)).length>s)throw new c("Document size limit exceeded");return o}function p(e,t){if(null===e||"object"!=typeof e||Array.isArray(e))throw new c(`${t} must be an object`);return e}function u(e,t,n){if(!Array.isArray(e)||e.length>n)throw new c(`${t} must be an array within the ${n} item limit`);return e}function f(e,t,n=200,r=!1){if("string"!=typeof e||!r&&0===e.trim().length||e.length>n||Array.from(e).some(e=>e.charCodeAt(0)<32))throw new c(`${t} must be ${r?"0":"1"}-${n} characters`);return e.trim()}function h(e,t,n,r=Number.MAX_SAFE_INTEGER,a=!1){if("number"!=typeof e||!Number.isFinite(e)||e<n||e>r||a&&!Number.isInteger(e))throw new c(`${t} must be ${a?"an integer":"a number"} between ${n} and ${r}`);return e}function m(e,t,n){if(void 0===e&&void 0!==n)return n;if("boolean"!=typeof e)throw new c(`${t} must be a boolean`);return e}function w(e,t,n,r){if(void 0===e&&void 0!==r)return r;if("string"!=typeof e||!n.includes(e))throw new c(`${t} is not supported`);return e}function g(e){return!1===e?.removable||!1===e?.listed}const y=1,v=31,I=256;function b(e,t){if(e.kind!==t||1!==e.version)throw new c(`Unsupported ${t} kind or version`);const n=h(e.createdAt,"createdAt",0);return{version:1,id:f(e.id,"id",100),name:f(e.name,"name",120),createdAt:n,updatedAt:h(e.updatedAt,"updatedAt",n)}}function _(e,t){if(!i(e))throw new c(`Invalid ${t}`);return e}function S(t,n=!0,r=!0){const a=new Set;return u(t,"indicators",I).map(t=>{const o=p(t,"indicator"),i=p(o.settings,"indicator settings"),s={indicatorId:f(o.indicatorId,"indicatorId"),settings:i,paneIndex:h(o.paneIndex,"indicator paneIndex",0,v,!0)};if(void 0!==o.studyInputs){const e=u(o.studyInputs,"indicator studyInputs",1e5).map(e=>{if("string"!=typeof e||!e.trim())throw new c("Invalid study input key");const t=p(i[e],"study input reference");if("indicator"!==t.kind||3!==Object.keys(t).length)throw new c("Invalid study input reference");const n=f(t.instanceId,"study input instanceId");if("string"!=typeof t.plotKey||!t.plotKey.trim())throw new c("Invalid study input plotKey");return i[e]={kind:"indicator",instanceId:n,plotKey:t.plotKey},e});if(new Set(e).size!==e.length)throw new c("Duplicate study input key");s.studyInputs=e}if(void 0!==o.visible&&(s.visible=m(o.visible,"indicator visibility")),void 0!==o.barSource&&(s.barSource=w(o.barSource,"indicator bar source",["chart","underlying"])),r&&void 0!==o.policy){let t;try{t=e(o.policy)}catch{throw new c("Invalid indicator policy")}Object.keys(t).length&&(s.policy={...t})}if(void 0!==o.priceScaleId&&(s.priceScaleId=_(o.priceScaleId,"indicator priceScaleId")),void 0!==o.plotPriceScaleIds){const e=p(o.plotPriceScaleIds,"indicator plotPriceScaleIds"),t=Object.entries(e).map(([e,t])=>[e,_(t,"indicator plot priceScaleId")]);t.length&&(s.plotPriceScaleIds=Object.fromEntries(t))}if(n&&void 0!==o.instanceId){if(s.instanceId=f(o.instanceId,"indicator instanceId"),a.has(s.instanceId))throw new c("Duplicate indicator instance ID");a.add(s.instanceId)}return s})}function A(e){return S(d(e))}function k(e){const t=!0===e.indicatorRange?.manual;delete e.indicatorRange,delete e.fixedRange,t||(e.autoScale=!0,delete e.range,delete e.ratioLock)}function x(t,n=!1,r){const a=function(t){const n=t.map(e=>p(e,"indicator")),r=n.map(t=>{if(void 0===t.policy)return!1;try{return g(e(t.policy))}catch{throw new c("Invalid indicator policy")}}),a=new Set,o=e=>{const t=e.settings,n=Array.isArray(e.studyInputs)?e.studyInputs:[];return null!==t&&"object"==typeof t&&!Array.isArray(t)&&n.some(e=>{const n="string"==typeof e?t[e]:void 0;return null!==n&&"object"==typeof n&&!Array.isArray(n)&&"string"==typeof n.instanceId&&a.has(n.instanceId)})};for(let e=!0;e;)e=!1,n.forEach((t,n)=>{!r[n]&&o(t)&&(r[n]=e=!0),r[n]&&"string"==typeof t.instanceId&&!a.has(t.instanceId)&&(a.add(t.instanceId),e=!0)});return{kept:t.filter((e,t)=>!r[t]),left:a}}(u(t,"indicators",I)),o=a.kept;for(const e of a.left)r?.add(e);const i=o.some(e=>{const t=p(e,"indicator").studyInputs;return Array.isArray(t)&&t.length>0}),s=S(o,i||n,!1);if(n&&s.some(e=>void 0===e.instanceId))throw new c("Template layout requires every indicator instance ID");if(!i)return s;const l=new Map(s.flatMap((e,t)=>void 0===e.instanceId?[]:[[e.instanceId,t]])),d=s.map((e,t)=>(e.studyInputs??[]).map(n=>{const r=e.settings[n],a=l.get(r.instanceId);if(void 0===a)throw new c(`External study dependency: ${r.instanceId}`);if(a===t)throw new c("A study cannot depend on itself");return a})),f=new Set,h=new Set,m=e=>{if(f.has(e))throw new c("Study dependencies contain a cycle");if(!h.has(e)){f.add(e);for(const t of d[e])m(t);f.delete(e),h.add(e)}};for(let e=0;e<s.length;e++)m(e);return s}function O(e){return u(e,"chart panes",32).map(e=>{try{const n=t(e);h(n.weight,"pane weight",Number.MIN_VALUE);for(const e of[n.priceScale,...Object.values(n.scales??{})])if(e){if(h(e.marginTop,"marginTop",0,1),h(e.marginBottom,"marginBottom",0,1),h(e.minMove,"minMove",0),e.marginTop+e.marginBottom>=1)throw new c("Price scale margins leave no chart area");for(const t of[e.range,e.fixedRange])if(t&&(h(t.min,"range.min",-Number.MAX_SAFE_INTEGER),h(t.max,"range.max",t.min),t.max===t.min))throw new c("Price scale range must have positive width")}return n}catch(e){throw new c(e instanceof Error?e.message:"Invalid pane scale state")}})}function D(e){if(Array.isArray(e))return{indicators:x(e)};const t=p(e,"indicator template payload"),n=new Set,r=x(t.indicators,void 0!==t.layout,n);if(void 0===t.layout)return{indicators:r};const a=p(t.layout,"indicator template layout"),o=O(a.panes);if(!o.length)throw new c("Template layout needs pane zero");const i=new Map(r.map(e=>[e.instanceId,e]));for(const e of r)if(e.paneIndex>=o.length)throw new c("Template indicator pane is missing");for(const e of o)for(const t of[e.priceScale,...Object.values(e.scales??{})]){const e=t?.indicatorRange;if(t&&e&&n.has(e.instanceId))k(t);else if(t?.indicatorRange&&!i.has(t.indicatorRange.instanceId))throw new c("Template scale range owner is missing")}const s=(e,t)=>{if("right"!==t&&!o[e].scales?.[t])throw new c("Template plot or primary scale is missing")},l=new Map,d=u(a.plots,"template plot bindings",1e5).filter(e=>{const t=p(e,"template plot binding").instanceId;return"string"!=typeof t||!n.has(t)}).map(e=>{const t=p(e,"template plot binding"),n=f(t.instanceId,"template plot instanceId"),r=i.get(n);if(!r)throw new c("Template plot indicator is missing");if("string"!=typeof t.plotKey)throw new c("Template plot key must be a string");const a=t.plotKey,d=h(t.paneIndex,"template plot paneIndex",0,o.length-1,!0);if(0!==d&&d!==r.paneIndex)throw new c("Template plot belongs to another pane");const u=_(t.scaleId,"template plot scaleId");s(d,u);const m=l.get(n)??new Map;if(m.has(a))throw new c("Duplicate template plot binding");return m.set(a,u),l.set(n,m),{instanceId:n,plotKey:a,paneIndex:d,scaleId:u}});for(const e of r)for(const[t,n]of Object.entries(e.plotPriceScaleIds??{}))if(l.get(e.instanceId)?.get(t)!==n)throw new c("Template plot override needs a matching scale binding");const m={indicators:r,layout:{panes:o,plots:d}};if(void 0!==a.primaryScaleId){const e=_(a.primaryScaleId,"template primaryScaleId");s(0,e),m.layout.primaryScaleId=e}return m}function E(e){return D(d(e))}function P(e){const t=p(e,"chart");if(1!==t.version&&2!==t.version)throw new c("Unsupported chart version");const n={version:2===t.version?2:1};void 0!==t.timezone&&(n.timezone=f(t.timezone,"timezone",100));for(const e of["navigation","canvas","statusLine","watermark","trading","events","axisChrome"])void 0!==t[e]&&Object.assign(n,{[e]:p(t[e],e)});if(void 0!==t.viewport){const e=p(t.viewport,"viewport"),r=h(e.from,"viewport.from",-Number.MAX_SAFE_INTEGER);n.viewport={from:r,to:h(e.to,"viewport.to",r)}}if(void 0!==t.barSpacing&&(n.barSpacing=h(t.barSpacing,"barSpacing",Number.MIN_VALUE)),void 0!==t.grid){const e=p(t.grid,"grid");n.grid={vertLines:m(e.vertLines,"vertical grid"),horzLines:m(e.horzLines,"horizontal grid")}}if(void 0!==t.crosshairMode&&(n.crosshairMode=w(t.crosshairMode,"crosshairMode",["normal","magnet"])),void 0!==t.crosshairSnapToBar&&(n.crosshairSnapToBar=m(t.crosshairSnapToBar,"crosshairSnapToBar")),void 0!==t.priceOnlyAutoScale&&(n.priceOnlyAutoScale=m(t.priceOnlyAutoScale,"priceOnlyAutoScale")),void 0!==t.indicatorLegendCollapsed&&(n.indicatorLegendCollapsed=m(t.indicatorLegendCollapsed,"indicatorLegendCollapsed")),void 0!==t.indicators&&(n.indicators=S(t.indicators)),void 0!==t.sourceAbove&&(n.sourceAbove=f(t.sourceAbove,"sourceAbove")),void 0!==t.alerts)try{n.alerts=r(t.alerts)}catch(e){throw new c(e instanceof Error?e.message:"Invalid alert document")}if(void 0!==t.drawings){if(null===t.drawings||"object"!=typeof t.drawings)throw new c("drawings must be a document or array");n.drawings=t.drawings}if(void 0!==t.panes&&(n.panes=O(t.panes)),void 0!==t.primaryPane){if(2!==t.version)throw new c("A moved price pane needs chart version 2");if(n.primaryPane=h(t.primaryPane,"primaryPane",0,v,!0),!n.panes||n.primaryPane>=n.panes.length)throw new c("The price pane slot must name a saved pane")}return void 0!==t.series&&(n.series=u(t.series,"series descriptors",512).map(e=>{const t=p(e,"series"),n=t.priceScaleId;if("string"!=typeof n)throw new c("priceScaleId must be a string");const r={type:f(t.type,"series type"),style:p(t.style,"series style"),paneIndex:h(t.paneIndex,"series paneIndex",0,v,!0),priceScaleId:n};return void 0!==t.transform&&(r.transform=function(e){const t=p(e,"series transform"),n={type:f(t.type,"series transform type",100)};if(void 0===t.options)return n;const r=p(t.options,"series transform options");for(const e of Object.values(r))if("string"!=typeof e&&("number"!=typeof e||!Number.isFinite(e)))throw new c("Series transform options must be finite numbers or text");return n.options=r,n}(t.transform)),r})),n}function T(e){const t=p(e,"workspace pane"),r=p(t.settings??{},"settings");for(const e of Object.values(r))if(!["string","number","boolean"].includes(typeof e))throw new c("Chart settings must be scalar values");const a=u(t.comparisons??[],"comparisons",32).map(e=>{const t=p(e,"comparison"),n={id:f(t.id,"comparison id",100),symbol:f(t.symbol,"comparison symbol"),exchange:f(t.exchange,"comparison exchange",200,!0),visible:m(t.visible,"comparison visibility",!0)};return void 0!==t.color&&(n.color=f(t.color,"comparison color",200)),n});if(new Set(a.map(e=>e.id)).size!==a.length)throw new c("Duplicate comparison ID");const o={id:f(t.id,"pane id",100),symbol:f(t.symbol,"symbol"),exchange:f(t.exchange,"exchange",200,!0),interval:f(t.interval,"interval",100),chartType:f(t.chartType,"chartType",100),chart:P(t.chart),settings:r,volume:m(t.volume,"volume",!0),magnet:w(t.magnet,"magnet",["off","weak","strong"],"off"),stay:m(t.stay,"stay",!1),comparisons:a,comparisonMode:w(t.comparisonMode,"comparisonMode",["price","percent"],"percent")};if(void 0!==t.historyPeriod&&(o.historyPeriod=f(t.historyPeriod,"historyPeriod",100)),void 0!==t.variant){let e;try{e=n(t.variant)}catch(e){throw new c(e instanceof Error?e.message:"Invalid data variant")}e&&(o.variant={...e})}return void 0!==t.linkGroup&&(o.linkGroup=f(t.linkGroup,"linkGroup",100)),o}function j(e,t){const n={crosshair:m(e.crosshair,`${t}crosshair sync`,!0),viewport:m(e.viewport,`${t}viewport sync`,!0),symbol:m(e.symbol,`${t}symbol sync`,!1),interval:m(e.interval,`${t}interval sync`,!1)};for(const r of["appearance","chartType","drawings"])void 0!==e[r]&&(n[r]=m(e[r],`${t}${r} sync`));return void 0!==e.whenMissing&&(n.whenMissing=w(e.whenMissing,`${t}whenMissing`,["nearest","hide"])),n}function M(e){const t=u(e.panes,"panes",16).map(T);if(0===t.length)throw new c("A workspace needs at least one pane");const n=new Set(t.map(e=>e.id));if(n.size!==t.length)throw new c("Duplicate pane ID");const r=f(e.activePaneId,"activePaneId",100);if(!n.has(r))throw new c("Focused pane is missing");const a=p(e.layout,"layout"),o=h(a.rows,"rows",1,8,!0),i=h(a.columns,"columns",1,8,!0),s=new Set,l=new Set,d=u(a.slots,"layout slots",16).map(e=>{const t=p(e,"slot"),r={paneId:f(t.paneId,"slot paneId",100),row:h(t.row,"row",0,o-1,!0),column:h(t.column,"column",0,i-1,!0),rowSpan:h(t.rowSpan??1,"rowSpan",1,o,!0),columnSpan:h(t.columnSpan??1,"columnSpan",1,i,!0)};if(!n.has(r.paneId)||s.has(r.paneId))throw new c("Invalid or duplicate layout pane ID");if(s.add(r.paneId),r.row+r.rowSpan>o||r.column+r.columnSpan>i)throw new c("Layout slot is outside the grid");for(let e=r.row;e<r.row+r.rowSpan;e++)for(let t=r.column;t<r.column+r.columnSpan;t++){const n=e*i+t;if(l.has(n))throw new c("Layout slots overlap");l.add(n)}return r});if(s.size!==t.length)throw new c("Every pane needs one layout slot");const m=p(e.sync??{},"sync"),w={layout:{rows:o,columns:i,slots:d},panes:t,activePaneId:r,sync:j(m,"")};void 0!==m.groups&&(w.sync.groups=function(e){const t=new Set;return u(e,"link groups",16).map(e=>{const n=p(e,"link group"),r=f(n.id,"link group id",100);if(t.has(r))throw new c("Duplicate link group ID");return t.add(r),{id:r,name:f(n.name,"link group name",120),...j(n,"link group ")}})}(m.groups));const g=new Set(w.sync.groups?.map(e=>e.id));for(const e of t)if(void 0!==e.linkGroup&&!g.has(e.linkGroup))throw new c(`Link group ${e.linkGroup} is not declared`);void 0!==a.preset&&(w.layout.preset=f(a.preset,"layout preset",100));for(const[e,t]of[["rowWeights",o],["columnWeights",i]]){if(void 0===a[e])continue;const n=u(a[e],e,8).map(t=>h(t,e,Number.MIN_VALUE,1e3));if(n.length!==t)throw new c(`${e} must match the number of grid tracks`);w.layout[e]=n}return w}function $(e){const t=p(d(e),"workspace");return{kind:"workspace",...b(t,"workspace"),...M(t)}}function N(e){return M(p(d(e),"workspace"))}function L(e){const t=p(d(e),"indicator template");return{kind:"indicator-template",...b(t,"indicator-template"),...D(t)}}function R(e,t){const n=p(d(e),"widget");if(1!==n.version||void 0!==n.kind)throw new c("Unsupported widget state");const r=null===n.rail?{}:p(n.rail??{},"rail");return $({kind:"workspace",version:1,id:t.id,name:t.name,createdAt:t.now,updatedAt:t.now,panes:[{id:"p0",symbol:n.symbol,exchange:n.exchange??"",interval:n.interval,chartType:n.chartType,chart:n.chart,magnet:r.magnet??"off",stay:r.stay??!1,...void 0===n.variant?{}:{variant:n.variant},settings:{"widget.theme":w(n.theme,"theme",["light","dark"],"dark")}}],layout:{rows:1,columns:1,slots:[{paneId:"p0",row:0,column:0,rowSpan:1,columnSpan:1}]},activePaneId:"p0"})}const z=e=>d(e);function W(){if(!globalThis.crypto?.randomUUID)throw new c("Supply an ID factory when crypto.randomUUID is unavailable");return globalThis.crypto.randomUUID()}class C{constructor(e,t,n,r){Object.defineProperty(this,"_storage",{enumerable:!0,configurable:!0,writable:!0,value:e}),Object.defineProperty(this,"_kind",{enumerable:!0,configurable:!0,writable:!0,value:n}),Object.defineProperty(this,"_empty",{enumerable:!0,configurable:!0,writable:!0,value:r}),Object.defineProperty(this,"namespace",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_listeners",{enumerable:!0,configurable:!0,writable:!0,value:new Set}),Object.defineProperty(this,"_queue",{enumerable:!0,configurable:!0,writable:!0,value:Promise.resolve()}),this.namespace=f(t,"storage namespace")}async load(){return await this._queue,this._read()}subscribe(e){return this._listeners.add(e),()=>{this._listeners.delete(e)}}transact(e,t={}){const{signal:n}=t,r=void 0===t.expectedRevision?void 0:h(t.expectedRevision,"expected revision",0,Number.MAX_SAFE_INTEGER,!0),a=this._queue.then(async()=>{n?.throwIfAborted();const t=await this._read();if(n?.throwIfAborted(),void 0!==r&&t.revision!==r)throw this._kind.conflict();const a=t.revision,o=e(t);t.revision++;const i=this._kind.parse(t);n?.throwIfAborted(),await this._storage.write(this.namespace,i,a,{signal:n});for(const e of Array.from(this._listeners))try{e(z(i))}catch(e){queueMicrotask(()=>{throw e})}return void 0===o?o:z(o)});return this._queue=a.then(()=>{},()=>{}),a}async _read(){const e=await this._storage.read(this.namespace);return null===e?this._empty():this._kind.parse(e)}}function G(e,t){const n=new Map(Object.entries(e).map(([e,t])=>[e,z(t)]));return{async read(e){const t=n.get(f(e,"storage namespace"));return void 0===t?null:z(t)},async write(e,r,a,o){o?.signal?.throwIfAborted();const i=f(e,"storage namespace"),s=h(a,"expected revision",0,Number.MAX_SAFE_INTEGER,!0),l=t.parse(r);if(l.revision!==s+1)throw new c("A write must advance the catalog revision by one");const d=n.get(i);if((void 0===d?0:t.parse(d).revision)!==s)throw t.conflict();n.set(i,l)}}}function U(e,t,n,r,a,o=0){if("replace"!==n&&"append"!==n)throw new c("Unsupported indicator template mode");if(!Number.isInteger(o)||o<0||o>v)throw new c("Invalid price pane slot");const i=A(e),s=x(d(t));if(s.some(e=>e.studyInputs?.length)&&q(i,s),"append"===n){const e=Math.max(0,...i.map(e=>e.paneIndex));if(!Number.isInteger(a)||a<=Math.max(e,o)||a>32)throw new c("Invalid next indicator pane");const t=[...new Set(s.map(e=>e.paneIndex).filter(e=>e>0))].sort((e,t)=>e-t);for(const e of s)if(e.paneIndex=e.paneIndex>0?a+t.indexOf(e.paneIndex):o,e.paneIndex>v)throw new c("Indicator pane limit exceeded")}else if(o>0)for(const e of s)e.paneIndex=X(e.paneIndex,o);const l="replace"===n?i.filter(e=>g(e.policy)):[],p=new Set(l.map(e=>e.paneIndex).filter(e=>e!==o));if(p.size){const e=[...new Set(s.map(e=>e.paneIndex).filter(e=>e!==o))].sort((e,t)=>e-t),t=new Map;let n=-1;for(const r of e){do{n++}while(p.has(n)||n===o);t.set(r,n)}for(const e of s)e.paneIndex=t.get(e.paneIndex)??e.paneIndex;if(n>v)throw new c("Indicator pane limit exceeded")}const u="replace"===n?[...l,...s]:[...i,...s];return F(u,r),u}function F(e,t){if(e.length>I)throw new c("At most 256 indicator instances are supported");const n=[...new Set(e.filter(e=>!t.has(e.indicatorId)).map(e=>e.indicatorId))];if(n.length)throw new c(`Missing indicators: ${n.join(", ")}`)}function K(e,t){return e===t?0:e<t?e+1:e}function X(e,t){return 0===e?t:e<=t?e-1:e}function q(e,t){const n=new Map;if(!t.length)return n;const r=new Set([...e,...t].flatMap(e=>void 0===e.instanceId?[]:[e.instanceId]));for(const t of e)for(const e of t.studyInputs??[])r.add(t.settings[e].instanceId);if(!globalThis.crypto?.getRandomValues)throw new c("Random IDs are unavailable for connected template copies");const a=Array.from(globalThis.crypto.getRandomValues(new Uint32Array(4)),e=>e.toString(16).padStart(8,"0")).join("");let o=1;for(const e of t){let t;do{t=`template-study-${a}-${o++}`}while(r.has(t));r.add(t),void 0!==e.instanceId&&n.set(e.instanceId,t),e.instanceId=t}for(const e of t)for(const t of e.studyInputs??[]){const r=e.settings[t];e.settings[t]={...r,instanceId:n.get(r.instanceId)}}return n}function B(e){return[["right",e.priceScale],...Object.entries(e.scales??{})]}function J(e,t){return"right"===t?e.priceScale:e.scales?.[t]}function V(e,t,n){const r=e.primarySeries();if(!r)return;const a=r.priceScale();return B(n).find(([n])=>e.panes()[t].scaleFor(n)===a)?.[0]}function Z(e,t){return[e[t],...e.filter((e,n)=>n!==t)]}function H(e,t){const n=[];return e.forEach((e,r)=>{n[X(r,t)]=e}),n}function Q(e){const t=e.getState(),n=t.panes;if(!n?.length)throw new c("Cannot capture a destroyed chart");const r=e.primaryPaneIndex(),o=Z(n,r),i=[];for(const t of e.indicators())for(const n of a(t.indicatorId).plots){const a=t.series(n.key),o=t.plotPriceScaleId(n.key);if(!a||null===e.seriesType(a)||null===o)throw new c("Cannot capture a removed indicator plot");i.push({instanceId:t.id,plotKey:n.key,paneIndex:!0===n.overlay?0:K(t.paneIndex,r),scaleId:o})}const s={panes:o,plots:i},l=V(e,r,o[0]);void 0!==l&&(s.primaryScaleId=l);return E({indicators:(t.indicators??[]).map(e=>({...e,paneIndex:K(e.paneIndex,r)})),layout:s})}function Y(e,t,n){const r=e.plots.find(e=>!0!==e.overlay),a=r?n.find(e=>e.plotKey===r.key).scaleId:t.priceScaleId??"right";for(const r of e.fills??[]){const e=!0===r.overlay?0:t.paneIndex,o=r.between.map(t=>n.find(e=>e.plotKey===t)??{paneIndex:e,scaleId:!0===r.overlay?"right":a});if(o.some(t=>t.paneIndex!==e)||o[0].scaleId!==o[1].scaleId)throw new c("Template fill endpoints must share their pane and scale")}}function ee(e,n,r){const a=t({weight:1,priceScale:e}).priceScale;return r?a.indicatorRange&&(a.indicatorRange.instanceId=n.get(a.indicatorRange.instanceId)):(a.autoScale=!0,delete a.range,delete a.ratioLock,a.indicatorRange&&(delete a.indicatorRange,delete a.fixedRange)),a}function te(e,t){for(const n of e)for(const[,e]of B(n)){const n=e.indicatorRange;n&&t.has(n.instanceId)&&k(e)}}function ne(e,n,r,i={}){if("replace"!==r&&"append"!==r)throw new c("Unsupported indicator template mode");const s=e.primaryPaneIndex(),l=function(e,n,r,i,s){const l=p(d(i),"template apply options"),u=w(l.scalePolicy,"scale policy",["copy","share"],"copy"),f="preserve"===w(l.rangePolicy,"range policy",["auto","preserve"],"auto"),h=E(n),m=e.getState();if(!m.panes?.length)throw new c("Cannot plan for a destroyed chart");const y={...m,panes:Z(m.panes,s),indicators:(m.indicators??[]).map(e=>({...e,paneIndex:K(e.paneIndex,s)}))},I=new Set(o().map(e=>e.id));if(!h.layout)return{indicators:U(y.indicators,h.indicators,r,I,y.panes.length)};const b=h.layout,_=h.indicators,S=A(y.indicators??[]),k="replace"===r?S.filter(e=>g(e.policy)):[],x="append"===r?[...S,..._]:[...k,..._];F(x,I);const O=function(e,t){const n=new Map;for(const e of t.plots){const t=n.get(e.instanceId)??[];t.push(e),n.set(e.instanceId,t)}for(const t of e){const r=a(t.indicatorId),o=n.get(t.instanceId)??[],i=r.inputs.filter(e=>"source"===e.type&&"string"!=typeof(Object.prototype.hasOwnProperty.call(t.settings,e.key)?t.settings[e.key]:e.default)),s=new Set(t.studyInputs??[]);if(i.length!==s.size||i.some(e=>!s.has(e.key)||"source"!==e.type||!0!==e.allowStudyOutputs))throw new c("Template dependency metadata disagrees with its source inputs");for(const n of s){const r=t.settings[n],o=e.find(e=>e.instanceId===r.instanceId),i=a(o.indicatorId).plots.filter(e=>e.key===r.plotKey);if(1!==i.length||void 0!==i[0].ohlc)throw new c("Template study source must identify one declared scalar plot")}if(o.length!==r.plots.length)throw new c("Template needs every declared plot binding");for(const e of o){const n=r.plots.find(t=>t.key===e.plotKey);if(!n||e.paneIndex!==(!0===n.overlay?0:t.paneIndex))throw new c("Template plot key or pane disagrees with its descriptor")}Y(r,t,o)}return n}(_,b),D="replace"===r?function(e){const t=e.panes().map(e=>e.series().length),n=e.primaryPaneIndex();for(const r of e.indicators())for(const o of a(r.indicatorId).plots){const a=r.series(o.key);a&&null!==e.seriesType(a)&&t[!0===o.overlay?n:r.paneIndex]--}return new Set(t.flatMap((e,t)=>e>0?[K(t,n)]:[]))}(e):new Set;for(const e of k)e.paneIndex>0&&D.add(e.paneIndex);const P={preserveScaleFormats:y.panes.flatMap((e,t)=>0===t||"append"===r||D.has(t)?B(e).map(([e])=>({paneIndex:t,scaleId:e})):[])};if("append"===r&&!_.length)return{indicators:S,restoreOptions:P};const T=V(e,s,y.panes[0]),j=y.panes.map(e=>t(e));"replace"===r&&te(j,new Set(S.filter(e=>!k.includes(e)).flatMap(e=>e.instanceId?[e.instanceId]:[])));const M=function(e,t,n){const r=new Map([[0,0]]);let a=n;for(const n of[...new Set(e.map(e=>e.paneIndex).filter(e=>e>0))].sort((e,t)=>e-t)){for(;t.has(a);)a++;if(a>v)throw new c("Indicator pane limit exceeded");r.set(n,a++)}return r}(_,D,"append"===r?j.length:1),$=_.map(e=>e.instanceId),N=q(S,_),L=j[0],R=b.panes[0];!function(e,t,n,r){const a=e[0],o=t[0];for(const[i,s]of n){if(!i)continue;const n=t[i],l=n.weight/o.weight*a.weight;if(!(l>0&&Number.isFinite(l)))throw new c("Template pane weight is not representable");const d=r(n);e[s]={weight:l,priceScale:d[0][1],...d.length>1?{scales:Object.fromEntries(d.slice(1))}:{}}}}(j,b.panes,M,e=>B(e).map(([e,t])=>[e,ee(t,N,f)]));const z=new Set(B(L).map(([e])=>e)),W=new Map,C=[];let G=1;const X=(e,t)=>{if(e>0)return t;const n=W.get(t);if(void 0!==n)return n;if(t===b.primaryScaleId&&void 0!==T)return W.set(t,T),T;let r=t;if("copy"===u)do{r=`overlay:template-scale-${_[0].instanceId}-${G++}`}while(J(L,r));if(W.set(t,r),!J(L,r)){const e=J(R,t)??{marginTop:.1,marginBottom:.1,minMove:0,minPrecision:0,mode:"linear",inverted:!1,autoScale:!0},n=ee(e,N,f);"right"===r?L.priceScale=n:(L.scales??(L.scales={}),L.scales[r]=n),"copy"===u&&C.push({id:r,source:{...e,placement:e.placement??{side:"left"===t||"right"===t?t:"hidden",order:0}}})}return r};_.forEach((e,t)=>{const n=$[t],r=e.paneIndex,o=(O.get(n)??[]).map(e=>[e.plotKey,X(e.paneIndex,e.scaleId)]);o.length&&(e.plotPriceScaleIds=Object.fromEntries(o)),void 0!==e.priceScaleId?e.priceScaleId=X(r,e.priceScaleId):a(e.indicatorId).plots.some(e=>!0!==e.overlay)||(e.priceScaleId=X(r,"right")),e.paneIndex=M.get(r),Y(a(e.indicatorId),e,(O.get(n)??[]).map(t=>({...t,paneIndex:M.get(t.paneIndex),scaleId:e.plotPriceScaleIds[t.plotKey]})))});for(const e of["left","right","hidden"]){let t=Math.max(-1,...B(L).filter(([e])=>z.has(e)).map(([e,t])=>t.placement??{side:"left"===e||"right"===e?e:"hidden",order:0}).filter(t=>t.side===e).map(e=>e.order))+1;for(const n of C.filter(t=>t.source.placement.side===e).sort((e,t)=>e.source.placement.order-t.source.placement.order))J(L,n.id).placement={side:e,order:t++}}return{indicators:x,panes:j,restoreOptions:P}}(e,n,r,i,s);if(0===s)return l;const u={indicators:l.indicators.map(e=>({...e,paneIndex:X(e.paneIndex,s)})),panes:l.panes?H(l.panes,s):re(e,r),primaryPane:s};return l.restoreOptions&&(u.restoreOptions={preserveScaleFormats:(l.restoreOptions.preserveScaleFormats??[]).map(e=>({...e,paneIndex:X(e.paneIndex,s)}))}),u}function re(e,n){const r=e.getState(),a=(r.panes??[]).map(e=>t(e));return"replace"===n&&te(a,new Set((r.indicators??[]).flatMap(e=>e.instanceId?[e.instanceId]:[]))),a}class ae extends Error{constructor(){super("The saved workspace changed in another session. Reload and retry."),this.name="WorkspaceConflictError"}}const oe={parse:ie,conflict:()=>new ae};function ie(e){const t=p(d(e),"workspace catalog");if(1!==t.version)throw new c("Unsupported workspace catalog version");const n=u(t.workspaces,"workspaces",100).map($),r=u(t.templates,"templates",100).map(L),a=[...n,...r].map(e=>e.id);if(new Set(a).size!==a.length)throw new c("Duplicate document ID");const o=new Set(n.map(e=>e.id)),i=u(t.recentWorkspaceIds,"recent workspaces",10).map(e=>f(e,"recent workspace ID",100));if(new Set(i).size!==i.length||i.some(e=>!o.has(e)))throw new c("Invalid recent workspace IDs");const s=null===t.activeWorkspaceId?null:f(t.activeWorkspaceId,"active workspace ID",100);if(null!==s&&!o.has(s))throw new c("Active workspace is missing");return{version:1,revision:h(t.revision,"catalog revision",0,Number.MAX_SAFE_INTEGER,!0),workspaces:n,templates:r,recentWorkspaceIds:i,activeWorkspaceId:s,autosave:m(t.autosave,"autosave")}}function se(){return{version:1,revision:0,workspaces:[],templates:[],recentWorkspaceIds:[],activeWorkspaceId:null,autosave:!1}}function ce(e){const t=p(d(e),"document");if("workspace"===t.kind)return $(t);if("indicator-template"===t.kind)return L(t);throw new c("Unsupported document kind")}class le{constructor(e,t,n={}){Object.defineProperty(this,"_catalog",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_now",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_id",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this._catalog=new C(e,t,oe,se),this._now=n.now??Date.now,this._id=n.id??W}get namespace(){return this._catalog.namespace}load(){return this._catalog.load()}subscribe(e){return this._catalog.subscribe(e)}async createWorkspace(e,t,n){const r=N(t),a=f(e,"name",120);return this._catalog.transact(e=>{const t=this._now(),n=$({...r,kind:"workspace",version:1,id:this._newId(e),name:a,createdAt:t,updatedAt:t});return e.workspaces.push(n),n},n)}async saveWorkspace(e,t,n){const r=N(t);return this._catalog.transact(t=>{const n=this._find(t,"workspace",e),a=$({...n,...r,updatedAt:this._updatedAt(n)});return t.workspaces[t.workspaces.indexOf(n)]=a,a},n)}async createTemplate(e,t,n){const r=E(t),a=f(e,"name",120);return this._catalog.transact(e=>{const t=this._now(),n=L({kind:"indicator-template",version:1,id:this._newId(e),name:a,createdAt:t,updatedAt:t,...r});return e.templates.push(n),n},n)}async saveTemplate(e,t,n){const r=E(t);return this._catalog.transact(t=>{const n=this._find(t,"indicator-template",e),a=L({kind:"indicator-template",version:n.version,id:n.id,name:n.name,createdAt:n.createdAt,updatedAt:this._updatedAt(n),...r});return t.templates[t.templates.indexOf(n)]=a,a},n)}async rename(e,t,n,r){const a=f(n,"name",120);return this._catalog.transact(n=>{const r=this._find(n,e,t);r.name=a,r.updatedAt=this._updatedAt(r)},r)}async duplicate(e,t,n,r){const a=f(n,"name",120);return this._catalog.transact(n=>this._insert(n,this._find(n,e,t),a),r)}async remove(e,t,n){return this._catalog.transact(n=>{this._find(n,e,t),"indicator-template"===e?n.templates=n.templates.filter(e=>e.id!==t):(n.workspaces=n.workspaces.filter(e=>e.id!==t),n.recentWorkspaceIds=n.recentWorkspaceIds.filter(e=>e!==t),n.activeWorkspaceId===t&&(n.activeWorkspaceId=n.recentWorkspaceIds[0]??null))},n)}async openWorkspace(e,t){return this._catalog.transact(t=>{const n=this._find(t,"workspace",e);return t.activeWorkspaceId=n.id,t.recentWorkspaceIds=[n.id,...t.recentWorkspaceIds.filter(e=>e!==n.id)].slice(0,10),n},t)}async setAutosave(e,t){const n=m(e,"autosave");return this._catalog.transact(e=>{e.autosave=n},t)}async importDocument(e,t){const n=ce(e);return this._catalog.transact(e=>this._insert(e,n,n.name),t)}captureIndicatorTemplate(e){return Q(e)}planIndicatorTemplateState(e,t,n,r){return ne(e,t,n,r)}async exportDocument(e,t){const n=await this.load();return JSON.stringify(this._find(n,e,t),null,2)}_find(e,t,n){if("workspace"!==t&&"indicator-template"!==t)throw new c("Unsupported document kind");const r=f(n,"document ID",100),a=("workspace"===t?e.workspaces:e.templates).find(e=>e.id===r);if(!a)throw new c(`Saved ${t} does not exist`);return a}_updatedAt(e){return Math.max(e.updatedAt,h(this._now(),"current time",0))}_newId(e){const t=f(this._id(),"generated document ID",100);if([...e.workspaces,...e.templates].some(e=>e.id===t))throw new c("Generated document ID collision");return t}_insert(e,t,n){const r=this._now(),a=ce({...t,name:n,id:this._newId(e),createdAt:r,updatedAt:r});return"workspace"===a.kind?e.workspaces.push(a):e.templates.push(a),a}}function de(e={}){return G(e,oe)}function pe(e,t,n,r,a){const o=f(t,"database name");let i=null,s=null,l=!1;const d=()=>new Error(`${n} storage is closed. Create a new adapter to reconnect.`);function p(){return l?Promise.reject(d()):i||(i=new Promise((t,r)=>{const a=e.open(o,1);let i=!1;a.onupgradeneeded=()=>{a.result.objectStoreNames.contains("catalogs")||a.result.createObjectStore("catalogs")},a.onblocked=()=>{i=!0,r(new Error(`${n} database is blocked by another session. Close that session and retry.`))},a.onerror=()=>{i=!0,r(a.error??new Error(`Cannot open ${n.toLowerCase()} database`))},a.onsuccess=()=>{const e=a.result;if(l||i)return e.close(),void r(d());s=e,e.onversionchange=()=>{l=!0,e.close(),s=null},e.onclose=()=>{l=!0,s=null},t(e)}}).catch(e=>{throw i=null,e}),i)}return{async read(e){const t=f(e,"storage namespace"),r=await p();return new Promise((e,a)=>{const o=r.transaction("catalogs","readonly"),i=o.objectStore("catalogs").get(t);let s=null;i.onsuccess=()=>{s=i.result??null},o.oncomplete=()=>e(s),o.onabort=()=>a(o.error??new Error(`${n} read was aborted`))})},async write(e,t,o,i){const s=i?.signal;s?.throwIfAborted();const l=f(e,"storage namespace"),d=h(o,"expected revision",0,Number.MAX_SAFE_INTEGER,!0),u=r(t);if(u.revision!==d+1)throw new c("A write must advance the catalog revision by one");const m=await p();return s?.throwIfAborted(),new Promise((e,t)=>{const o=m.transaction("catalogs","readwrite"),i=o.objectStore("catalogs"),c=i.get(l);let p;const f=()=>{try{o.abort(),p=s?.reason}catch{}},h=()=>s?.removeEventListener("abort",f);s?.addEventListener("abort",f,{once:!0}),c.onsuccess=()=>{try{const e=c.result;if((void 0===e?0:r(e).revision)!==d)throw a();i.put(u,l)}catch(e){p=e,o.abort()}},o.oncomplete=()=>{h(),e()},o.onabort=()=>{h(),t(p??o.error??new Error(`${n} save was aborted`))}})},async close(){l=!0,s&&(s.close(),s=null),i&&await i.catch(()=>{})}}}function ue(e,t="openalgo-chart-workspaces"){return pe(e,t,"Workspace",ie,()=>new ae)}class fe extends Error{constructor(){super("The saved watchlists changed in another session. Reload and retry."),this.name="WatchlistConflictError"}}function he(e){return JSON.stringify([e.symbol,e.exchange])}function me(e){const t=p(e,"watchlist entry");return{symbol:f(t.symbol,"symbol",64),exchange:f(t.exchange,"exchange",32,!0)}}function we(e){const t=u(e,"watchlist entries",500).map(me);if(new Set(t.map(he)).size!==t.length)throw new c("Duplicate watchlist entry");return t}function ge(e){const t=p(e,"watchlist"),n=h(t.createdAt,"createdAt",0);return{id:f(t.id,"watchlist ID",100),name:f(t.name,"name",120),entries:we(t.entries),createdAt:n,updatedAt:h(t.updatedAt,"updatedAt",n)}}function ye(e){const t=p(d(e),"watchlist catalog");if(1!==t.version)throw new c("Unsupported watchlist catalog version");const n=u(t.lists,"watchlists",100).map(ge),r=new Set(n.map(e=>e.id));if(r.size!==n.length)throw new c("Duplicate watchlist ID");const a=null===t.activeListId?null:f(t.activeListId,"active watchlist ID",100);if(null!==a&&!r.has(a))throw new c("Active watchlist is missing");return{version:1,revision:h(t.revision,"catalog revision",0,Number.MAX_SAFE_INTEGER,!0),lists:n,activeListId:a}}const ve=()=>({version:1,revision:0,lists:[],activeListId:null}),Ie=e=>""===e.exchange?e.symbol:`${e.symbol} on ${e.exchange}`,be={parse:ye,conflict:()=>new fe};class _e{constructor(e,t,n={}){Object.defineProperty(this,"_catalog",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_now",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_id",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this._catalog=new C(e,t,be,ve),this._now=n.now??Date.now,this._id=n.id??W}get namespace(){return this._catalog.namespace}load(){return this._catalog.load()}subscribe(e){return this._catalog.subscribe(e)}async createList(e,t=[],n){const r=f(e,"name",120),a=we(d(t));return this._catalog.transact(e=>{const t=this._now(),n={id:this._newId(e),name:r,entries:a,createdAt:t,updatedAt:t};return e.lists.push(n),n},n)}async renameList(e,t,n){const r=f(t,"name",120);return this._catalog.transact(t=>this._touch(this._find(t,e),e=>{e.name=r}),n)}async duplicateList(e,t,n){const r=f(t,"name",120);return this._catalog.transact(t=>{const n=this._find(t,e),a=this._now(),o={id:this._newId(t),name:r,entries:n.entries.map(e=>({...e})),createdAt:a,updatedAt:a};return t.lists.push(o),o},n)}async removeList(e,t){return this._catalog.transact(t=>{const n=this._find(t,e);t.lists=t.lists.filter(e=>e!==n),t.activeListId===n.id&&(t.activeListId=null)},t)}async setActiveList(e,t){return this._catalog.transact(t=>{t.activeListId=null===e?null:this._find(t,e).id},t)}async addEntry(e,t,n={}){const r=me(d(t));return this._catalog.transact(t=>this._touch(this._find(t,e),e=>{if(e.entries.some(e=>he(e)===he(r)))throw new c(`${Ie(r)} is already in this list`);e.entries.splice(this._index(n.index??e.entries.length,e.entries.length),0,r)}),n)}async removeEntry(e,t,n){const r=me(d(t));return this._catalog.transact(t=>this._touch(this._find(t,e),e=>{e.entries.splice(this._position(e,r),1)}),n)}async moveEntry(e,t,n,r){const a=me(d(t));return this._catalog.transact(t=>this._touch(this._find(t,e),e=>{const[t]=e.entries.splice(this._position(e,a),1);e.entries.splice(this._index(n,e.entries.length),0,t)}),r)}_find(e,t){const n=f(t,"watchlist ID",100),r=e.lists.find(e=>e.id===n);if(!r)throw new c("Saved watchlist does not exist");return r}_touch(e,t){return t(e),e.updatedAt=Math.max(e.updatedAt,h(this._now(),"current time",0)),e}_position(e,t){const n=e.entries.findIndex(e=>he(e)===he(t));if(n<0)throw new c(`${Ie(t)} is not in this list`);return n}_index(e,t){return Math.max(0,Math.min(t,Math.trunc(h(e,"entry index",-Number.MAX_SAFE_INTEGER))))}_newId(e){const t=f(this._id(),"generated watchlist ID",100);if(e.lists.some(e=>e.id===t))throw new c("Generated watchlist ID collision");return t}}function Se(e={}){return G(e,be)}function Ae(e,t="openalgo-chart-watchlists"){return pe(e,t,"Watchlist",ye,()=>new fe)}class ke extends Error{constructor(){super("The saved drawing templates changed in another session. Reload and retry."),this.name="DrawingTemplateConflictError"}}const xe=/^(style|text|props)\.[A-Za-z][A-Za-z0-9_]{0,63}$/;function Oe(e){const t=p(e,"template level"),n={ratio:h(t.ratio,"level ratio",-1e6,1e6)};if(void 0!==t.color&&(n.color=f(t.color,"level color",64)),void 0!==t.enabled){if("boolean"!=typeof t.enabled)throw new c("level enabled must be a boolean");n.enabled=t.enabled}return void 0!==t.label&&(n.label=f(t.label,"level label",64,!0)),n}function De(e,t){if("boolean"==typeof e)return e;if("number"==typeof e)return h(e,t,-1e9,1e9);if("string"==typeof e)return f(e,t,256,!0);if(Array.isArray(e))return u(e,t,64).map(Oe);throw new c(`${t} must be a string, a number, a boolean or a list of levels`)}function Ee(e){const t=p(d(e),"template values"),n=Object.keys(t);if(n.length>64)throw new c("template values must be within the 64 setting limit");const r={};for(const e of n){if(!xe.test(e)||"text.value"===e)throw new c(`${e} is not a template setting`);r[e]=De(t[e],e)}return r}const Pe=e=>f(e,"drawing tool",100);function Te(e){const t=p(e,"drawing template"),n=h(t.createdAt,"createdAt",0);return{id:f(t.id,"template ID",100),name:f(t.name,"name",120),tool:Pe(t.tool),values:Ee(t.values),createdAt:n,updatedAt:h(t.updatedAt,"updatedAt",n)}}function je(e){const t=p(e,"drawing default");return{tool:Pe(t.tool),values:Ee(t.values),updatedAt:h(t.updatedAt,"updatedAt",0)}}function Me(e){const t=p(d(e),"drawing template catalog");if(1!==t.version)throw new c("Unsupported drawing template catalog version");const n=u(t.templates,"drawing templates",200).map(Te);if(new Set(n.map(e=>e.id)).size!==n.length)throw new c("Duplicate template ID");const r=u(t.defaults,"drawing defaults",200).map(je);if(new Set(r.map(e=>e.tool)).size!==r.length)throw new c("Duplicate tool default");return{version:1,revision:h(t.revision,"catalog revision",0,Number.MAX_SAFE_INTEGER,!0),templates:n,defaults:r}}const $e=()=>({version:1,revision:0,templates:[],defaults:[]}),Ne={parse:Me,conflict:()=>new ke},Le=(e,t)=>e.trim().toLowerCase()===t.trim().toLowerCase();class Re{constructor(e,t,n={}){Object.defineProperty(this,"_catalog",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_now",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),Object.defineProperty(this,"_id",{enumerable:!0,configurable:!0,writable:!0,value:void 0}),this._catalog=new C(e,t,Ne,$e),this._now=n.now??Date.now,this._id=n.id??W}get namespace(){return this._catalog.namespace}load(){return this._catalog.load()}subscribe(e){return this._catalog.subscribe(e)}async saveTemplate(e,t,n,r){const a=f(e,"name",120),o=Pe(t),i=Ee(n);return this._catalog.transact(e=>{const t=this._now(),n=e.templates.find(e=>e.tool===o&&Le(e.name,a));if(n)return n.values=i,n.updatedAt=this._later(n.updatedAt,t),n;const r={id:this._newId(e),name:a,tool:o,values:i,createdAt:t,updatedAt:t};return e.templates.push(r),r},r)}async renameTemplate(e,t,n){const r=f(t,"name",120);return this._catalog.transact(t=>{const n=this._find(t,e);if(t.templates.some(e=>e!==n&&e.tool===n.tool&&Le(e.name,r)))throw new c(`A template named ${r} already exists for this tool`);return n.name=r,n.updatedAt=this._later(n.updatedAt,this._now()),n},n)}async removeTemplate(e,t){return this._catalog.transact(t=>{const n=this._find(t,e);t.templates=t.templates.filter(e=>e!==n)},t)}async setDefault(e,t,n){const r=Pe(e),a=null===t?null:Ee(t);return this._catalog.transact(e=>{const t=e.defaults.find(e=>e.tool===r);if(null===a)return void(e.defaults=e.defaults.filter(e=>e!==t));const n=this._later(t?.updatedAt??0,this._now());t?(t.values=a,t.updatedAt=n):e.defaults.push({tool:r,values:a,updatedAt:n})},n)}_find(e,t){const n=f(t,"template ID",100),r=e.templates.find(e=>e.id===n);if(!r)throw new c("Saved drawing template does not exist");return r}_later(e,t){return Math.max(e,h(t,"current time",0))}_newId(e){const t=f(this._id(),"generated template ID",100);if(e.templates.some(e=>e.id===t))throw new c("Generated template ID collision");return t}}function ze(e={}){return G(e,Ne)}function We(e,t="openalgo-chart-drawing-templates"){return pe(e,t,"Drawing template",Me,()=>new ke)}export{ke as DrawingTemplateConflictError,Re as DrawingTemplateRepository,y as WORKSPACE_VERSION,fe as WatchlistConflictError,_e as WatchlistRepository,ae as WorkspaceConflictError,c as WorkspaceDocumentError,le as WorkspaceRepository,Q as captureIndicatorTemplate,We as createIndexedDbDrawingTemplateStorage,Ae as createIndexedDbWatchlistStorage,ue as createIndexedDbWorkspaceStorage,ze as createMemoryDrawingTemplateStorage,Se as createMemoryWatchlistStorage,de as createMemoryWorkspaceStorage,R as migrateWidgetWorkspace,Me as parseDrawingTemplateCatalog,Ee as parseDrawingTemplateValues,A as parseIndicatorStates,L as parseIndicatorTemplate,E as parseIndicatorTemplatePayload,ye as parseWatchlistCatalog,ie as parseWorkspaceCatalog,$ as parseWorkspaceDocument,N as parseWorkspacePayload,U as planIndicatorTemplate,ne as planIndicatorTemplateState,he as watchlistKey};
//# sourceMappingURL=openalgo-charts.workspace.mjs.map