storyblok-js-client
Advanced tools
Comparing version 6.10.1 to 6.10.2
@@ -1,24 +0,24 @@ | ||
(function(g,d){typeof exports=="object"&&typeof module<"u"?module.exports=d():typeof define=="function"&&define.amd?define(d):(g=typeof globalThis<"u"?globalThis:g||self,g.StoryblokJSClient=d())})(this,function(){"use strict";var H=Object.defineProperty;var U=(g,d,y)=>d in g?H(g,d,{enumerable:!0,configurable:!0,writable:!0,value:y}):g[d]=y;var c=(g,d,y)=>U(g,typeof d!="symbol"?d+"":d,y);function g(a){return!(a!==a||a===1/0||a===-1/0)}function d(a,e,t){if(!g(e))throw new TypeError("Expected `limit` to be a finite number");if(!g(t))throw new TypeError("Expected `interval` to be a finite number");const r=[];let s=[],i=0;const n=function(){i++;const o=setTimeout(function(){i--,r.length>0&&n(),s=s.filter(function(u){return u!==o})},t);s.indexOf(o)<0&&s.push(o);const h=r.shift();h.resolve(a.apply(h.self,h.args))},l=function(...o){const h=this;return new Promise(function(u,f){r.push({resolve:u,reject:f,args:o,self:h}),i<e&&n()})};return l.abort=function(){s.forEach(clearTimeout),s=[],r.forEach(function(o){o.reject(function(){Error.call(this,"Throttled function aborted"),this.name="AbortError"})}),r.length=0},l}class y{constructor(){c(this,"isCDNUrl",(e="")=>e.indexOf("/cdn/")>-1);c(this,"getOptionsPage",(e,t=25,r=1)=>({...e,per_page:t,page:r}));c(this,"delay",e=>new Promise(t=>setTimeout(t,e)));c(this,"arrayFrom",(e=0,t)=>[...Array(e)].map(t));c(this,"range",(e=0,t=e)=>{const r=Math.abs(t-e)||0,s=e<t?1:-1;return this.arrayFrom(r,(i,n)=>n*s+e)});c(this,"asyncMap",async(e,t)=>Promise.all(e.map(t)));c(this,"flatMap",(e=[],t)=>e.map(t).reduce((r,s)=>[...r,...s],[]));c(this,"escapeHTML",function(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"},r=/[&<>"']/g,s=RegExp(r.source);return e&&s.test(e)?e.replace(r,i=>t[i]):e})}stringify(e,t,r){const s=[];for(const i in e){if(!Object.prototype.hasOwnProperty.call(e,i))continue;const n=e[i],l=r?"":encodeURIComponent(i);let o;typeof n=="object"?o=this.stringify(n,t?t+encodeURIComponent("["+l+"]"):l,Array.isArray(n)):o=(t?t+encodeURIComponent("["+l+"]"):l)+"="+encodeURIComponent(n),s.push(o)}return s.join("&")}getRegionURL(e){const t="api.storyblok.com",r="api-us.storyblok.com",s="app.storyblokchina.cn",i="api-ap.storyblok.com",n="api-ca.storyblok.com";switch(e){case"us":return r;case"cn":return s;case"ap":return i;case"ca":return n;default:return t}}}const O=Object.freeze(Object.defineProperty({__proto__:null,SbHelpers:y},Symbol.toStringTag,{value:"Module"})),L=function(a,e){const t={};for(const r in a){const s=a[r];e.indexOf(r)>-1&&s!==null&&(t[r]=s)}return t},E=a=>a==="email",w={nodes:{horizontal_rule:()=>({singleTag:"hr"}),blockquote:()=>({tag:"blockquote"}),bullet_list:()=>({tag:"ul"}),code_block:a=>({tag:["pre",{tag:"code",attrs:a.attrs}]}),hard_break:()=>({singleTag:"br"}),heading:a=>({tag:`h${a.attrs.level}`}),image:a=>({singleTag:[{tag:"img",attrs:L(a.attrs,["src","alt","title"])}]}),list_item:()=>({tag:"li"}),ordered_list:()=>({tag:"ol"}),paragraph:()=>({tag:"p"}),emoji:a=>({tag:[{tag:"span",attrs:{"data-type":"emoji","data-name":a.attrs.name,emoji:a.attrs.emoji}}]})},marks:{bold:()=>({tag:"b"}),strike:()=>({tag:"s"}),underline:()=>({tag:"u"}),strong:()=>({tag:"strong"}),code:()=>({tag:"code"}),italic:()=>({tag:"i"}),link:a=>{if(!a.attrs)return{tag:""};const e=new y().escapeHTML,t={...a.attrs},{linktype:r="url"}=a.attrs;if(delete t.linktype,t.href&&(t.href=e(a.attrs.href||"")),E(r)&&(t.href=`mailto:${t.href}`),t.anchor&&(t.href=`${t.href}#${t.anchor}`,delete t.anchor),t.custom){for(const s in t.custom)t[s]=t.custom[s];delete t.custom}return{tag:[{tag:"a",attrs:t}]}},styled:a=>({tag:[{tag:"span",attrs:a.attrs}]}),subscript:()=>({tag:"sub"}),superscript:()=>({tag:"sup"}),anchor:a=>({tag:[{tag:"span",attrs:a.attrs}]}),highlight:a=>{var t;return(t=a.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`background-color:${a.attrs.color};`}}]}:{tag:""}},textStyle:a=>{var t;return(t=a.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`color:${a.attrs.color}`}}]}:{tag:""}}}},j=function(a){const e={"&":"&","<":"<",">":">",'"':""","'":"'"},t=/[&<>"']/g,r=RegExp(t.source);return a&&r.test(a)?a.replace(t,s=>e[s]):a};let S=!1;class R{constructor(e){c(this,"marks");c(this,"nodes");e||(e=w),this.marks=e.marks||[],this.nodes=e.nodes||[]}addNode(e,t){this.nodes[e]=t}addMark(e,t){this.marks[e]=t}render(e,t={optimizeImages:!1},r=!0){if(!S&&r&&(console.warn("Warning ⚠️: The RichTextResolver class is deprecated and will be removed in the next major release. Please use the `@storyblok/richtext` package instead. https://github.com/storyblok/richtext/"),S=!0),e&&e.content&&Array.isArray(e.content)){let s="";return e.content.forEach(i=>{s+=this.renderNode(i)}),t.optimizeImages?this.optimizeImages(s,t.optimizeImages):s}return console.warn(`The render method must receive an Object with a "content" field. | ||
The "content" field must be an array of nodes as the type ISbRichtext. | ||
ISbRichtext: | ||
content?: ISbRichtext[] | ||
marks?: ISbRichtext[] | ||
attrs?: any | ||
text?: string | ||
type: string | ||
Example: | ||
{ | ||
content: [ | ||
{ | ||
content: [ | ||
{ | ||
text: 'Hello World', | ||
type: 'text' | ||
} | ||
], | ||
type: 'paragraph' | ||
} | ||
], | ||
type: 'doc' | ||
}`),""}optimizeImages(e,t){let r=0,s=0,i="",n="";typeof t!="boolean"&&(typeof t.width=="number"&&t.width>0&&(i+=`width="${t.width}" `,r=t.width),typeof t.height=="number"&&t.height>0&&(i+=`height="${t.height}" `,s=t.height),(t.loading==="lazy"||t.loading==="eager")&&(i+=`loading="${t.loading}" `),typeof t.class=="string"&&t.class.length>0&&(i+=`class="${t.class}" `),t.filters&&(typeof t.filters.blur=="number"&&t.filters.blur>=0&&t.filters.blur<=100&&(n+=`:blur(${t.filters.blur})`),typeof t.filters.brightness=="number"&&t.filters.brightness>=-100&&t.filters.brightness<=100&&(n+=`:brightness(${t.filters.brightness})`),t.filters.fill&&(t.filters.fill.match(/[0-9A-Fa-f]{6}/g)||t.filters.fill==="transparent")&&(n+=`:fill(${t.filters.fill})`),t.filters.format&&["webp","png","jpeg"].includes(t.filters.format)&&(n+=`:format(${t.filters.format})`),typeof t.filters.grayscale=="boolean"&&t.filters.grayscale&&(n+=":grayscale()"),typeof t.filters.quality=="number"&&t.filters.quality>=0&&t.filters.quality<=100&&(n+=`:quality(${t.filters.quality})`),t.filters.rotate&&[90,180,270].includes(t.filters.rotate)&&(n+=`:rotate(${t.filters.rotate})`),n.length>0&&(n="/filters"+n))),i.length>0&&(e=e.replace(/<img/g,`<img ${i.trim()}`));const l=r>0||s>0||n.length>0?`${r}x${s}${n}`:"";return e=e.replace(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|tiff|bmp)/g,`a.storyblok.com/f/$1/$2.$3/m/${l}`),typeof t!="boolean"&&(t.sizes||t.srcset)&&(e=e.replace(/<img.*?src=["|'](.*?)["|']/g,o=>{var u,f;const h=o.match(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|tiff|bmp)/g);if(h&&h.length>0){const m={srcset:(u=t.srcset)==null?void 0:u.map(p=>{if(typeof p=="number")return`//${h}/m/${p}x0${n} ${p}w`;if(typeof p=="object"&&p.length===2){let $=0,C=0;return typeof p[0]=="number"&&($=p[0]),typeof p[1]=="number"&&(C=p[1]),`//${h}/m/${$}x${C}${n} ${$}w`}}).join(", "),sizes:(f=t.sizes)==null?void 0:f.map(p=>p).join(", ")};let v="";return m.srcset&&(v+=`srcset="${m.srcset}" `),m.sizes&&(v+=`sizes="${m.sizes}" `),o.replace(/<img/g,`<img ${v.trim()}`)}return o})),e}renderNode(e){const t=[];e.marks&&e.marks.forEach(s=>{const i=this.getMatchingMark(s);i&&i.tag!==""&&t.push(this.renderOpeningTag(i.tag))});const r=this.getMatchingNode(e);return r&&r.tag&&t.push(this.renderOpeningTag(r.tag)),e.content?e.content.forEach(s=>{t.push(this.renderNode(s))}):e.text?t.push(j(e.text)):r&&r.singleTag?t.push(this.renderTag(r.singleTag," /")):r&&r.html?t.push(r.html):e.type==="emoji"&&t.push(this.renderEmoji(e)),r&&r.tag&&t.push(this.renderClosingTag(r.tag)),e.marks&&e.marks.slice(0).reverse().forEach(s=>{const i=this.getMatchingMark(s);i&&i.tag!==""&&t.push(this.renderClosingTag(i.tag))}),t.join("")}renderTag(e,t){return e.constructor===String?`<${e}${t}>`:e.map(s=>{if(s.constructor===String)return`<${s}${t}>`;{let i=`<${s.tag}`;if(s.attrs){for(const n in s.attrs)if(Object.prototype.hasOwnProperty.call(s.attrs,n)){const l=s.attrs[n];l!==null&&(i+=` ${n}="${l}"`)}}return`${i}${t}>`}}).join("")}renderOpeningTag(e){return this.renderTag(e,"")}renderClosingTag(e){return e.constructor===String?`</${e}>`:e.slice(0).reverse().map(r=>r.constructor===String?`</${r}>`:`</${r.tag}>`).join("")}getMatchingNode(e){const t=this.nodes[e.type];if(typeof t=="function")return t(e)}getMatchingMark(e){const t=this.marks[e.type];if(typeof t=="function")return t(e)}renderEmoji(e){if(e.attrs.emoji)return e.attrs.emoji;const t=[{tag:"img",attrs:{src:e.attrs.fallbackImage,draggable:"false",loading:"lazy",align:"absmiddle"}}];return this.renderTag(t," /")}}class x{constructor(e){c(this,"baseURL");c(this,"timeout");c(this,"headers");c(this,"responseInterceptor");c(this,"fetch");c(this,"ejectInterceptor");c(this,"url");c(this,"parameters");c(this,"fetchOptions");this.baseURL=e.baseURL,this.headers=e.headers||new Headers,this.timeout=e!=null&&e.timeout?e.timeout*1e3:0,this.responseInterceptor=e.responseInterceptor,this.fetch=(...t)=>e.fetch?e.fetch(...t):fetch(...t),this.ejectInterceptor=!1,this.url="",this.parameters={},this.fetchOptions={}}get(e,t){return this.url=e,this.parameters=t,this._methodHandler("get")}post(e,t){return this.url=e,this.parameters=t,this._methodHandler("post")}put(e,t){return this.url=e,this.parameters=t,this._methodHandler("put")}delete(e,t){return this.url=e,this.parameters=t,this._methodHandler("delete")}async _responseHandler(e){const t=[],r={data:{},headers:{},status:0,statusText:""};e.status!==204&&await e.json().then(s=>{r.data=s});for(const s of e.headers.entries())t[s[0]]=s[1];return r.headers={...t},r.status=e.status,r.statusText=e.statusText,r}async _methodHandler(e){let t=`${this.baseURL}${this.url}`,r=null;if(e==="get"){const o=new y;t=`${this.baseURL}${this.url}?${o.stringify(this.parameters)}`}else r=JSON.stringify(this.parameters);const s=new URL(t),i=new AbortController,{signal:n}=i;let l;this.timeout&&(l=setTimeout(()=>i.abort(),this.timeout));try{const o=await this.fetch(`${s}`,{method:e,headers:this.headers,body:r,signal:n,...this.fetchOptions});this.timeout&&clearTimeout(l);const h=await this._responseHandler(o);return this.responseInterceptor&&!this.ejectInterceptor?this._statusHandler(this.responseInterceptor(h)):this._statusHandler(h)}catch(o){return{message:o}}}setFetchOptions(e={}){Object.keys(e).length>0&&"method"in e&&delete e.method,this.fetchOptions={...e}}eject(){this.ejectInterceptor=!0}_statusHandler(e){const t=/20[0-6]/g;return new Promise((r,s)=>{if(t.test(`${e.status}`))return r(e);const i={message:e.statusText,status:e.status,response:Array.isArray(e.data)?e.data[0]:e.data.error||e.data.slug};s(i)})}}const P="SB-Agent",_={defaultAgentName:"SB-JS-CLIENT",defaultAgentVersion:"SB-Agent-Version",packageVersion:"6.0.0"};let b={};const k={};class T{constructor(e,t){c(this,"client");c(this,"maxRetries");c(this,"retriesDelay");c(this,"throttle");c(this,"accessToken");c(this,"cache");c(this,"helpers");c(this,"resolveCounter");c(this,"relations");c(this,"links");c(this,"richTextResolver");c(this,"resolveNestedRelations");c(this,"stringifiedStoriesCache");let r=e.endpoint||t;if(!r){const n=new y().getRegionURL,l=e.https===!1?"http":"https";e.oauthToken?r=`${l}://${n(e.region)}/v1`:r=`${l}://${n(e.region)}/v2`}const s=new Headers;s.set("Content-Type","application/json"),s.set("Accept","application/json"),e.headers&&(e.headers.constructor.name==="Headers"?e.headers.entries().toArray():Object.entries(e.headers)).forEach(([l,o])=>{s.set(l,o)}),s.has(P)||(s.set(P,_.defaultAgentName),s.set(_.defaultAgentVersion,_.packageVersion));let i=5;e.oauthToken&&(s.set("Authorization",e.oauthToken),i=3),e.rateLimit&&(i=e.rateLimit),e.richTextSchema?this.richTextResolver=new R(e.richTextSchema):this.richTextResolver=new R,e.componentResolver&&this.setComponentResolver(e.componentResolver),this.maxRetries=e.maxRetries||10,this.retriesDelay=300,this.throttle=d(this.throttledRequest,i,1e3),this.accessToken=e.accessToken||"",this.relations={},this.links={},this.cache=e.cache||{clear:"manual"},this.helpers=new y,this.resolveCounter=0,this.resolveNestedRelations=e.resolveNestedRelations||!0,this.stringifiedStoriesCache={},this.client=new x({baseURL:r,timeout:e.timeout||0,headers:s,responseInterceptor:e.responseInterceptor,fetch:e.fetch})}setComponentResolver(e){this.richTextResolver.addNode("blok",t=>{let r="";return t.attrs.body&&t.attrs.body.forEach(s=>{r+=e(s.component,s)}),{html:r}})}parseParams(e){return e.token||(e.token=this.getToken()),e.cv||(e.cv=k[e.token]),Array.isArray(e.resolve_relations)&&(e.resolve_relations=e.resolve_relations.join(",")),typeof e.resolve_relations<"u"&&(e.resolve_level=2),e}factoryParamOptions(e,t){return this.helpers.isCDNUrl(e)?this.parseParams(t):t}makeRequest(e,t,r,s,i){const n=this.factoryParamOptions(e,this.helpers.getOptionsPage(t,r,s));return this.cacheResponse(e,n,void 0,i)}get(e,t,r){t||(t={});const s=`/${e}`,i=this.factoryParamOptions(s,t);return this.cacheResponse(s,i,void 0,r)}async getAll(e,t,r,s){const i=(t==null?void 0:t.per_page)||25,n=`/${e}`.replace(/\/$/,""),l=r??n.substring(n.lastIndexOf("/")+1),o=1,h=await this.makeRequest(n,t,i,o,s),u=h.total?Math.ceil(h.total/i):1,f=await this.helpers.asyncMap(this.helpers.range(o,u),m=>this.makeRequest(n,t,i,m+1,s));return this.helpers.flatMap([h,...f],m=>Object.values(m.data[l]))}post(e,t,r){const s=`/${e}`;return Promise.resolve(this.throttle("post",s,t,r))}put(e,t,r){const s=`/${e}`;return Promise.resolve(this.throttle("put",s,t,r))}delete(e,t,r){const s=`/${e}`;return Promise.resolve(this.throttle("delete",s,t,r))}getStories(e,t){return this._addResolveLevel(e),this.get("cdn/stories",e,t)}getStory(e,t,r){return this._addResolveLevel(t),this.get(`cdn/stories/${e}`,t,r)}getToken(){return this.accessToken}ejectInterceptor(){this.client.eject()}_addResolveLevel(e){typeof e.resolve_relations<"u"&&(e.resolve_level=2)}_cleanCopy(e){return JSON.parse(JSON.stringify(e))}_insertLinks(e,t,r){const s=e[t];s&&s.fieldtype=="multilink"&&s.linktype=="story"&&typeof s.id=="string"&&this.links[r][s.id]?s.story=this._cleanCopy(this.links[r][s.id]):s&&s.linktype==="story"&&typeof s.uuid=="string"&&this.links[r][s.uuid]&&(s.story=this._cleanCopy(this.links[r][s.uuid]))}getStoryReference(e,t){return this.relations[e][t]?(this.stringifiedStoriesCache[t]||(this.stringifiedStoriesCache[t]=JSON.stringify(this.relations[e][t])),JSON.parse(this.stringifiedStoriesCache[t])):t}_insertRelations(e,t,r,s){r.indexOf(`${e.component}.${t}`)>-1&&(typeof e[t]=="string"?e[t]=this.getStoryReference(s,e[t]):Array.isArray(e[t])&&(e[t]=e[t].map(i=>this.getStoryReference(s,i)).filter(Boolean)))}iterateTree(e,t,r){const s=i=>{if(i!=null){if(i.constructor===Array)for(let n=0;n<i.length;n++)s(i[n]);else if(i.constructor===Object){if(i._stopResolving)return;for(const n in i)(i.component&&i._uid||i.type==="link")&&(this._insertRelations(i,n,t,r),this._insertLinks(i,n,r)),s(i[n])}}};s(e.content)}async resolveLinks(e,t,r){let s=[];if(e.link_uuids){const i=e.link_uuids.length,n=[],l=50;for(let o=0;o<i;o+=l){const h=Math.min(i,o+l);n.push(e.link_uuids.slice(o,h))}for(let o=0;o<n.length;o++)(await this.getStories({per_page:l,language:t.language,version:t.version,by_uuids:n[o].join(",")})).data.stories.forEach(u=>{s.push(u)})}else s=e.links;s.forEach(i=>{this.links[r][i.uuid]={...i,_stopResolving:!0}})}async resolveRelations(e,t,r){let s=[];if(e.rel_uuids){const i=e.rel_uuids.length,n=[],l=50;for(let o=0;o<i;o+=l){const h=Math.min(i,o+l);n.push(e.rel_uuids.slice(o,h))}for(let o=0;o<n.length;o++)(await this.getStories({per_page:l,language:t.language,version:t.version,by_uuids:n[o].join(","),excluding_fields:t.excluding_fields})).data.stories.forEach(u=>{s.push(u)})}else s=e.rels;s&&s.length>0&&s.forEach(i=>{this.relations[r][i.uuid]={...i,_stopResolving:!0}})}async resolveStories(e,t,r){var i,n;let s=[];if(this.links[r]={},this.relations[r]={},typeof t.resolve_relations<"u"&&t.resolve_relations.length>0&&(typeof t.resolve_relations=="string"&&(s=t.resolve_relations.split(",")),await this.resolveRelations(e,t,r)),t.resolve_links&&["1","story","url","link"].indexOf(t.resolve_links)>-1&&((i=e.links)!=null&&i.length||(n=e.link_uuids)!=null&&n.length)&&await this.resolveLinks(e,t,r),this.resolveNestedRelations)for(const l in this.relations[r])this.iterateTree(this.relations[r][l],s,r);e.story?this.iterateTree(e.story,s,r):e.stories.forEach(l=>{this.iterateTree(l,s,r)}),this.stringifiedStoriesCache={},delete this.links[r],delete this.relations[r]}async cacheResponse(e,t,r,s){const i=this.helpers.stringify({url:e,params:t}),n=this.cacheProvider();if(this.cache.clear==="auto"&&t.version==="draft"&&await this.flushCache(),t.version==="published"&&e!="/cdn/spaces/me"){const l=await n.get(i);if(l)return Promise.resolve(l)}return new Promise(async(l,o)=>{var h;try{const u=await this.throttle("get",e,t,s);if(u.status!==200)return o(u);let f={data:u.data,headers:u.headers};if((h=u.headers)!=null&&h["per-page"]&&(f=Object.assign({},f,{perPage:u.headers["per-page"]?parseInt(u.headers["per-page"]):0,total:u.headers["per-page"]?parseInt(u.headers.total):0})),f.data.story||f.data.stories){const m=this.resolveCounter=++this.resolveCounter%1e3;await this.resolveStories(f.data,t,`${m}`)}return t.version==="published"&&e!="/cdn/spaces/me"&&await n.set(i,f),f.data.cv&&t.token&&k[t.token]!=f.data.cv&&(await this.flushCache(),k[t.token]=f.data.cv),l(f)}catch(u){if(u.response&&u.status===429&&(r=typeof r>"u"?0:r+1,r<this.maxRetries))return console.log(`Hit rate limit. Retrying in ${this.retriesDelay/1e3} seconds.`),await this.helpers.delay(this.retriesDelay),this.cacheResponse(e,t,r).then(l).catch(o);o(u)}})}throttledRequest(e,t,r,s){return this.client.setFetchOptions(s),this.client[e](t,r)}cacheVersions(){return k}cacheVersion(){return k[this.accessToken]}setCacheVersion(e){this.accessToken&&(k[this.accessToken]=e)}clearCacheVersion(){this.accessToken&&(k[this.accessToken]=0)}cacheProvider(){switch(this.cache.type){case"memory":return{get(e){return Promise.resolve(b[e])},getAll(){return Promise.resolve(b)},set(e,t){return b[e]=t,Promise.resolve(void 0)},flush(){return b={},Promise.resolve(void 0)}};case"custom":if(this.cache.custom)return this.cache.custom;default:return{get(){return Promise.resolve()},getAll(){return Promise.resolve(void 0)},set(){return Promise.resolve(void 0)},flush(){return Promise.resolve(void 0)}}}}async flushCache(){return await this.cacheProvider().flush(),this.clearCacheVersion(),this}}const A=(a,e)=>{for(const t in e)a[t]=e[t]};return A(T,{RichtextResolver:R,SbFetch:x,RichTextSchema:w}),A(T,O),T}); | ||
(function(g,d){typeof exports=="object"&&typeof module<"u"?module.exports=d():typeof define=="function"&&define.amd?define(d):(g=typeof globalThis<"u"?globalThis:g||self,g.StoryblokJSClient=d())})(this,function(){"use strict";var N=Object.defineProperty;var H=(g,d,y)=>d in g?N(g,d,{enumerable:!0,configurable:!0,writable:!0,value:y}):g[d]=y;var c=(g,d,y)=>H(g,typeof d!="symbol"?d+"":d,y);class g extends Error{constructor(e){super(e),this.name="AbortError"}}function d(a,e,t){if(!Number.isFinite(e))throw new TypeError("Expected `limit` to be a finite number");if(!Number.isFinite(t))throw new TypeError("Expected `interval` to be a finite number");const r=[];let s=[],i=0,n=!1;const l=async()=>{i++;const h=r.shift();if(h){const f=await a(...h.args);h.resolve(f)}const u=setTimeout(()=>{i--,r.length>0&&l(),s=s.filter(f=>f!==u)},t);s.includes(u)||s.push(u)},o=(...h)=>n?Promise.reject(new Error("Throttled function is already aborted and not accepting new promises")):new Promise((u,f)=>{r.push({resolve:u,reject:f,args:h}),i<e&&l()});return o.abort=()=>{n=!0,s.forEach(clearTimeout),s=[],r.forEach(h=>h.reject(()=>new g("Throttle function aborted"))),r.length=0},o}class y{constructor(){c(this,"isCDNUrl",(e="")=>e.includes("/cdn/"));c(this,"getOptionsPage",(e,t=25,r=1)=>({...e,per_page:t,page:r}));c(this,"delay",e=>new Promise(t=>setTimeout(t,e)));c(this,"arrayFrom",(e=0,t)=>Array.from({length:e},t));c(this,"range",(e=0,t=e)=>{const r=Math.abs(t-e)||0,s=e<t?1:-1;return this.arrayFrom(r,(i,n)=>n*s+e)});c(this,"asyncMap",async(e,t)=>Promise.all(e.map(t)));c(this,"flatMap",(e=[],t)=>e.map(t).reduce((r,s)=>[...r,...s],[]));c(this,"escapeHTML",function(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"},r=/[&<>"']/g,s=new RegExp(r.source);return e&&s.test(e)?e.replace(r,i=>t[i]):e})}stringify(e,t,r){const s=[];for(const i in e){if(!Object.prototype.hasOwnProperty.call(e,i))continue;const n=e[i],l=r?"":encodeURIComponent(i);let o;typeof n=="object"?o=this.stringify(n,t?t+encodeURIComponent(`[${l}]`):l,Array.isArray(n)):o=`${t?t+encodeURIComponent(`[${l}]`):l}=${encodeURIComponent(n)}`,s.push(o)}return s.join("&")}getRegionURL(e){const t="api.storyblok.com",r="api-us.storyblok.com",s="app.storyblokchina.cn",i="api-ap.storyblok.com",n="api-ca.storyblok.com";switch(e){case"us":return r;case"cn":return s;case"ap":return i;case"ca":return n;default:return t}}}const E=Object.freeze(Object.defineProperty({__proto__:null,SbHelpers:y},Symbol.toStringTag,{value:"Module"})),L=function(a,e){const t={};for(const r in a){const s=a[r];e.includes(r)&&s!==null&&(t[r]=s)}return t},O=a=>a==="email",w={nodes:{horizontal_rule:()=>({singleTag:"hr"}),blockquote:()=>({tag:"blockquote"}),bullet_list:()=>({tag:"ul"}),code_block:a=>({tag:["pre",{tag:"code",attrs:a.attrs}]}),hard_break:()=>({singleTag:"br"}),heading:a=>({tag:`h${a.attrs.level}`}),image:a=>({singleTag:[{tag:"img",attrs:L(a.attrs,["src","alt","title"])}]}),list_item:()=>({tag:"li"}),ordered_list:()=>({tag:"ol"}),paragraph:()=>({tag:"p"}),emoji:a=>({tag:[{tag:"span",attrs:{"data-type":"emoji","data-name":a.attrs.name,emoji:a.attrs.emoji}}]})},marks:{bold:()=>({tag:"b"}),strike:()=>({tag:"s"}),underline:()=>({tag:"u"}),strong:()=>({tag:"strong"}),code:()=>({tag:"code"}),italic:()=>({tag:"i"}),link:a=>{if(!a.attrs)return{tag:""};const e=new y().escapeHTML,t={...a.attrs},{linktype:r="url"}=a.attrs;if(delete t.linktype,t.href&&(t.href=e(a.attrs.href||"")),O(r)&&(t.href=`mailto:${t.href}`),t.anchor&&(t.href=`${t.href}#${t.anchor}`,delete t.anchor),t.custom){for(const s in t.custom)t[s]=t.custom[s];delete t.custom}return{tag:[{tag:"a",attrs:t}]}},styled:a=>({tag:[{tag:"span",attrs:a.attrs}]}),subscript:()=>({tag:"sub"}),superscript:()=>({tag:"sup"}),anchor:a=>({tag:[{tag:"span",attrs:a.attrs}]}),highlight:a=>{var t;return(t=a.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`background-color:${a.attrs.color};`}}]}:{tag:""}},textStyle:a=>{var t;return(t=a.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`color:${a.attrs.color}`}}]}:{tag:""}}}},j=function(a){const e={"&":"&","<":"<",">":">",'"':""","'":"'"},t=/[&<>"']/g,r=new RegExp(t.source);return a&&r.test(a)?a.replace(t,s=>e[s]):a};let S=!1;class R{constructor(e){c(this,"marks");c(this,"nodes");e||(e=w),this.marks=e.marks||[],this.nodes=e.nodes||[]}addNode(e,t){this.nodes[e]=t}addMark(e,t){this.marks[e]=t}render(e,t={optimizeImages:!1},r=!0){if(!S&&r&&(console.warn("Warning ⚠️: The RichTextResolver class is deprecated and will be removed in the next major release. Please use the `@storyblok/richtext` package instead. https://github.com/storyblok/richtext/"),S=!0),e&&e.content&&Array.isArray(e.content)){let s="";return e.content.forEach(i=>{s+=this.renderNode(i)}),t.optimizeImages?this.optimizeImages(s,t.optimizeImages):s}return console.warn(`The render method must receive an Object with a "content" field. | ||
The "content" field must be an array of nodes as the type ISbRichtext. | ||
ISbRichtext: | ||
content?: ISbRichtext[] | ||
marks?: ISbRichtext[] | ||
attrs?: any | ||
text?: string | ||
type: string | ||
Example: | ||
{ | ||
content: [ | ||
{ | ||
content: [ | ||
{ | ||
text: 'Hello World', | ||
type: 'text' | ||
} | ||
], | ||
type: 'paragraph' | ||
} | ||
], | ||
type: 'doc' | ||
}`),""}optimizeImages(e,t){let r=0,s=0,i="",n="";typeof t!="boolean"&&(typeof t.width=="number"&&t.width>0&&(i+=`width="${t.width}" `,r=t.width),typeof t.height=="number"&&t.height>0&&(i+=`height="${t.height}" `,s=t.height),(t.loading==="lazy"||t.loading==="eager")&&(i+=`loading="${t.loading}" `),typeof t.class=="string"&&t.class.length>0&&(i+=`class="${t.class}" `),t.filters&&(typeof t.filters.blur=="number"&&t.filters.blur>=0&&t.filters.blur<=100&&(n+=`:blur(${t.filters.blur})`),typeof t.filters.brightness=="number"&&t.filters.brightness>=-100&&t.filters.brightness<=100&&(n+=`:brightness(${t.filters.brightness})`),t.filters.fill&&(t.filters.fill.match(/[0-9A-F]{6}/gi)||t.filters.fill==="transparent")&&(n+=`:fill(${t.filters.fill})`),t.filters.format&&["webp","png","jpeg"].includes(t.filters.format)&&(n+=`:format(${t.filters.format})`),typeof t.filters.grayscale=="boolean"&&t.filters.grayscale&&(n+=":grayscale()"),typeof t.filters.quality=="number"&&t.filters.quality>=0&&t.filters.quality<=100&&(n+=`:quality(${t.filters.quality})`),t.filters.rotate&&[90,180,270].includes(t.filters.rotate)&&(n+=`:rotate(${t.filters.rotate})`),n.length>0&&(n=`/filters${n}`))),i.length>0&&(e=e.replace(/<img/g,`<img ${i.trim()}`));const l=r>0||s>0||n.length>0?`${r}x${s}${n}`:"";return e=e.replace(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g,`a.storyblok.com/f/$1/$2.$3/m/${l}`),typeof t!="boolean"&&(t.sizes||t.srcset)&&(e=e.replace(/<img.*?src=["|'](.*?)["|']/g,o=>{var u,f;const h=o.match(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g);if(h&&h.length>0){const m={srcset:(u=t.srcset)==null?void 0:u.map(p=>{if(typeof p=="number")return`//${h}/m/${p}x0${n} ${p}w`;if(typeof p=="object"&&p.length===2){let $=0,C=0;return typeof p[0]=="number"&&($=p[0]),typeof p[1]=="number"&&(C=p[1]),`//${h}/m/${$}x${C}${n} ${$}w`}return""}).join(", "),sizes:(f=t.sizes)==null?void 0:f.map(p=>p).join(", ")};let v="";return m.srcset&&(v+=`srcset="${m.srcset}" `),m.sizes&&(v+=`sizes="${m.sizes}" `),o.replace(/<img/g,`<img ${v.trim()}`)}return o})),e}renderNode(e){const t=[];e.marks&&e.marks.forEach(s=>{const i=this.getMatchingMark(s);i&&i.tag!==""&&t.push(this.renderOpeningTag(i.tag))});const r=this.getMatchingNode(e);return r&&r.tag&&t.push(this.renderOpeningTag(r.tag)),e.content?e.content.forEach(s=>{t.push(this.renderNode(s))}):e.text?t.push(j(e.text)):r&&r.singleTag?t.push(this.renderTag(r.singleTag," /")):r&&r.html?t.push(r.html):e.type==="emoji"&&t.push(this.renderEmoji(e)),r&&r.tag&&t.push(this.renderClosingTag(r.tag)),e.marks&&e.marks.slice(0).reverse().forEach(s=>{const i=this.getMatchingMark(s);i&&i.tag!==""&&t.push(this.renderClosingTag(i.tag))}),t.join("")}renderTag(e,t){return e.constructor===String?`<${e}${t}>`:e.map(s=>{if(s.constructor===String)return`<${s}${t}>`;{let i=`<${s.tag}`;if(s.attrs){for(const n in s.attrs)if(Object.prototype.hasOwnProperty.call(s.attrs,n)){const l=s.attrs[n];l!==null&&(i+=` ${n}="${l}"`)}}return`${i}${t}>`}}).join("")}renderOpeningTag(e){return this.renderTag(e,"")}renderClosingTag(e){return e.constructor===String?`</${e}>`:e.slice(0).reverse().map(r=>r.constructor===String?`</${r}>`:`</${r.tag}>`).join("")}getMatchingNode(e){const t=this.nodes[e.type];if(typeof t=="function")return t(e)}getMatchingMark(e){const t=this.marks[e.type];if(typeof t=="function")return t(e)}renderEmoji(e){if(e.attrs.emoji)return e.attrs.emoji;const t=[{tag:"img",attrs:{src:e.attrs.fallbackImage,draggable:"false",loading:"lazy",align:"absmiddle"}}];return this.renderTag(t," /")}}class x{constructor(e){c(this,"baseURL");c(this,"timeout");c(this,"headers");c(this,"responseInterceptor");c(this,"fetch");c(this,"ejectInterceptor");c(this,"url");c(this,"parameters");c(this,"fetchOptions");this.baseURL=e.baseURL,this.headers=e.headers||new Headers,this.timeout=e!=null&&e.timeout?e.timeout*1e3:0,this.responseInterceptor=e.responseInterceptor,this.fetch=(...t)=>e.fetch?e.fetch(...t):fetch(...t),this.ejectInterceptor=!1,this.url="",this.parameters={},this.fetchOptions={}}get(e,t){return this.url=e,this.parameters=t,this._methodHandler("get")}post(e,t){return this.url=e,this.parameters=t,this._methodHandler("post")}put(e,t){return this.url=e,this.parameters=t,this._methodHandler("put")}delete(e,t){return this.url=e,this.parameters=t,this._methodHandler("delete")}async _responseHandler(e){const t=[],r={data:{},headers:{},status:0,statusText:""};e.status!==204&&await e.json().then(s=>{r.data=s});for(const s of e.headers.entries())t[s[0]]=s[1];return r.headers={...t},r.status=e.status,r.statusText=e.statusText,r}async _methodHandler(e){let t=`${this.baseURL}${this.url}`,r=null;if(e==="get"){const o=new y;t=`${this.baseURL}${this.url}?${o.stringify(this.parameters)}`}else r=JSON.stringify(this.parameters);const s=new URL(t),i=new AbortController,{signal:n}=i;let l;this.timeout&&(l=setTimeout(()=>i.abort(),this.timeout));try{const o=await this.fetch(`${s}`,{method:e,headers:this.headers,body:r,signal:n,...this.fetchOptions});this.timeout&&clearTimeout(l);const h=await this._responseHandler(o);return this.responseInterceptor&&!this.ejectInterceptor?this._statusHandler(this.responseInterceptor(h)):this._statusHandler(h)}catch(o){return{message:o}}}setFetchOptions(e={}){Object.keys(e).length>0&&"method"in e&&delete e.method,this.fetchOptions={...e}}eject(){this.ejectInterceptor=!0}_statusHandler(e){const t=/20[0-6]/g;return new Promise((r,s)=>{if(t.test(`${e.status}`))return r(e);const i={message:e.statusText,status:e.status,response:Array.isArray(e.data)?e.data[0]:e.data.error||e.data.slug};s(i)})}}const P="SB-Agent",_={defaultAgentName:"SB-JS-CLIENT",defaultAgentVersion:"SB-Agent-Version",packageVersion:"6.0.0"};let k={};const b={};class T{constructor(e,t){c(this,"client");c(this,"maxRetries");c(this,"retriesDelay");c(this,"throttle");c(this,"accessToken");c(this,"cache");c(this,"helpers");c(this,"resolveCounter");c(this,"relations");c(this,"links");c(this,"richTextResolver");c(this,"resolveNestedRelations");c(this,"stringifiedStoriesCache");let r=e.endpoint||t;if(!r){const n=new y().getRegionURL,l=e.https===!1?"http":"https";e.oauthToken?r=`${l}://${n(e.region)}/v1`:r=`${l}://${n(e.region)}/v2`}const s=new Headers;s.set("Content-Type","application/json"),s.set("Accept","application/json"),e.headers&&(e.headers.constructor.name==="Headers"?e.headers.entries().toArray():Object.entries(e.headers)).forEach(([l,o])=>{s.set(l,o)}),s.has(P)||(s.set(P,_.defaultAgentName),s.set(_.defaultAgentVersion,_.packageVersion));let i=5;e.oauthToken&&(s.set("Authorization",e.oauthToken),i=3),e.rateLimit&&(i=e.rateLimit),e.richTextSchema?this.richTextResolver=new R(e.richTextSchema):this.richTextResolver=new R,e.componentResolver&&this.setComponentResolver(e.componentResolver),this.maxRetries=e.maxRetries||10,this.retriesDelay=300,this.throttle=d(this.throttledRequest.bind(this),i,1e3),this.accessToken=e.accessToken||"",this.relations={},this.links={},this.cache=e.cache||{clear:"manual"},this.helpers=new y,this.resolveCounter=0,this.resolveNestedRelations=e.resolveNestedRelations||!0,this.stringifiedStoriesCache={},this.client=new x({baseURL:r,timeout:e.timeout||0,headers:s,responseInterceptor:e.responseInterceptor,fetch:e.fetch})}setComponentResolver(e){this.richTextResolver.addNode("blok",t=>{let r="";return t.attrs.body&&t.attrs.body.forEach(s=>{r+=e(s.component,s)}),{html:r}})}parseParams(e){return e.token||(e.token=this.getToken()),e.cv||(e.cv=b[e.token]),Array.isArray(e.resolve_relations)&&(e.resolve_relations=e.resolve_relations.join(",")),typeof e.resolve_relations<"u"&&(e.resolve_level=2),e}factoryParamOptions(e,t){return this.helpers.isCDNUrl(e)?this.parseParams(t):t}makeRequest(e,t,r,s,i){const n=this.factoryParamOptions(e,this.helpers.getOptionsPage(t,r,s));return this.cacheResponse(e,n,void 0,i)}get(e,t,r){t||(t={});const s=`/${e}`,i=this.factoryParamOptions(s,t);return this.cacheResponse(s,i,void 0,r)}async getAll(e,t,r,s){const i=(t==null?void 0:t.per_page)||25,n=`/${e}`.replace(/\/$/,""),l=r??n.substring(n.lastIndexOf("/")+1),o=1,h=await this.makeRequest(n,t,i,o,s),u=h.total?Math.ceil(h.total/i):1,f=await this.helpers.asyncMap(this.helpers.range(o,u),m=>this.makeRequest(n,t,i,m+1,s));return this.helpers.flatMap([h,...f],m=>Object.values(m.data[l]))}post(e,t,r){const s=`/${e}`;return Promise.resolve(this.throttle("post",s,t,r))}put(e,t,r){const s=`/${e}`;return Promise.resolve(this.throttle("put",s,t,r))}delete(e,t,r){const s=`/${e}`;return Promise.resolve(this.throttle("delete",s,t,r))}getStories(e,t){return this._addResolveLevel(e),this.get("cdn/stories",e,t)}getStory(e,t,r){return this._addResolveLevel(t),this.get(`cdn/stories/${e}`,t,r)}getToken(){return this.accessToken}ejectInterceptor(){this.client.eject()}_addResolveLevel(e){typeof e.resolve_relations<"u"&&(e.resolve_level=2)}_cleanCopy(e){return JSON.parse(JSON.stringify(e))}_insertLinks(e,t,r){const s=e[t];s&&s.fieldtype==="multilink"&&s.linktype==="story"&&typeof s.id=="string"&&this.links[r][s.id]?s.story=this._cleanCopy(this.links[r][s.id]):s&&s.linktype==="story"&&typeof s.uuid=="string"&&this.links[r][s.uuid]&&(s.story=this._cleanCopy(this.links[r][s.uuid]))}getStoryReference(e,t){return this.relations[e][t]?(this.stringifiedStoriesCache[t]||(this.stringifiedStoriesCache[t]=JSON.stringify(this.relations[e][t])),JSON.parse(this.stringifiedStoriesCache[t])):t}_insertRelations(e,t,r,s){r.includes(`${e.component}.${t}`)&&(typeof e[t]=="string"?e[t]=this.getStoryReference(s,e[t]):Array.isArray(e[t])&&(e[t]=e[t].map(i=>this.getStoryReference(s,i)).filter(Boolean)))}iterateTree(e,t,r){const s=i=>{if(i!=null){if(i.constructor===Array)for(let n=0;n<i.length;n++)s(i[n]);else if(i.constructor===Object){if(i._stopResolving)return;for(const n in i)(i.component&&i._uid||i.type==="link")&&(this._insertRelations(i,n,t,r),this._insertLinks(i,n,r)),s(i[n])}}};s(e.content)}async resolveLinks(e,t,r){let s=[];if(e.link_uuids){const i=e.link_uuids.length,n=[],l=50;for(let o=0;o<i;o+=l){const h=Math.min(i,o+l);n.push(e.link_uuids.slice(o,h))}for(let o=0;o<n.length;o++)(await this.getStories({per_page:l,language:t.language,version:t.version,by_uuids:n[o].join(",")})).data.stories.forEach(u=>{s.push(u)})}else s=e.links;s.forEach(i=>{this.links[r][i.uuid]={...i,_stopResolving:!0}})}async resolveRelations(e,t,r){let s=[];if(e.rel_uuids){const i=e.rel_uuids.length,n=[],l=50;for(let o=0;o<i;o+=l){const h=Math.min(i,o+l);n.push(e.rel_uuids.slice(o,h))}for(let o=0;o<n.length;o++)(await this.getStories({per_page:l,language:t.language,version:t.version,by_uuids:n[o].join(","),excluding_fields:t.excluding_fields})).data.stories.forEach(u=>{s.push(u)})}else s=e.rels;s&&s.length>0&&s.forEach(i=>{this.relations[r][i.uuid]={...i,_stopResolving:!0}})}async resolveStories(e,t,r){var i,n;let s=[];if(this.links[r]={},this.relations[r]={},typeof t.resolve_relations<"u"&&t.resolve_relations.length>0&&(typeof t.resolve_relations=="string"&&(s=t.resolve_relations.split(",")),await this.resolveRelations(e,t,r)),t.resolve_links&&["1","story","url","link"].includes(t.resolve_links)&&((i=e.links)!=null&&i.length||(n=e.link_uuids)!=null&&n.length)&&await this.resolveLinks(e,t,r),this.resolveNestedRelations)for(const l in this.relations[r])this.iterateTree(this.relations[r][l],s,r);e.story?this.iterateTree(e.story,s,r):e.stories.forEach(l=>{this.iterateTree(l,s,r)}),this.stringifiedStoriesCache={},delete this.links[r],delete this.relations[r]}async cacheResponse(e,t,r,s){const i=this.helpers.stringify({url:e,params:t}),n=this.cacheProvider();if(this.cache.clear==="auto"&&t.version==="draft"&&await this.flushCache(),t.version==="published"&&e!=="/cdn/spaces/me"){const l=await n.get(i);if(l)return Promise.resolve(l)}return new Promise(async(l,o)=>{var h;try{const u=await this.throttle("get",e,t,s);if(u.status!==200)return o(u);let f={data:u.data,headers:u.headers};if((h=u.headers)!=null&&h["per-page"]&&(f=Object.assign({},f,{perPage:u.headers["per-page"]?Number.parseInt(u.headers["per-page"]):0,total:u.headers["per-page"]?Number.parseInt(u.headers.total):0})),f.data.story||f.data.stories){const m=this.resolveCounter=++this.resolveCounter%1e3;await this.resolveStories(f.data,t,`${m}`)}return t.version==="published"&&e!=="/cdn/spaces/me"&&await n.set(i,f),f.data.cv&&t.token&&b[t.token]!==f.data.cv&&(await this.flushCache(),b[t.token]=f.data.cv),l(f)}catch(u){if(u.response&&u.status===429&&(r=typeof r>"u"?0:r+1,r<this.maxRetries))return console.log(`Hit rate limit. Retrying in ${this.retriesDelay/1e3} seconds.`),await this.helpers.delay(this.retriesDelay),this.cacheResponse(e,t,r).then(l).catch(o);o(u)}})}throttledRequest(e,t,r,s){return this.client.setFetchOptions(s),this.client[e](t,r)}cacheVersions(){return b}cacheVersion(){return b[this.accessToken]}setCacheVersion(e){this.accessToken&&(b[this.accessToken]=e)}clearCacheVersion(){this.accessToken&&(b[this.accessToken]=0)}cacheProvider(){switch(this.cache.type){case"memory":return{get(e){return Promise.resolve(k[e])},getAll(){return Promise.resolve(k)},set(e,t){return k[e]=t,Promise.resolve(void 0)},flush(){return k={},Promise.resolve(void 0)}};case"custom":if(this.cache.custom)return this.cache.custom;default:return{get(){return Promise.resolve()},getAll(){return Promise.resolve(void 0)},set(){return Promise.resolve(void 0)},flush(){return Promise.resolve(void 0)}}}}async flushCache(){return await this.cacheProvider().flush(),this.clearCacheVersion(),this}}const A=(a,e)=>{for(const t in e)a[t]=e[t]};return A(T,{RichtextResolver:R,SbFetch:x,RichTextSchema:w}),A(T,E),T}); |
@@ -1,24 +0,24 @@ | ||
(function(l,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(l=typeof globalThis<"u"?globalThis:l||self,l.RichTextResolver=c())})(this,function(){"use strict";var R=Object.defineProperty;var I=(l,c,h)=>c in l?R(l,c,{enumerable:!0,configurable:!0,writable:!0,value:h}):l[c]=h;var f=(l,c,h)=>I(l,typeof c!="symbol"?c+"":c,h);class l{constructor(){f(this,"isCDNUrl",(e="")=>e.indexOf("/cdn/")>-1);f(this,"getOptionsPage",(e,t=25,r=1)=>({...e,per_page:t,page:r}));f(this,"delay",e=>new Promise(t=>setTimeout(t,e)));f(this,"arrayFrom",(e=0,t)=>[...Array(e)].map(t));f(this,"range",(e=0,t=e)=>{const r=Math.abs(t-e)||0,s=e<t?1:-1;return this.arrayFrom(r,(a,i)=>i*s+e)});f(this,"asyncMap",async(e,t)=>Promise.all(e.map(t)));f(this,"flatMap",(e=[],t)=>e.map(t).reduce((r,s)=>[...r,...s],[]));f(this,"escapeHTML",function(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"},r=/[&<>"']/g,s=RegExp(r.source);return e&&s.test(e)?e.replace(r,a=>t[a]):e})}stringify(e,t,r){const s=[];for(const a in e){if(!Object.prototype.hasOwnProperty.call(e,a))continue;const i=e[a],g=r?"":encodeURIComponent(a);let u;typeof i=="object"?u=this.stringify(i,t?t+encodeURIComponent("["+g+"]"):g,Array.isArray(i)):u=(t?t+encodeURIComponent("["+g+"]"):g)+"="+encodeURIComponent(i),s.push(u)}return s.join("&")}getRegionURL(e){const t="api.storyblok.com",r="api-us.storyblok.com",s="app.storyblokchina.cn",a="api-ap.storyblok.com",i="api-ca.storyblok.com";switch(e){case"us":return r;case"cn":return s;case"ap":return a;case"ca":return i;default:return t}}}const c=function(n,e){const t={};for(const r in n){const s=n[r];e.indexOf(r)>-1&&s!==null&&(t[r]=s)}return t},h=n=>n==="email",T={nodes:{horizontal_rule:()=>({singleTag:"hr"}),blockquote:()=>({tag:"blockquote"}),bullet_list:()=>({tag:"ul"}),code_block:n=>({tag:["pre",{tag:"code",attrs:n.attrs}]}),hard_break:()=>({singleTag:"br"}),heading:n=>({tag:`h${n.attrs.level}`}),image:n=>({singleTag:[{tag:"img",attrs:c(n.attrs,["src","alt","title"])}]}),list_item:()=>({tag:"li"}),ordered_list:()=>({tag:"ol"}),paragraph:()=>({tag:"p"}),emoji:n=>({tag:[{tag:"span",attrs:{"data-type":"emoji","data-name":n.attrs.name,emoji:n.attrs.emoji}}]})},marks:{bold:()=>({tag:"b"}),strike:()=>({tag:"s"}),underline:()=>({tag:"u"}),strong:()=>({tag:"strong"}),code:()=>({tag:"code"}),italic:()=>({tag:"i"}),link:n=>{if(!n.attrs)return{tag:""};const e=new l().escapeHTML,t={...n.attrs},{linktype:r="url"}=n.attrs;if(delete t.linktype,t.href&&(t.href=e(n.attrs.href||"")),h(r)&&(t.href=`mailto:${t.href}`),t.anchor&&(t.href=`${t.href}#${t.anchor}`,delete t.anchor),t.custom){for(const s in t.custom)t[s]=t.custom[s];delete t.custom}return{tag:[{tag:"a",attrs:t}]}},styled:n=>({tag:[{tag:"span",attrs:n.attrs}]}),subscript:()=>({tag:"sub"}),superscript:()=>({tag:"sup"}),anchor:n=>({tag:[{tag:"span",attrs:n.attrs}]}),highlight:n=>{var t;return(t=n.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`background-color:${n.attrs.color};`}}]}:{tag:""}},textStyle:n=>{var t;return(t=n.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`color:${n.attrs.color}`}}]}:{tag:""}}}},x=function(n){const e={"&":"&","<":"<",">":">",'"':""","'":"'"},t=/[&<>"']/g,r=RegExp(t.source);return n&&r.test(n)?n.replace(t,s=>e[s]):n};let b=!1;class _{constructor(e){f(this,"marks");f(this,"nodes");e||(e=T),this.marks=e.marks||[],this.nodes=e.nodes||[]}addNode(e,t){this.nodes[e]=t}addMark(e,t){this.marks[e]=t}render(e,t={optimizeImages:!1},r=!0){if(!b&&r&&(console.warn("Warning ⚠️: The RichTextResolver class is deprecated and will be removed in the next major release. Please use the `@storyblok/richtext` package instead. https://github.com/storyblok/richtext/"),b=!0),e&&e.content&&Array.isArray(e.content)){let s="";return e.content.forEach(a=>{s+=this.renderNode(a)}),t.optimizeImages?this.optimizeImages(s,t.optimizeImages):s}return console.warn(`The render method must receive an Object with a "content" field. | ||
The "content" field must be an array of nodes as the type ISbRichtext. | ||
ISbRichtext: | ||
content?: ISbRichtext[] | ||
marks?: ISbRichtext[] | ||
attrs?: any | ||
text?: string | ||
type: string | ||
Example: | ||
{ | ||
content: [ | ||
{ | ||
content: [ | ||
{ | ||
text: 'Hello World', | ||
type: 'text' | ||
} | ||
], | ||
type: 'paragraph' | ||
} | ||
], | ||
type: 'doc' | ||
}`),""}optimizeImages(e,t){let r=0,s=0,a="",i="";typeof t!="boolean"&&(typeof t.width=="number"&&t.width>0&&(a+=`width="${t.width}" `,r=t.width),typeof t.height=="number"&&t.height>0&&(a+=`height="${t.height}" `,s=t.height),(t.loading==="lazy"||t.loading==="eager")&&(a+=`loading="${t.loading}" `),typeof t.class=="string"&&t.class.length>0&&(a+=`class="${t.class}" `),t.filters&&(typeof t.filters.blur=="number"&&t.filters.blur>=0&&t.filters.blur<=100&&(i+=`:blur(${t.filters.blur})`),typeof t.filters.brightness=="number"&&t.filters.brightness>=-100&&t.filters.brightness<=100&&(i+=`:brightness(${t.filters.brightness})`),t.filters.fill&&(t.filters.fill.match(/[0-9A-Fa-f]{6}/g)||t.filters.fill==="transparent")&&(i+=`:fill(${t.filters.fill})`),t.filters.format&&["webp","png","jpeg"].includes(t.filters.format)&&(i+=`:format(${t.filters.format})`),typeof t.filters.grayscale=="boolean"&&t.filters.grayscale&&(i+=":grayscale()"),typeof t.filters.quality=="number"&&t.filters.quality>=0&&t.filters.quality<=100&&(i+=`:quality(${t.filters.quality})`),t.filters.rotate&&[90,180,270].includes(t.filters.rotate)&&(i+=`:rotate(${t.filters.rotate})`),i.length>0&&(i="/filters"+i))),a.length>0&&(e=e.replace(/<img/g,`<img ${a.trim()}`));const g=r>0||s>0||i.length>0?`${r}x${s}${i}`:"";return e=e.replace(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|tiff|bmp)/g,`a.storyblok.com/f/$1/$2.$3/m/${g}`),typeof t!="boolean"&&(t.sizes||t.srcset)&&(e=e.replace(/<img.*?src=["|'](.*?)["|']/g,u=>{var k,$;const d=u.match(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|tiff|bmp)/g);if(d&&d.length>0){const m={srcset:(k=t.srcset)==null?void 0:k.map(o=>{if(typeof o=="number")return`//${d}/m/${o}x0${i} ${o}w`;if(typeof o=="object"&&o.length===2){let y=0,j=0;return typeof o[0]=="number"&&(y=o[0]),typeof o[1]=="number"&&(j=o[1]),`//${d}/m/${y}x${j}${i} ${y}w`}}).join(", "),sizes:($=t.sizes)==null?void 0:$.map(o=>o).join(", ")};let p="";return m.srcset&&(p+=`srcset="${m.srcset}" `),m.sizes&&(p+=`sizes="${m.sizes}" `),u.replace(/<img/g,`<img ${p.trim()}`)}return u})),e}renderNode(e){const t=[];e.marks&&e.marks.forEach(s=>{const a=this.getMatchingMark(s);a&&a.tag!==""&&t.push(this.renderOpeningTag(a.tag))});const r=this.getMatchingNode(e);return r&&r.tag&&t.push(this.renderOpeningTag(r.tag)),e.content?e.content.forEach(s=>{t.push(this.renderNode(s))}):e.text?t.push(x(e.text)):r&&r.singleTag?t.push(this.renderTag(r.singleTag," /")):r&&r.html?t.push(r.html):e.type==="emoji"&&t.push(this.renderEmoji(e)),r&&r.tag&&t.push(this.renderClosingTag(r.tag)),e.marks&&e.marks.slice(0).reverse().forEach(s=>{const a=this.getMatchingMark(s);a&&a.tag!==""&&t.push(this.renderClosingTag(a.tag))}),t.join("")}renderTag(e,t){return e.constructor===String?`<${e}${t}>`:e.map(s=>{if(s.constructor===String)return`<${s}${t}>`;{let a=`<${s.tag}`;if(s.attrs){for(const i in s.attrs)if(Object.prototype.hasOwnProperty.call(s.attrs,i)){const g=s.attrs[i];g!==null&&(a+=` ${i}="${g}"`)}}return`${a}${t}>`}}).join("")}renderOpeningTag(e){return this.renderTag(e,"")}renderClosingTag(e){return e.constructor===String?`</${e}>`:e.slice(0).reverse().map(r=>r.constructor===String?`</${r}>`:`</${r.tag}>`).join("")}getMatchingNode(e){const t=this.nodes[e.type];if(typeof t=="function")return t(e)}getMatchingMark(e){const t=this.marks[e.type];if(typeof t=="function")return t(e)}renderEmoji(e){if(e.attrs.emoji)return e.attrs.emoji;const t=[{tag:"img",attrs:{src:e.attrs.fallbackImage,draggable:"false",loading:"lazy",align:"absmiddle"}}];return this.renderTag(t," /")}}return _}); | ||
(function(l,c){typeof exports=="object"&&typeof module<"u"?module.exports=c():typeof define=="function"&&define.amd?define(c):(l=typeof globalThis<"u"?globalThis:l||self,l.RichTextResolver=c())})(this,function(){"use strict";var x=Object.defineProperty;var w=(l,c,h)=>c in l?x(l,c,{enumerable:!0,configurable:!0,writable:!0,value:h}):l[c]=h;var g=(l,c,h)=>w(l,typeof c!="symbol"?c+"":c,h);class l{constructor(){g(this,"isCDNUrl",(e="")=>e.includes("/cdn/"));g(this,"getOptionsPage",(e,t=25,r=1)=>({...e,per_page:t,page:r}));g(this,"delay",e=>new Promise(t=>setTimeout(t,e)));g(this,"arrayFrom",(e=0,t)=>Array.from({length:e},t));g(this,"range",(e=0,t=e)=>{const r=Math.abs(t-e)||0,s=e<t?1:-1;return this.arrayFrom(r,(a,i)=>i*s+e)});g(this,"asyncMap",async(e,t)=>Promise.all(e.map(t)));g(this,"flatMap",(e=[],t)=>e.map(t).reduce((r,s)=>[...r,...s],[]));g(this,"escapeHTML",function(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"},r=/[&<>"']/g,s=new RegExp(r.source);return e&&s.test(e)?e.replace(r,a=>t[a]):e})}stringify(e,t,r){const s=[];for(const a in e){if(!Object.prototype.hasOwnProperty.call(e,a))continue;const i=e[a],f=r?"":encodeURIComponent(a);let u;typeof i=="object"?u=this.stringify(i,t?t+encodeURIComponent(`[${f}]`):f,Array.isArray(i)):u=`${t?t+encodeURIComponent(`[${f}]`):f}=${encodeURIComponent(i)}`,s.push(u)}return s.join("&")}getRegionURL(e){const t="api.storyblok.com",r="api-us.storyblok.com",s="app.storyblokchina.cn",a="api-ap.storyblok.com",i="api-ca.storyblok.com";switch(e){case"us":return r;case"cn":return s;case"ap":return a;case"ca":return i;default:return t}}}const c=function(n,e){const t={};for(const r in n){const s=n[r];e.includes(r)&&s!==null&&(t[r]=s)}return t},h=n=>n==="email",T={nodes:{horizontal_rule:()=>({singleTag:"hr"}),blockquote:()=>({tag:"blockquote"}),bullet_list:()=>({tag:"ul"}),code_block:n=>({tag:["pre",{tag:"code",attrs:n.attrs}]}),hard_break:()=>({singleTag:"br"}),heading:n=>({tag:`h${n.attrs.level}`}),image:n=>({singleTag:[{tag:"img",attrs:c(n.attrs,["src","alt","title"])}]}),list_item:()=>({tag:"li"}),ordered_list:()=>({tag:"ol"}),paragraph:()=>({tag:"p"}),emoji:n=>({tag:[{tag:"span",attrs:{"data-type":"emoji","data-name":n.attrs.name,emoji:n.attrs.emoji}}]})},marks:{bold:()=>({tag:"b"}),strike:()=>({tag:"s"}),underline:()=>({tag:"u"}),strong:()=>({tag:"strong"}),code:()=>({tag:"code"}),italic:()=>({tag:"i"}),link:n=>{if(!n.attrs)return{tag:""};const e=new l().escapeHTML,t={...n.attrs},{linktype:r="url"}=n.attrs;if(delete t.linktype,t.href&&(t.href=e(n.attrs.href||"")),h(r)&&(t.href=`mailto:${t.href}`),t.anchor&&(t.href=`${t.href}#${t.anchor}`,delete t.anchor),t.custom){for(const s in t.custom)t[s]=t.custom[s];delete t.custom}return{tag:[{tag:"a",attrs:t}]}},styled:n=>({tag:[{tag:"span",attrs:n.attrs}]}),subscript:()=>({tag:"sub"}),superscript:()=>({tag:"sup"}),anchor:n=>({tag:[{tag:"span",attrs:n.attrs}]}),highlight:n=>{var t;return(t=n.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`background-color:${n.attrs.color};`}}]}:{tag:""}},textStyle:n=>{var t;return(t=n.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`color:${n.attrs.color}`}}]}:{tag:""}}}},_=function(n){const e={"&":"&","<":"<",">":">",'"':""","'":"'"},t=/[&<>"']/g,r=new RegExp(t.source);return n&&r.test(n)?n.replace(t,s=>e[s]):n};let b=!1;class R{constructor(e){g(this,"marks");g(this,"nodes");e||(e=T),this.marks=e.marks||[],this.nodes=e.nodes||[]}addNode(e,t){this.nodes[e]=t}addMark(e,t){this.marks[e]=t}render(e,t={optimizeImages:!1},r=!0){if(!b&&r&&(console.warn("Warning ⚠️: The RichTextResolver class is deprecated and will be removed in the next major release. Please use the `@storyblok/richtext` package instead. https://github.com/storyblok/richtext/"),b=!0),e&&e.content&&Array.isArray(e.content)){let s="";return e.content.forEach(a=>{s+=this.renderNode(a)}),t.optimizeImages?this.optimizeImages(s,t.optimizeImages):s}return console.warn(`The render method must receive an Object with a "content" field. | ||
The "content" field must be an array of nodes as the type ISbRichtext. | ||
ISbRichtext: | ||
content?: ISbRichtext[] | ||
marks?: ISbRichtext[] | ||
attrs?: any | ||
text?: string | ||
type: string | ||
Example: | ||
{ | ||
content: [ | ||
{ | ||
content: [ | ||
{ | ||
text: 'Hello World', | ||
type: 'text' | ||
} | ||
], | ||
type: 'paragraph' | ||
} | ||
], | ||
type: 'doc' | ||
}`),""}optimizeImages(e,t){let r=0,s=0,a="",i="";typeof t!="boolean"&&(typeof t.width=="number"&&t.width>0&&(a+=`width="${t.width}" `,r=t.width),typeof t.height=="number"&&t.height>0&&(a+=`height="${t.height}" `,s=t.height),(t.loading==="lazy"||t.loading==="eager")&&(a+=`loading="${t.loading}" `),typeof t.class=="string"&&t.class.length>0&&(a+=`class="${t.class}" `),t.filters&&(typeof t.filters.blur=="number"&&t.filters.blur>=0&&t.filters.blur<=100&&(i+=`:blur(${t.filters.blur})`),typeof t.filters.brightness=="number"&&t.filters.brightness>=-100&&t.filters.brightness<=100&&(i+=`:brightness(${t.filters.brightness})`),t.filters.fill&&(t.filters.fill.match(/[0-9A-F]{6}/gi)||t.filters.fill==="transparent")&&(i+=`:fill(${t.filters.fill})`),t.filters.format&&["webp","png","jpeg"].includes(t.filters.format)&&(i+=`:format(${t.filters.format})`),typeof t.filters.grayscale=="boolean"&&t.filters.grayscale&&(i+=":grayscale()"),typeof t.filters.quality=="number"&&t.filters.quality>=0&&t.filters.quality<=100&&(i+=`:quality(${t.filters.quality})`),t.filters.rotate&&[90,180,270].includes(t.filters.rotate)&&(i+=`:rotate(${t.filters.rotate})`),i.length>0&&(i=`/filters${i}`))),a.length>0&&(e=e.replace(/<img/g,`<img ${a.trim()}`));const f=r>0||s>0||i.length>0?`${r}x${s}${i}`:"";return e=e.replace(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g,`a.storyblok.com/f/$1/$2.$3/m/${f}`),typeof t!="boolean"&&(t.sizes||t.srcset)&&(e=e.replace(/<img.*?src=["|'](.*?)["|']/g,u=>{var $,k;const d=u.match(/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g);if(d&&d.length>0){const m={srcset:($=t.srcset)==null?void 0:$.map(o=>{if(typeof o=="number")return`//${d}/m/${o}x0${i} ${o}w`;if(typeof o=="object"&&o.length===2){let y=0,j=0;return typeof o[0]=="number"&&(y=o[0]),typeof o[1]=="number"&&(j=o[1]),`//${d}/m/${y}x${j}${i} ${y}w`}return""}).join(", "),sizes:(k=t.sizes)==null?void 0:k.map(o=>o).join(", ")};let p="";return m.srcset&&(p+=`srcset="${m.srcset}" `),m.sizes&&(p+=`sizes="${m.sizes}" `),u.replace(/<img/g,`<img ${p.trim()}`)}return u})),e}renderNode(e){const t=[];e.marks&&e.marks.forEach(s=>{const a=this.getMatchingMark(s);a&&a.tag!==""&&t.push(this.renderOpeningTag(a.tag))});const r=this.getMatchingNode(e);return r&&r.tag&&t.push(this.renderOpeningTag(r.tag)),e.content?e.content.forEach(s=>{t.push(this.renderNode(s))}):e.text?t.push(_(e.text)):r&&r.singleTag?t.push(this.renderTag(r.singleTag," /")):r&&r.html?t.push(r.html):e.type==="emoji"&&t.push(this.renderEmoji(e)),r&&r.tag&&t.push(this.renderClosingTag(r.tag)),e.marks&&e.marks.slice(0).reverse().forEach(s=>{const a=this.getMatchingMark(s);a&&a.tag!==""&&t.push(this.renderClosingTag(a.tag))}),t.join("")}renderTag(e,t){return e.constructor===String?`<${e}${t}>`:e.map(s=>{if(s.constructor===String)return`<${s}${t}>`;{let a=`<${s.tag}`;if(s.attrs){for(const i in s.attrs)if(Object.prototype.hasOwnProperty.call(s.attrs,i)){const f=s.attrs[i];f!==null&&(a+=` ${i}="${f}"`)}}return`${a}${t}>`}}).join("")}renderOpeningTag(e){return this.renderTag(e,"")}renderClosingTag(e){return e.constructor===String?`</${e}>`:e.slice(0).reverse().map(r=>r.constructor===String?`</${r}>`:`</${r.tag}>`).join("")}getMatchingNode(e){const t=this.nodes[e.type];if(typeof t=="function")return t(e)}getMatchingMark(e){const t=this.marks[e.type];if(typeof t=="function")return t(e)}renderEmoji(e){if(e.attrs.emoji)return e.attrs.emoji;const t=[{tag:"img",attrs:{src:e.attrs.fallbackImage,draggable:"false",loading:"lazy",align:"absmiddle"}}];return this.renderTag(t," /")}}return R}); |
@@ -1,1 +0,1 @@ | ||
(function(a,o){typeof exports=="object"&&typeof module<"u"?module.exports=o():typeof define=="function"&&define.amd?define(o):(a=typeof globalThis<"u"?globalThis:a||self,a.RichTextSchema=o())})(this,function(){"use strict";var h=Object.defineProperty;var m=(a,o,u)=>o in a?h(a,o,{enumerable:!0,configurable:!0,writable:!0,value:u}):a[o]=u;var c=(a,o,u)=>m(a,typeof o!="symbol"?o+"":o,u);class a{constructor(){c(this,"isCDNUrl",(e="")=>e.indexOf("/cdn/")>-1);c(this,"getOptionsPage",(e,t=25,n=1)=>({...e,per_page:t,page:n}));c(this,"delay",e=>new Promise(t=>setTimeout(t,e)));c(this,"arrayFrom",(e=0,t)=>[...Array(e)].map(t));c(this,"range",(e=0,t=e)=>{const n=Math.abs(t-e)||0,s=e<t?1:-1;return this.arrayFrom(n,(i,l)=>l*s+e)});c(this,"asyncMap",async(e,t)=>Promise.all(e.map(t)));c(this,"flatMap",(e=[],t)=>e.map(t).reduce((n,s)=>[...n,...s],[]));c(this,"escapeHTML",function(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"},n=/[&<>"']/g,s=RegExp(n.source);return e&&s.test(e)?e.replace(n,i=>t[i]):e})}stringify(e,t,n){const s=[];for(const i in e){if(!Object.prototype.hasOwnProperty.call(e,i))continue;const l=e[i],g=n?"":encodeURIComponent(i);let p;typeof l=="object"?p=this.stringify(l,t?t+encodeURIComponent("["+g+"]"):g,Array.isArray(l)):p=(t?t+encodeURIComponent("["+g+"]"):g)+"="+encodeURIComponent(l),s.push(p)}return s.join("&")}getRegionURL(e){const t="api.storyblok.com",n="api-us.storyblok.com",s="app.storyblokchina.cn",i="api-ap.storyblok.com",l="api-ca.storyblok.com";switch(e){case"us":return n;case"cn":return s;case"ap":return i;case"ca":return l;default:return t}}}const o=function(r,e){const t={};for(const n in r){const s=r[n];e.indexOf(n)>-1&&s!==null&&(t[n]=s)}return t},u=r=>r==="email";return{nodes:{horizontal_rule:()=>({singleTag:"hr"}),blockquote:()=>({tag:"blockquote"}),bullet_list:()=>({tag:"ul"}),code_block:r=>({tag:["pre",{tag:"code",attrs:r.attrs}]}),hard_break:()=>({singleTag:"br"}),heading:r=>({tag:`h${r.attrs.level}`}),image:r=>({singleTag:[{tag:"img",attrs:o(r.attrs,["src","alt","title"])}]}),list_item:()=>({tag:"li"}),ordered_list:()=>({tag:"ol"}),paragraph:()=>({tag:"p"}),emoji:r=>({tag:[{tag:"span",attrs:{"data-type":"emoji","data-name":r.attrs.name,emoji:r.attrs.emoji}}]})},marks:{bold:()=>({tag:"b"}),strike:()=>({tag:"s"}),underline:()=>({tag:"u"}),strong:()=>({tag:"strong"}),code:()=>({tag:"code"}),italic:()=>({tag:"i"}),link:r=>{if(!r.attrs)return{tag:""};const e=new a().escapeHTML,t={...r.attrs},{linktype:n="url"}=r.attrs;if(delete t.linktype,t.href&&(t.href=e(r.attrs.href||"")),u(n)&&(t.href=`mailto:${t.href}`),t.anchor&&(t.href=`${t.href}#${t.anchor}`,delete t.anchor),t.custom){for(const s in t.custom)t[s]=t.custom[s];delete t.custom}return{tag:[{tag:"a",attrs:t}]}},styled:r=>({tag:[{tag:"span",attrs:r.attrs}]}),subscript:()=>({tag:"sub"}),superscript:()=>({tag:"sup"}),anchor:r=>({tag:[{tag:"span",attrs:r.attrs}]}),highlight:r=>{var t;return(t=r.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`background-color:${r.attrs.color};`}}]}:{tag:""}},textStyle:r=>{var t;return(t=r.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`color:${r.attrs.color}`}}]}:{tag:""}}}}}); | ||
(function(a,o){typeof exports=="object"&&typeof module<"u"?module.exports=o():typeof define=="function"&&define.amd?define(o):(a=typeof globalThis<"u"?globalThis:a||self,a.RichTextSchema=o())})(this,function(){"use strict";var h=Object.defineProperty;var m=(a,o,u)=>o in a?h(a,o,{enumerable:!0,configurable:!0,writable:!0,value:u}):a[o]=u;var c=(a,o,u)=>m(a,typeof o!="symbol"?o+"":o,u);class a{constructor(){c(this,"isCDNUrl",(e="")=>e.includes("/cdn/"));c(this,"getOptionsPage",(e,t=25,n=1)=>({...e,per_page:t,page:n}));c(this,"delay",e=>new Promise(t=>setTimeout(t,e)));c(this,"arrayFrom",(e=0,t)=>Array.from({length:e},t));c(this,"range",(e=0,t=e)=>{const n=Math.abs(t-e)||0,s=e<t?1:-1;return this.arrayFrom(n,(i,l)=>l*s+e)});c(this,"asyncMap",async(e,t)=>Promise.all(e.map(t)));c(this,"flatMap",(e=[],t)=>e.map(t).reduce((n,s)=>[...n,...s],[]));c(this,"escapeHTML",function(e){const t={"&":"&","<":"<",">":">",'"':""","'":"'"},n=/[&<>"']/g,s=new RegExp(n.source);return e&&s.test(e)?e.replace(n,i=>t[i]):e})}stringify(e,t,n){const s=[];for(const i in e){if(!Object.prototype.hasOwnProperty.call(e,i))continue;const l=e[i],g=n?"":encodeURIComponent(i);let p;typeof l=="object"?p=this.stringify(l,t?t+encodeURIComponent(`[${g}]`):g,Array.isArray(l)):p=`${t?t+encodeURIComponent(`[${g}]`):g}=${encodeURIComponent(l)}`,s.push(p)}return s.join("&")}getRegionURL(e){const t="api.storyblok.com",n="api-us.storyblok.com",s="app.storyblokchina.cn",i="api-ap.storyblok.com",l="api-ca.storyblok.com";switch(e){case"us":return n;case"cn":return s;case"ap":return i;case"ca":return l;default:return t}}}const o=function(r,e){const t={};for(const n in r){const s=r[n];e.includes(n)&&s!==null&&(t[n]=s)}return t},u=r=>r==="email";return{nodes:{horizontal_rule:()=>({singleTag:"hr"}),blockquote:()=>({tag:"blockquote"}),bullet_list:()=>({tag:"ul"}),code_block:r=>({tag:["pre",{tag:"code",attrs:r.attrs}]}),hard_break:()=>({singleTag:"br"}),heading:r=>({tag:`h${r.attrs.level}`}),image:r=>({singleTag:[{tag:"img",attrs:o(r.attrs,["src","alt","title"])}]}),list_item:()=>({tag:"li"}),ordered_list:()=>({tag:"ol"}),paragraph:()=>({tag:"p"}),emoji:r=>({tag:[{tag:"span",attrs:{"data-type":"emoji","data-name":r.attrs.name,emoji:r.attrs.emoji}}]})},marks:{bold:()=>({tag:"b"}),strike:()=>({tag:"s"}),underline:()=>({tag:"u"}),strong:()=>({tag:"strong"}),code:()=>({tag:"code"}),italic:()=>({tag:"i"}),link:r=>{if(!r.attrs)return{tag:""};const e=new a().escapeHTML,t={...r.attrs},{linktype:n="url"}=r.attrs;if(delete t.linktype,t.href&&(t.href=e(r.attrs.href||"")),u(n)&&(t.href=`mailto:${t.href}`),t.anchor&&(t.href=`${t.href}#${t.anchor}`,delete t.anchor),t.custom){for(const s in t.custom)t[s]=t.custom[s];delete t.custom}return{tag:[{tag:"a",attrs:t}]}},styled:r=>({tag:[{tag:"span",attrs:r.attrs}]}),subscript:()=>({tag:"sub"}),superscript:()=>({tag:"sup"}),anchor:r=>({tag:[{tag:"span",attrs:r.attrs}]}),highlight:r=>{var t;return(t=r.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`background-color:${r.attrs.color};`}}]}:{tag:""}},textStyle:r=>{var t;return(t=r.attrs)!=null&&t.color?{tag:[{tag:"span",attrs:{style:`color:${r.attrs.color}`}}]}:{tag:""}}}}}); |
@@ -1,2 +0,2 @@ | ||
declare const METHOD: { | ||
declare const _METHOD: { | ||
readonly GET: "get"; | ||
@@ -8,3 +8,3 @@ readonly DELETE: "delete"; | ||
type ObjectValues<T> = T[keyof T]; | ||
type Method = ObjectValues<typeof METHOD>; | ||
type Method = ObjectValues<typeof _METHOD>; | ||
export default Method; | ||
@@ -11,0 +11,0 @@ export declare const STORYBLOK_AGENT = "SB-Agent"; |
@@ -6,4 +6,4 @@ import Client from './index'; | ||
export { default as RichtextResolver } from './richTextResolver'; | ||
export { default as RichtextSchema } from './schema'; | ||
export { default as SbFetch } from './sbFetch'; | ||
export * from './sbHelpers'; | ||
export { default as RichtextSchema } from './schema'; |
import RichTextResolver from './richTextResolver'; | ||
import { ISbStoriesParams, ISbConfig, ISbResult, ISbStories, ISbStory, ISbStoryData, ISbStoryParams, ISbContentMangmntAPI, ISbCustomFetch } from './interfaces'; | ||
type ComponentResolverFn = { | ||
import type { ISbConfig, ISbContentMangmntAPI, ISbCustomFetch, ISbResponseData, ISbResult, ISbStories, ISbStoriesParams, ISbStory, ISbStoryParams } from './interfaces'; | ||
interface ComponentResolverFn { | ||
(...args: any): any; | ||
}; | ||
type CachedVersions = { | ||
} | ||
interface CachedVersions { | ||
[key: string]: number; | ||
}; | ||
type LinksType = { | ||
} | ||
interface LinksType { | ||
[key: string]: any; | ||
}; | ||
type RelationsType = { | ||
} | ||
interface RelationsType { | ||
[key: string]: any; | ||
}; | ||
export interface ISbResponseData { | ||
link_uuids: string[]; | ||
links: string[]; | ||
rel_uuids: string[]; | ||
rels: any; | ||
story: ISbStoryData; | ||
stories: Array<ISbStoryData>; | ||
} | ||
@@ -40,3 +32,3 @@ declare class Storyblok { | ||
* @param config ISbConfig interface | ||
* @param endpoint string, optional | ||
* @param pEndpoint string, optional | ||
*/ | ||
@@ -43,0 +35,0 @@ constructor(config: ISbConfig, pEndpoint?: string); |
@@ -1,2 +0,3 @@ | ||
import { ResponseFn } from './sbFetch'; | ||
import type { ResponseFn } from './sbFetch'; | ||
import type Method from './constants'; | ||
export interface ISbStoriesParams extends Partial<ISbStoryData>, ISbMultipleStoriesData, ISbAssetsParams { | ||
@@ -54,3 +55,3 @@ resolve_level?: number; | ||
} | ||
type Dimension = { | ||
interface Dimension { | ||
id: number; | ||
@@ -62,3 +63,3 @@ name: string; | ||
updated_at: string; | ||
}; | ||
} | ||
/** | ||
@@ -226,2 +227,3 @@ * @interface ISbDimensions | ||
statusText: string; | ||
headers: any; | ||
} | ||
@@ -245,8 +247,8 @@ export interface ISbError { | ||
} | ||
export type NodeSchema = { | ||
export interface NodeSchema { | ||
(node: ISbNode): object; | ||
}; | ||
export type MarkSchema = { | ||
} | ||
export interface MarkSchema { | ||
(node: ISbNode): object; | ||
}; | ||
} | ||
export interface ISbContentMangmntAPI<Content = ISbComponentType<string> & { | ||
@@ -314,10 +316,25 @@ [index: string]: any; | ||
} | ||
export type ThrottleFn = { | ||
(...args: any): any; | ||
}; | ||
export interface Queue<T> { | ||
resolve: (value: unknown) => void; | ||
reject: (reason?: unknown) => void; | ||
args: T; | ||
} | ||
export interface ISbResponseData { | ||
link_uuids: string[]; | ||
links: string[]; | ||
rel_uuids: string[]; | ||
rels: any; | ||
story: ISbStoryData; | ||
stories: Array<ISbStoryData>; | ||
} | ||
export interface ISbThrottle<T extends (...args: Parameters<T>) => ReturnType<T>> { | ||
abort?: () => void; | ||
(...args: Parameters<T>): Promise<unknown>; | ||
} | ||
export type ISbThrottledRequest = (type: Method, url: string, params: ISbStoriesParams, fetchOptions?: ISbCustomFetch) => Promise<unknown>; | ||
export type AsyncFn = (...args: any) => [] | Promise<ISbResult>; | ||
export type ArrayFn = (...args: any) => void; | ||
export type HtmlEscapes = { | ||
export interface HtmlEscapes { | ||
[key: string]: string; | ||
}; | ||
} | ||
export interface ISbCustomFetch extends Omit<RequestInit, 'method'> { | ||
@@ -324,0 +341,0 @@ } |
@@ -1,2 +0,2 @@ | ||
import { ISbSchema, ISbRichtext } from './interfaces'; | ||
import type { ISbRichtext, ISbSchema } from './interfaces'; | ||
type OptimizeImagesOptions = boolean | { | ||
@@ -19,5 +19,5 @@ class?: string; | ||
}; | ||
type RenderOptions = { | ||
interface RenderOptions { | ||
optimizeImages?: OptimizeImagesOptions; | ||
}; | ||
} | ||
interface ISbFunction<T extends any[], R> { | ||
@@ -24,0 +24,0 @@ (...args: T): R; |
@@ -1,5 +0,5 @@ | ||
import { ISbResponse, ISbError, ISbStoriesParams, ISbCustomFetch } from './interfaces'; | ||
export type ResponseFn = { | ||
import type { ISbCustomFetch, ISbError, ISbResponse, ISbStoriesParams } from './interfaces'; | ||
export interface ResponseFn { | ||
(arg?: ISbResponse | any): any; | ||
}; | ||
} | ||
export interface ISbFetch { | ||
@@ -6,0 +6,0 @@ baseURL: string; |
@@ -1,2 +0,2 @@ | ||
import { ISbStoriesParams, ISbResult, AsyncFn } from './interfaces'; | ||
import type { AsyncFn, ISbResult, ISbStoriesParams } from './interfaces'; | ||
interface ISbParams extends ISbStoriesParams { | ||
@@ -114,6 +114,6 @@ [key: string]: any; | ||
* @method stringify | ||
* @param {Object} params | ||
* @param {String} prefix | ||
* @param {Boolean} isArray | ||
* @return {String} Stringified object | ||
* @param {object} params | ||
* @param {string} prefix | ||
* @param {boolean} isArray | ||
* @return {string} Stringified object | ||
*/ | ||
@@ -123,4 +123,4 @@ stringify(params: ISbParams, prefix?: string, isArray?: boolean): string; | ||
* @method getRegionURL | ||
* @param {String} regionCode region code, could be eu, us, cn, ap or ca | ||
* @return {String} The base URL of the region | ||
* @param {string} regionCode region code, could be eu, us, cn, ap or ca | ||
* @return {string} The base URL of the region | ||
*/ | ||
@@ -130,4 +130,4 @@ getRegionURL(regionCode?: string): string; | ||
* @method escapeHTML | ||
* @param {String} string text to be parsed | ||
* @return {String} Text parsed | ||
* @param {string} string text to be parsed | ||
* @return {string} Text parsed | ||
*/ | ||
@@ -134,0 +134,0 @@ escapeHTML: (string: string) => string; |
@@ -1,2 +0,2 @@ | ||
import { NodeSchema, MarkSchema } from './interfaces'; | ||
import type { MarkSchema, NodeSchema } from './interfaces'; | ||
declare const _default: { | ||
@@ -3,0 +3,0 @@ nodes: { |
@@ -1,15 +0,3 @@ | ||
import { ThrottleFn } from './interfaces'; | ||
type Queue = { | ||
resolve: (args: any) => any; | ||
reject: (args: any) => any; | ||
args: any[]; | ||
self: any; | ||
}; | ||
interface ISbThrottle { | ||
abort: () => any; | ||
(args: []): Promise<Queue>; | ||
name: string; | ||
AbortError?: () => void; | ||
} | ||
declare function throttledQueue(fn: ThrottleFn, limit: number, interval: number): ISbThrottle; | ||
import type { ISbThrottle } from './interfaces'; | ||
declare function throttledQueue<T extends (...args: Parameters<T>) => ReturnType<T>>(fn: T, limit: number, interval: number): ISbThrottle<T>; | ||
export default throttledQueue; |
118
package.json
{ | ||
"name": "storyblok-js-client", | ||
"module": "./dist/index.mjs", | ||
"version": "6.10.1", | ||
"packageManager": "pnpm@9.1.4", | ||
"version": "6.10.2", | ||
"packageManager": "pnpm@9.12.3", | ||
"description": "Universal JavaScript SDK for Storyblok's API", | ||
"author": "Alexander Feiglstorfer <delooks@gmail.com>", | ||
"license": "MIT", | ||
"homepage": "https://github.com/storyblok/storyblok-js-client#readme", | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/storyblok/storyblok-js-client.git" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/storyblok/storyblok-js-client/issues" | ||
}, | ||
"keywords": [ | ||
@@ -14,24 +22,25 @@ "storyblok", | ||
"sideEffects": false, | ||
"types": "./dist/types/entry.esm.d.ts", | ||
"main": "./dist/index.umd.js", | ||
"unpkg": "./dist/index.umd.js", | ||
"jsdelivr": "./dist/index.umd.js", | ||
"source": "src/index.ts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/types/entry.esm.d.ts", | ||
"import": "./dist/index.mjs", | ||
"require": "./dist/index.umd.js", | ||
"types": "./dist/types/entry.esm.d.ts" | ||
"require": "./dist/index.umd.js" | ||
}, | ||
"./richTextResolver": { | ||
"types": "./dist/types/richTextResolver.d.ts", | ||
"import": "./dist/richTextResolver.mjs", | ||
"require": "./dist/richTextResolver.umd.js", | ||
"types": "./dist/types/richTextResolver.d.ts" | ||
"require": "./dist/richTextResolver.umd.js" | ||
}, | ||
"./schema": { | ||
"types": "./dist/types/schema.d.ts", | ||
"import": "./dist/schema.mjs", | ||
"require": "./dist/schema.umd.js", | ||
"types": "./dist/types/schema.d.ts" | ||
"require": "./dist/schema.umd.js" | ||
} | ||
}, | ||
"main": "./dist/index.umd.js", | ||
"module": "./dist/index.mjs", | ||
"unpkg": "./dist/index.umd.js", | ||
"jsdelivr": "./dist/index.umd.js", | ||
"types": "./dist/types/entry.esm.d.ts", | ||
"source": "src/index.ts", | ||
"files": [ | ||
@@ -44,3 +53,2 @@ "dist", | ||
"lint": "eslint --max-warnings=0 './src/**/*.{ts,js}'", | ||
"prettier": "prettier . --write", | ||
"build": "node vite.build.mjs && tsc", | ||
@@ -55,30 +63,21 @@ "demo": "vite serve playground", | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "https://github.com/storyblok/storyblok-js-client.git" | ||
}, | ||
"author": "Alexander Feiglstorfer <delooks@gmail.com>", | ||
"bugs": { | ||
"url": "https://github.com/storyblok/storyblok-js-client/issues" | ||
}, | ||
"homepage": "https://github.com/storyblok/storyblok-js-client#readme", | ||
"devDependencies": { | ||
"@commitlint/cli": "^18.4.3", | ||
"@commitlint/config-conventional": "^18.4.3", | ||
"@tsconfig/recommended": "^1.0.7", | ||
"@typescript-eslint/eslint-plugin": "^6.12.0", | ||
"@typescript-eslint/parser": "^6.12.0", | ||
"@vitest/coverage-v8": "^2.0.5", | ||
"@vitest/ui": "^2.0.5", | ||
"eslint": "^8.54.0", | ||
"eslint-config-prettier": "^9.0.0", | ||
"husky": "^9.1.5", | ||
"@commitlint/cli": "^19.5.0", | ||
"@commitlint/config-conventional": "^19.5.0", | ||
"@storyblok/eslint-config": "^0.3.0", | ||
"@tsconfig/recommended": "^1.0.8", | ||
"@typescript-eslint/eslint-plugin": "^8.14.0", | ||
"@typescript-eslint/parser": "^8.14.0", | ||
"@vitest/coverage-v8": "^2.1.4", | ||
"@vitest/ui": "^2.1.4", | ||
"eslint": "^9.14.0", | ||
"eslint-config-prettier": "^9.1.0", | ||
"isomorphic-fetch": "^3.0.0", | ||
"kolorist": "^1.8.0", | ||
"prettier": "^3.3.3", | ||
"typescript": "^5.5.4", | ||
"vite": "^5.4.2", | ||
"vite-plugin-banner": "^0.7.1", | ||
"vite-plugin-dts": "^4.0.3", | ||
"vitest": "^2.0.5" | ||
"typescript": "^5.6.3", | ||
"vite": "^5.4.11", | ||
"vite-plugin-banner": "^0.8.0", | ||
"vite-plugin-dts": "^4.3.0", | ||
"vitest": "^2.1.4" | ||
}, | ||
@@ -101,11 +100,2 @@ "release": { | ||
}, | ||
"lint-staged": { | ||
"*.{js,jsx,css,ts,tsx}": [ | ||
"prettier --write", | ||
"eslint" | ||
], | ||
"*.md": [ | ||
"prettier --write" | ||
] | ||
}, | ||
"commitlint": { | ||
@@ -115,35 +105,3 @@ "extends": [ | ||
] | ||
}, | ||
"eslintConfig": { | ||
"env": { | ||
"browser": true, | ||
"es2021": true, | ||
"node": true | ||
}, | ||
"extends": [ | ||
"eslint:recommended", | ||
"plugin:@typescript-eslint/recommended", | ||
"prettier" | ||
], | ||
"parser": "@typescript-eslint/parser", | ||
"parserOptions": { | ||
"ecmaVersion": "latest", | ||
"sourceType": "module" | ||
}, | ||
"plugins": [ | ||
"@typescript-eslint" | ||
], | ||
"rules": { | ||
"@typescript-eslint/no-explicit-any": "off", | ||
"@typescript-eslint/no-var-requires": 1, | ||
"no-async-promise-executor": 0, | ||
"no-undef": 0 | ||
} | ||
}, | ||
"prettier": { | ||
"semi": false, | ||
"singleQuote": true, | ||
"tabWidth": 2, | ||
"trailingComma": "es5" | ||
} | ||
} |
@@ -1,2 +0,2 @@ | ||
const METHOD = { | ||
const _METHOD = { | ||
GET: 'get', | ||
@@ -6,10 +6,10 @@ DELETE: 'delete', | ||
PUT: 'put', | ||
} as const | ||
} as const; | ||
type ObjectValues<T> = T[keyof T] | ||
type Method = ObjectValues<typeof METHOD> | ||
type ObjectValues<T> = T[keyof T]; | ||
type Method = ObjectValues<typeof _METHOD>; | ||
export default Method | ||
export default Method; | ||
export const STORYBLOK_AGENT = 'SB-Agent' | ||
export const STORYBLOK_AGENT = 'SB-Agent'; | ||
@@ -20,2 +20,2 @@ export const STORYBLOK_JS_CLIENT_AGENT = { | ||
packageVersion: '6.0.0', | ||
} | ||
}; |
@@ -1,10 +0,10 @@ | ||
import Client from './index' | ||
import Client from './index'; | ||
// All default and named exports, including types for ESM bundle | ||
export default Client | ||
export * from './constants' | ||
export * from './interfaces' | ||
export { default as RichtextResolver } from './richTextResolver' | ||
export { default as RichtextSchema } from './schema' | ||
export { default as SbFetch } from './sbFetch' | ||
export * from './sbHelpers' | ||
export default Client; | ||
export * from './constants'; | ||
export * from './interfaces'; | ||
export { default as RichtextResolver } from './richTextResolver'; | ||
export { default as SbFetch } from './sbFetch'; | ||
export * from './sbHelpers'; | ||
export { default as RichtextSchema } from './schema'; |
@@ -1,15 +0,17 @@ | ||
import Client from './index' | ||
import RichtextResolver from './richTextResolver' | ||
import SbFetch from './sbFetch' | ||
import RichTextSchema from './schema' | ||
import * as sbHelpers from './sbHelpers' | ||
import Client from './index'; | ||
import RichtextResolver from './richTextResolver'; | ||
import SbFetch from './sbFetch'; | ||
import RichTextSchema from './schema'; | ||
import * as sbHelpers from './sbHelpers'; | ||
const extend = (to: Record<any, any>, _from: Record<any, any>) => { | ||
for (const key in _from) to[key] = _from[key] | ||
} | ||
for (const key in _from) { | ||
to[key] = _from[key]; | ||
} | ||
}; | ||
extend(Client, { RichtextResolver, SbFetch, RichTextSchema }) | ||
extend(Client, sbHelpers) | ||
extend(Client, { RichtextResolver, SbFetch, RichTextSchema }); | ||
extend(Client, sbHelpers); | ||
// Single default export object for UMD friendly bundle | ||
export default Client | ||
export default Client; |
@@ -1,5 +0,6 @@ | ||
import StoryblokClient from '.' | ||
import { describe, it, expect, beforeEach, vi } from 'vitest' | ||
import SbFetch, { ResponseFn } from './sbFetch' | ||
import { SbHelpers } from './sbHelpers' | ||
import StoryblokClient from '.'; | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import type { ResponseFn } from './sbFetch'; | ||
import SbFetch from './sbFetch'; | ||
import { SbHelpers } from './sbHelpers'; | ||
@@ -14,21 +15,22 @@ // Mocking external dependencies | ||
status: 200, | ||
}) | ||
const mockPost = vi.fn() | ||
const mockSetFetchOptions = vi.fn() | ||
}); | ||
const mockPost = vi.fn(); | ||
const mockSetFetchOptions = vi.fn(); | ||
// Define a mock class with baseURL property | ||
class MockSbFetch { | ||
private baseURL: string | ||
private timeout?: number | ||
private headers: Headers | ||
private helpers: any | ||
private responseInterceptor?: ResponseFn | ||
private baseURL: string; | ||
private timeout?: number; | ||
private headers: Headers; | ||
private helpers: any; | ||
private responseInterceptor?: ResponseFn; | ||
constructor(config: any) { | ||
this.helpers = new SbHelpers() | ||
this.baseURL = config.baseURL || 'https://api.storyblok.com/v2' | ||
this.responseInterceptor = config.responseInterceptor | ||
this.helpers = new SbHelpers(); | ||
this.baseURL = config.baseURL || 'https://api.storyblok.com/v2'; | ||
this.responseInterceptor = config.responseInterceptor; | ||
} | ||
public get = mockGet | ||
public post = mockPost | ||
public setFetchOptions = mockSetFetchOptions | ||
public get = mockGet; | ||
public post = mockPost; | ||
public setFetchOptions = mockSetFetchOptions; | ||
} | ||
@@ -38,7 +40,7 @@ | ||
default: MockSbFetch, | ||
} | ||
}) | ||
}; | ||
}); | ||
describe('StoryblokClient', () => { | ||
let client | ||
describe('storyblokClient', () => { | ||
let client; | ||
@@ -50,38 +52,38 @@ beforeEach(() => { | ||
/* fetch: mockFetch, */ | ||
}) | ||
}) | ||
}); | ||
}); | ||
describe('initialization', () => { | ||
it('should initialize a client instance', () => { | ||
expect(client).toBeDefined() | ||
expect(client).toBeInstanceOf(StoryblokClient) | ||
}) | ||
expect(client).toBeDefined(); | ||
expect(client).toBeInstanceOf(StoryblokClient); | ||
}); | ||
it('should initialize with default values', () => { | ||
expect(client.maxRetries).toBe(10) | ||
expect(client.retriesDelay).toBe(300) | ||
expect(client.maxRetries).toBe(10); | ||
expect(client.retriesDelay).toBe(300); | ||
expect(client.cache).toEqual({ | ||
clear: 'manual', | ||
}) | ||
expect(client.relations).toEqual({}) | ||
expect(client.links).toEqual({}) | ||
}); | ||
expect(client.relations).toEqual({}); | ||
expect(client.links).toEqual({}); | ||
// Failing test | ||
/* expect(client.helpers).toBeInstanceOf(SbHelpers) */ | ||
expect(client.resolveCounter).toBe(0) | ||
expect(client.resolveNestedRelations).toBeTruthy() | ||
expect(client.stringifiedStoriesCache).toEqual({}) | ||
}) | ||
expect(client.resolveCounter).toBe(0); | ||
expect(client.resolveNestedRelations).toBeTruthy(); | ||
expect(client.stringifiedStoriesCache).toEqual({}); | ||
}); | ||
it('should set an accessToken', () => { | ||
expect(client.accessToken).toBe('test-token') | ||
}) | ||
expect(client.accessToken).toBe('test-token'); | ||
}); | ||
it('should set an endpoint', () => { | ||
expect(client.client.baseURL).toBe('https://api.storyblok.com/v2') | ||
}) | ||
expect(client.client.baseURL).toBe('https://api.storyblok.com/v2'); | ||
}); | ||
it('should set a fetch instance', () => { | ||
expect(client.client).toBeInstanceOf(SbFetch) | ||
}) | ||
}) | ||
expect(client.client).toBeInstanceOf(SbFetch); | ||
}); | ||
}); | ||
@@ -92,7 +94,6 @@ describe('configuration via options', () => { | ||
endpoint: 'https://api-custom.storyblok.com/v2', | ||
}) | ||
}); | ||
expect(client.client.baseURL).toBe('https://api-custom.storyblok.com/v2') | ||
}) | ||
expect(client.client.baseURL).toBe('https://api-custom.storyblok.com/v2'); | ||
}); | ||
it('https: should set the http endpoint if option is set to false', () => { | ||
@@ -102,27 +103,27 @@ client = new StoryblokClient({ | ||
https: false, | ||
}) | ||
}); | ||
expect(client.client.baseURL).toBe('http://api.storyblok.com/v2') | ||
}) | ||
expect(client.client.baseURL).toBe('http://api.storyblok.com/v2'); | ||
}); | ||
it('should set the management endpoint v1 if oauthToken is available', () => { | ||
client = new StoryblokClient({ | ||
oauthToken: 'test-token', | ||
}) | ||
}); | ||
expect(client.client.baseURL).toBe('https://api.storyblok.com/v1') | ||
}) | ||
expect(client.client.baseURL).toBe('https://api.storyblok.com/v1'); | ||
}); | ||
it('should set the correct region endpoint', () => { | ||
client = new StoryblokClient({ | ||
region: 'us', | ||
}) | ||
}); | ||
expect(client.client.baseURL).toBe('https://api-us.storyblok.com/v2') | ||
}) | ||
expect(client.client.baseURL).toBe('https://api-us.storyblok.com/v2'); | ||
}); | ||
it('should set maxRetries', () => { | ||
client = new StoryblokClient({ | ||
maxRetries: 5, | ||
}) | ||
}); | ||
expect(client.maxRetries).toBe(5) | ||
}) | ||
expect(client.maxRetries).toBe(5); | ||
}); | ||
// TODO: seems like implmentation is missing | ||
@@ -132,6 +133,6 @@ it.skip('should desactivate resolveNestedRelations', () => { | ||
resolveNestedRelations: false, | ||
}) | ||
}); | ||
expect(client.resolveNestedRelations).toBeFalsy() | ||
}) | ||
expect(client.resolveNestedRelations).toBeFalsy(); | ||
}); | ||
@@ -143,53 +144,52 @@ it('should set automatic cache clearing', () => { | ||
}, | ||
}) | ||
}); | ||
expect(client.cache.clear).toBe('auto') | ||
}) | ||
expect(client.cache.clear).toBe('auto'); | ||
}); | ||
it('should set a responseInterceptor', async () => { | ||
const responseInterceptor = (response) => { | ||
return response | ||
} | ||
return response; | ||
}; | ||
client = new StoryblokClient({ | ||
responseInterceptor, | ||
}) | ||
await client.getAll('cdn/links') | ||
expect(client.client.responseInterceptor).toBe(responseInterceptor) | ||
}) | ||
}) | ||
}); | ||
await client.getAll('cdn/links'); | ||
expect(client.client.responseInterceptor).toBe(responseInterceptor); | ||
}); | ||
}); | ||
describe('cache', () => { | ||
it('should return cacheVersions', async () => { | ||
const mockThrottle = vi.fn().mockResolvedValue({ | ||
data: { | ||
data: { | ||
stories: [{ id: 1, title: 'Update' }], | ||
cv: 1645521118 | ||
cv: 1645521118, | ||
}, | ||
headers: {}, | ||
status: 200 | ||
status: 200, | ||
}); | ||
client.throttle = mockThrottle; | ||
await client.get('test', { version: 'draft', token: 'test-token' }); | ||
expect(client.cacheVersions()).toEqual({ | ||
'test-token': 1645521118 | ||
'test-token': 1645521118, | ||
}); | ||
}) | ||
}); | ||
it('should return cacheVersion', async () => { | ||
const mockThrottle = vi.fn().mockResolvedValue({ | ||
data: { | ||
data: { | ||
stories: [{ id: 1, title: 'Update' }], | ||
cv: 1645521118 | ||
cv: 1645521118, | ||
}, | ||
headers: {}, | ||
status: 200 | ||
status: 200, | ||
}); | ||
client.throttle = mockThrottle; | ||
await client.get('test', { version: 'draft', token: 'test-token' }); | ||
expect(client.cacheVersion('test-token')).toBe(1645521118); | ||
}) | ||
}); | ||
@@ -199,8 +199,8 @@ it('should set the cache version', async () => { | ||
expect(client.cacheVersions()).toEqual({ | ||
'test-token': 1645521118 | ||
'test-token': 1645521118, | ||
}); | ||
}) | ||
}); | ||
it('should clear the cache', async () => { | ||
// Mock the cacheProvider and its flush method | ||
// Mock the cacheProvider and its flush method | ||
client.cacheProvider = vi.fn().mockReturnValue({ | ||
@@ -211,7 +211,7 @@ flush: vi.fn().mockResolvedValue(undefined), | ||
client.clearCacheVersion = vi.fn(); | ||
await client.flushCache(); | ||
await client.flushCache(); | ||
expect(client.cacheProvider().flush).toHaveBeenCalled(); | ||
expect(client.clearCacheVersion).toHaveBeenCalled(); | ||
}) | ||
}); | ||
@@ -221,3 +221,3 @@ it('should clear the cache version', async () => { | ||
expect(client.cacheVersion()).toEqual(0); | ||
}) | ||
}); | ||
@@ -236,13 +236,15 @@ it('should flush the cache when the draft version is requested and clear is auto', async () => { | ||
}); | ||
}) | ||
}); | ||
describe('get', () => { | ||
it('should fetch data from the API', async () => { | ||
const result = await client.get('test') | ||
expect(result).toEqual({ data: { | ||
links: 'Test data', | ||
}, headers: {} }) | ||
}) | ||
}) | ||
const result = await client.get('test'); | ||
expect(result).toEqual({ | ||
data: { | ||
links: 'Test data', | ||
}, | ||
headers: {}, | ||
}); | ||
}); | ||
}); | ||
@@ -260,10 +262,10 @@ describe('getAll', () => { | ||
status: 200, | ||
}) | ||
client.makeRequest = mockMakeRequest | ||
const result = await client.getAll('links', { version: 'draft' }) | ||
}); | ||
client.makeRequest = mockMakeRequest; | ||
const result = await client.getAll('links', { version: 'draft' }); | ||
expect(result).toEqual([ | ||
{ id: 1, name: 'Test 1' }, | ||
{ id: 2, name: 'Test 2' }, | ||
]) | ||
}) | ||
]); | ||
}); | ||
@@ -280,10 +282,14 @@ it('should resolve using entity option', async () => { | ||
status: 200, | ||
}) | ||
client.makeRequest = mockMakeRequest | ||
const result = await client.getAll('cdn/links', { version: 'draft' }, 'custom') | ||
}); | ||
client.makeRequest = mockMakeRequest; | ||
const result = await client.getAll( | ||
'cdn/links', | ||
{ version: 'draft' }, | ||
'custom', | ||
); | ||
expect(result).toEqual([ | ||
{ id: 1, name: 'Test 1' }, | ||
{ id: 2, name: 'Test 2' }, | ||
]) | ||
}) | ||
]); | ||
}); | ||
@@ -300,7 +306,7 @@ it('should make a request for each page', async () => { | ||
status: 200, | ||
}) | ||
client.makeRequest = mockMakeRequest | ||
await client.getAll('links', { per_page: 1 }) | ||
expect(mockMakeRequest).toBeCalledTimes(2) | ||
}) | ||
}); | ||
client.makeRequest = mockMakeRequest; | ||
await client.getAll('links', { per_page: 1 }); | ||
expect(mockMakeRequest).toBeCalledTimes(2); | ||
}); | ||
@@ -317,11 +323,11 @@ it('should get all stories if the slug is passed with the trailing slash', async () => { | ||
status: 200, | ||
}) | ||
client.makeRequest = mockMakeRequest | ||
const result = await client.getAll('cdn/stories/', { version: 'draft' }) | ||
}); | ||
client.makeRequest = mockMakeRequest; | ||
const result = await client.getAll('cdn/stories/', { version: 'draft' }); | ||
expect(result).toEqual([ | ||
{ id: 1, name: 'Test Story 1' }, | ||
{ id: 2, name: 'Test Story 2' }, | ||
]) | ||
}) | ||
}) | ||
]); | ||
}); | ||
}); | ||
@@ -331,19 +337,19 @@ describe('post', () => { | ||
const mockThrottle = vi.fn().mockResolvedValue({ | ||
data: { | ||
stories: [{ id: 1, title: 'Keep me posted' }] | ||
data: { | ||
stories: [{ id: 1, title: 'Keep me posted' }], | ||
}, | ||
headers: {}, | ||
status: 200 | ||
status: 200, | ||
}); | ||
client.throttle = mockThrottle; | ||
const result = await client.post('test', { data: 'test' }) | ||
expect(result).toEqual({ | ||
const result = await client.post('test', { data: 'test' }); | ||
expect(result).toEqual({ | ||
data: { | ||
stories: [{ id: 1, title: 'Keep me posted' }] | ||
}, | ||
stories: [{ id: 1, title: 'Keep me posted' }], | ||
}, | ||
headers: {}, | ||
status: 200 | ||
}) | ||
}) | ||
}) | ||
status: 200, | ||
}); | ||
}); | ||
}); | ||
@@ -353,19 +359,19 @@ describe('put', () => { | ||
const mockThrottle = vi.fn().mockResolvedValue({ | ||
data: { | ||
stories: [{ id: 1, title: 'Update' }] | ||
data: { | ||
stories: [{ id: 1, title: 'Update' }], | ||
}, | ||
headers: {}, | ||
status: 200 | ||
status: 200, | ||
}); | ||
client.throttle = mockThrottle; | ||
const result = await client.put('test', { data: 'test' }) | ||
expect(result).toEqual({ | ||
const result = await client.put('test', { data: 'test' }); | ||
expect(result).toEqual({ | ||
data: { | ||
stories: [{ id: 1, title: 'Update' }] | ||
}, | ||
stories: [{ id: 1, title: 'Update' }], | ||
}, | ||
headers: {}, | ||
status: 200 | ||
}) | ||
}) | ||
}) | ||
status: 200, | ||
}); | ||
}); | ||
}); | ||
@@ -375,19 +381,19 @@ describe('delete', () => { | ||
const mockThrottle = vi.fn().mockResolvedValue({ | ||
data: { | ||
stories: [{ id: 1, title: 'Delete' }] | ||
data: { | ||
stories: [{ id: 1, title: 'Delete' }], | ||
}, | ||
headers: {}, | ||
status: 200 | ||
status: 200, | ||
}); | ||
client.throttle = mockThrottle; | ||
const result = await client.delete('test') | ||
expect(result).toEqual({ | ||
const result = await client.delete('test'); | ||
expect(result).toEqual({ | ||
data: { | ||
stories: [{ id: 1, title: 'Delete' }] | ||
}, | ||
stories: [{ id: 1, title: 'Delete' }], | ||
}, | ||
headers: {}, | ||
status: 200 | ||
}) | ||
}) | ||
}) | ||
status: 200, | ||
}); | ||
}); | ||
}); | ||
@@ -398,3 +404,3 @@ it('should resolve stories when response contains a story or stories', async () => { | ||
headers: {}, | ||
status: 200 | ||
status: 200, | ||
}); | ||
@@ -406,5 +412,8 @@ client.throttle = mockThrottle; | ||
}); | ||
await client.cacheResponse('/test-url', { token: 'test-token', version: 'published' }); | ||
await client.cacheResponse('/test-url', { | ||
token: 'test-token', | ||
version: 'published', | ||
}); | ||
expect(client.resolveStories).toHaveBeenCalled(); | ||
@@ -414,8 +423,5 @@ expect(client.resolveCounter).toBe(1); | ||
it('should return access token', () => { | ||
it('should return access token', () => { | ||
expect(client.getToken()).toBe('test-token'); | ||
}) | ||
}) | ||
}); | ||
}); |
600
src/index.ts
@@ -1,82 +0,74 @@ | ||
import throttledQueue from './throttlePromise' | ||
import RichTextResolver from './richTextResolver' | ||
import { SbHelpers } from './sbHelpers' | ||
import SbFetch from './sbFetch' | ||
import { STORYBLOK_AGENT, STORYBLOK_JS_CLIENT_AGENT } from './constants' | ||
import throttledQueue from './throttlePromise'; | ||
import RichTextResolver from './richTextResolver'; | ||
import { SbHelpers } from './sbHelpers'; | ||
import SbFetch from './sbFetch'; | ||
import type Method from './constants'; | ||
import { STORYBLOK_AGENT, STORYBLOK_JS_CLIENT_AGENT } from './constants'; | ||
import Method from './constants' | ||
import { | ||
ISbStoriesParams, | ||
import type { | ||
ICacheProvider, | ||
IMemoryType, | ||
ISbCache, | ||
ISbConfig, | ||
ISbContentMangmntAPI, | ||
ISbCustomFetch, | ||
ISbLinkURLObject, | ||
ISbNode, | ||
ISbResponse, | ||
ISbResponseData, | ||
ISbResult, | ||
ISbStories, | ||
ISbStoriesParams, | ||
ISbStory, | ||
ISbStoryData, | ||
ISbStoryParams, | ||
ISbContentMangmntAPI, | ||
ISbNode, | ||
ThrottleFn, | ||
IMemoryType, | ||
ICacheProvider, | ||
ISbCustomFetch, | ||
} from './interfaces' | ||
} from './interfaces'; | ||
let memory: Partial<IMemoryType> = {} | ||
let memory: Partial<IMemoryType> = {}; | ||
const cacheVersions = {} as CachedVersions | ||
const cacheVersions = {} as CachedVersions; | ||
type ComponentResolverFn = { | ||
(...args: any): any | ||
interface ComponentResolverFn { | ||
(...args: any): any; | ||
} | ||
type CachedVersions = { | ||
[key: string]: number | ||
interface CachedVersions { | ||
[key: string]: number; | ||
} | ||
type LinksType = { | ||
[key: string]: any | ||
interface LinksType { | ||
[key: string]: any; | ||
} | ||
type RelationsType = { | ||
[key: string]: any | ||
interface RelationsType { | ||
[key: string]: any; | ||
} | ||
interface ISbFlatMapped { | ||
data: any | ||
data: any; | ||
} | ||
export interface ISbResponseData { | ||
link_uuids: string[] | ||
links: string[] | ||
rel_uuids: string[] | ||
rels: any | ||
story: ISbStoryData | ||
stories: Array<ISbStoryData> | ||
} | ||
const VERSION = { | ||
const _VERSION = { | ||
V1: 'v1', | ||
V2: 'v2', | ||
} as const | ||
} as const; | ||
type ObjectValues<T> = T[keyof T] | ||
type Version = ObjectValues<typeof VERSION> | ||
type ObjectValues<T> = T[keyof T]; | ||
type Version = ObjectValues<typeof _VERSION>; | ||
class Storyblok { | ||
private client: SbFetch | ||
private maxRetries: number | ||
private retriesDelay: number | ||
private throttle: ThrottleFn | ||
private accessToken: string | ||
private cache: ISbCache | ||
private helpers: SbHelpers | ||
private resolveCounter: number | ||
public relations: RelationsType | ||
public links: LinksType | ||
private client: SbFetch; | ||
private maxRetries: number; | ||
private retriesDelay: number; | ||
private throttle: ReturnType<typeof throttledQueue>; | ||
private accessToken: string; | ||
private cache: ISbCache; | ||
private helpers: SbHelpers; | ||
private resolveCounter: number; | ||
public relations: RelationsType; | ||
public links: LinksType; | ||
// TODO: Remove on v7.x.x | ||
public richTextResolver: RichTextResolver | ||
public resolveNestedRelations: boolean | ||
private stringifiedStoriesCache: Record<string, string> | ||
public richTextResolver: RichTextResolver; | ||
public resolveNestedRelations: boolean; | ||
private stringifiedStoriesCache: Record<string, string>; | ||
@@ -86,79 +78,89 @@ /** | ||
* @param config ISbConfig interface | ||
* @param endpoint string, optional | ||
* @param pEndpoint string, optional | ||
*/ | ||
public constructor(config: ISbConfig, pEndpoint?: string) { | ||
let endpoint = config.endpoint || pEndpoint | ||
let endpoint = config.endpoint || pEndpoint; | ||
if (!endpoint) { | ||
const getRegion = new SbHelpers().getRegionURL | ||
const protocol = config.https === false ? 'http' : 'https' | ||
const getRegion = new SbHelpers().getRegionURL; | ||
const protocol = config.https === false ? 'http' : 'https'; | ||
if (!config.oauthToken) { | ||
endpoint = `${protocol}://${getRegion(config.region)}/${'v2' as Version}` | ||
} else { | ||
endpoint = `${protocol}://${getRegion(config.region)}/${'v1' as Version}` | ||
endpoint = `${protocol}://${getRegion(config.region)}/${'v2' as Version}`; | ||
} | ||
else { | ||
endpoint = `${protocol}://${getRegion(config.region)}/${'v1' as Version}`; | ||
} | ||
} | ||
const headers: Headers = new Headers() | ||
const headers: Headers = new Headers(); | ||
headers.set('Content-Type', 'application/json') | ||
headers.set('Accept', 'application/json') | ||
headers.set('Content-Type', 'application/json'); | ||
headers.set('Accept', 'application/json'); | ||
if (config.headers) { | ||
const entries = (config.headers.constructor.name === 'Headers') ? config.headers.entries().toArray() : Object.entries(config.headers) | ||
const entries | ||
= config.headers.constructor.name === 'Headers' | ||
? config.headers.entries().toArray() | ||
: Object.entries(config.headers); | ||
entries.forEach(([key, value]: [string, string]) => { | ||
headers.set(key, value) | ||
}) | ||
headers.set(key, value); | ||
}); | ||
} | ||
if (!headers.has(STORYBLOK_AGENT)) { | ||
headers.set(STORYBLOK_AGENT, STORYBLOK_JS_CLIENT_AGENT.defaultAgentName) | ||
headers.set(STORYBLOK_AGENT, STORYBLOK_JS_CLIENT_AGENT.defaultAgentName); | ||
headers.set( | ||
STORYBLOK_JS_CLIENT_AGENT.defaultAgentVersion, | ||
STORYBLOK_JS_CLIENT_AGENT.packageVersion | ||
) | ||
STORYBLOK_JS_CLIENT_AGENT.packageVersion, | ||
); | ||
} | ||
let rateLimit = 5 // per second for cdn api | ||
let rateLimit = 5; // per second for cdn api | ||
if (config.oauthToken) { | ||
headers.set('Authorization', config.oauthToken) | ||
rateLimit = 3 // per second for management api | ||
headers.set('Authorization', config.oauthToken); | ||
rateLimit = 3; // per second for management api | ||
} | ||
if (config.rateLimit) { | ||
rateLimit = config.rateLimit | ||
rateLimit = config.rateLimit; | ||
} | ||
if (config.richTextSchema) { | ||
this.richTextResolver = new RichTextResolver(config.richTextSchema) | ||
} else { | ||
this.richTextResolver = new RichTextResolver() | ||
this.richTextResolver = new RichTextResolver(config.richTextSchema); | ||
} | ||
else { | ||
this.richTextResolver = new RichTextResolver(); | ||
} | ||
if (config.componentResolver) { | ||
this.setComponentResolver(config.componentResolver) | ||
this.setComponentResolver(config.componentResolver); | ||
} | ||
this.maxRetries = config.maxRetries || 10 | ||
this.retriesDelay = 300 | ||
this.throttle = throttledQueue(this.throttledRequest, rateLimit, 1000) | ||
this.accessToken = config.accessToken || '' | ||
this.relations = {} as RelationsType | ||
this.links = {} as LinksType | ||
this.cache = config.cache || { clear: 'manual' } | ||
this.helpers = new SbHelpers() | ||
this.resolveCounter = 0 | ||
this.resolveNestedRelations = config.resolveNestedRelations || true | ||
this.stringifiedStoriesCache = {} as Record<string, string> | ||
this.maxRetries = config.maxRetries || 10; | ||
this.retriesDelay = 300; | ||
this.throttle = throttledQueue( | ||
this.throttledRequest.bind(this), | ||
rateLimit, | ||
1000, | ||
); | ||
this.accessToken = config.accessToken || ''; | ||
this.relations = {} as RelationsType; | ||
this.links = {} as LinksType; | ||
this.cache = config.cache || { clear: 'manual' }; | ||
this.helpers = new SbHelpers(); | ||
this.resolveCounter = 0; | ||
this.resolveNestedRelations = config.resolveNestedRelations || true; | ||
this.stringifiedStoriesCache = {} as Record<string, string>; | ||
this.client = new SbFetch({ | ||
baseURL: endpoint, | ||
timeout: config.timeout || 0, | ||
headers: headers, | ||
headers, | ||
responseInterceptor: config.responseInterceptor, | ||
fetch: config.fetch, | ||
}) | ||
}); | ||
} | ||
@@ -168,14 +170,14 @@ | ||
this.richTextResolver.addNode('blok', (node: ISbNode) => { | ||
let html = '' | ||
let html = ''; | ||
if (node.attrs.body) { | ||
node.attrs.body.forEach((blok) => { | ||
html += resolver(blok.component, blok) | ||
}) | ||
html += resolver(blok.component, blok); | ||
}); | ||
} | ||
return { | ||
html: html, | ||
} | ||
}) | ||
html, | ||
}; | ||
}); | ||
} | ||
@@ -185,18 +187,18 @@ | ||
if (!params.token) { | ||
params.token = this.getToken() | ||
params.token = this.getToken(); | ||
} | ||
if (!params.cv) { | ||
params.cv = cacheVersions[params.token] | ||
params.cv = cacheVersions[params.token]; | ||
} | ||
if (Array.isArray(params.resolve_relations)) { | ||
params.resolve_relations = params.resolve_relations.join(',') | ||
params.resolve_relations = params.resolve_relations.join(','); | ||
} | ||
if (typeof params.resolve_relations !== 'undefined') { | ||
params.resolve_level = 2 | ||
params.resolve_level = 2; | ||
} | ||
return params | ||
return params; | ||
} | ||
@@ -206,9 +208,9 @@ | ||
url: string, | ||
params: ISbStoriesParams | ||
params: ISbStoriesParams, | ||
): ISbStoriesParams { | ||
if (this.helpers.isCDNUrl(url)) { | ||
return this.parseParams(params) | ||
return this.parseParams(params); | ||
} | ||
return params | ||
return params; | ||
} | ||
@@ -221,10 +223,10 @@ | ||
page: number, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbResult> { | ||
const query = this.factoryParamOptions( | ||
url, | ||
this.helpers.getOptionsPage(params, per_page, page) | ||
) | ||
this.helpers.getOptionsPage(params, per_page, page), | ||
); | ||
return this.cacheResponse(url, query, undefined, fetchOptions) | ||
return this.cacheResponse(url, query, undefined, fetchOptions); | ||
} | ||
@@ -235,9 +237,11 @@ | ||
params?: ISbStoriesParams, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbResult> { | ||
if (!params) params = {} as ISbStoriesParams | ||
const url = `/${slug}` | ||
const query = this.factoryParamOptions(url, params) | ||
if (!params) { | ||
params = {} as ISbStoriesParams; | ||
} | ||
const url = `/${slug}`; | ||
const query = this.factoryParamOptions(url, params); | ||
return this.cacheResponse(url, query, undefined, fetchOptions) | ||
return this.cacheResponse(url, query, undefined, fetchOptions); | ||
} | ||
@@ -249,9 +253,9 @@ | ||
entity?: string, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<any[]> { | ||
const perPage = params?.per_page || 25 | ||
const url = `/${slug}`.replace(/\/$/, '') | ||
const e = entity ?? url.substring(url.lastIndexOf('/') + 1) | ||
const perPage = params?.per_page || 25; | ||
const url = `/${slug}`.replace(/\/$/, ''); | ||
const e = entity ?? url.substring(url.lastIndexOf('/') + 1); | ||
const firstPage = 1 | ||
const firstPage = 1; | ||
const firstRes = await this.makeRequest( | ||
@@ -262,5 +266,5 @@ url, | ||
firstPage, | ||
fetchOptions | ||
) | ||
const lastPage = firstRes.total ? Math.ceil(firstRes.total / perPage) : 1 | ||
fetchOptions, | ||
); | ||
const lastPage = firstRes.total ? Math.ceil(firstRes.total / perPage) : 1; | ||
@@ -270,9 +274,8 @@ const restRes: any = await this.helpers.asyncMap( | ||
(i: number) => { | ||
return this.makeRequest(url, params, perPage, i + 1, fetchOptions) | ||
} | ||
) | ||
return this.makeRequest(url, params, perPage, i + 1, fetchOptions); | ||
}, | ||
); | ||
return this.helpers.flatMap([firstRes, ...restRes], (res: ISbFlatMapped) => | ||
Object.values(res.data[e]) | ||
) | ||
Object.values(res.data[e])); | ||
} | ||
@@ -283,7 +286,9 @@ | ||
params: ISbStoriesParams | ISbContentMangmntAPI, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbResponseData> { | ||
const url = `/${slug}` | ||
const url = `/${slug}`; | ||
return Promise.resolve(this.throttle('post', url, params, fetchOptions)) | ||
return Promise.resolve( | ||
this.throttle('post', url, params, fetchOptions), | ||
) as Promise<ISbResponseData>; | ||
} | ||
@@ -294,7 +299,9 @@ | ||
params: ISbStoriesParams | ISbContentMangmntAPI, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbResponseData> { | ||
const url = `/${slug}` | ||
const url = `/${slug}`; | ||
return Promise.resolve(this.throttle('put', url, params, fetchOptions)) | ||
return Promise.resolve( | ||
this.throttle('put', url, params, fetchOptions), | ||
) as Promise<ISbResponseData>; | ||
} | ||
@@ -305,7 +312,9 @@ | ||
params: ISbStoriesParams | ISbContentMangmntAPI, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbResponseData> { | ||
const url = `/${slug}` | ||
const url = `/${slug}`; | ||
return Promise.resolve(this.throttle('delete', url, params, fetchOptions)) | ||
return Promise.resolve( | ||
this.throttle('delete', url, params, fetchOptions), | ||
) as Promise<ISbResponseData>; | ||
} | ||
@@ -315,7 +324,7 @@ | ||
params: ISbStoriesParams, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbStories> { | ||
this._addResolveLevel(params) | ||
this._addResolveLevel(params); | ||
return this.get('cdn/stories', params, fetchOptions) | ||
return this.get('cdn/stories', params, fetchOptions); | ||
} | ||
@@ -326,15 +335,15 @@ | ||
params: ISbStoryParams, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbStory> { | ||
this._addResolveLevel(params) | ||
this._addResolveLevel(params); | ||
return this.get(`cdn/stories/${slug}`, params, fetchOptions) | ||
return this.get(`cdn/stories/${slug}`, params, fetchOptions); | ||
} | ||
private getToken(): string { | ||
return this.accessToken | ||
return this.accessToken; | ||
} | ||
public ejectInterceptor(): void { | ||
this.client.eject() | ||
this.client.eject(); | ||
} | ||
@@ -344,3 +353,3 @@ | ||
if (typeof params.resolve_relations !== 'undefined') { | ||
params.resolve_level = 2 | ||
params.resolve_level = 2; | ||
} | ||
@@ -350,3 +359,3 @@ } | ||
private _cleanCopy(value: LinksType): JSON { | ||
return JSON.parse(JSON.stringify(value)) | ||
return JSON.parse(JSON.stringify(value)); | ||
} | ||
@@ -357,21 +366,22 @@ | ||
treeItem: keyof ISbStoriesParams, | ||
resolveId: string | ||
resolveId: string, | ||
): void { | ||
const node = jtree[treeItem] | ||
const node = jtree[treeItem]; | ||
if ( | ||
node && | ||
node.fieldtype == 'multilink' && | ||
node.linktype == 'story' && | ||
typeof node.id === 'string' && | ||
this.links[resolveId][node.id] | ||
node | ||
&& node.fieldtype === 'multilink' | ||
&& node.linktype === 'story' | ||
&& typeof node.id === 'string' | ||
&& this.links[resolveId][node.id] | ||
) { | ||
node.story = this._cleanCopy(this.links[resolveId][node.id]) | ||
} else if ( | ||
node && | ||
node.linktype === 'story' && | ||
typeof node.uuid === 'string' && | ||
this.links[resolveId][node.uuid] | ||
node.story = this._cleanCopy(this.links[resolveId][node.id]); | ||
} | ||
else if ( | ||
node | ||
&& node.linktype === 'story' | ||
&& typeof node.uuid === 'string' | ||
&& this.links[resolveId][node.uuid] | ||
) { | ||
node.story = this._cleanCopy(this.links[resolveId][node.uuid]) | ||
node.story = this._cleanCopy(this.links[resolveId][node.uuid]); | ||
} | ||
@@ -387,8 +397,11 @@ } | ||
private getStoryReference(resolveId: string, uuid: string): string | JSON { | ||
if (!this.relations[resolveId][uuid]) return uuid | ||
if (!this.stringifiedStoriesCache[uuid]) | ||
if (!this.relations[resolveId][uuid]) { | ||
return uuid; | ||
} | ||
if (!this.stringifiedStoriesCache[uuid]) { | ||
this.stringifiedStoriesCache[uuid] = JSON.stringify( | ||
this.relations[resolveId][uuid] | ||
) | ||
return JSON.parse(this.stringifiedStoriesCache[uuid]) | ||
this.relations[resolveId][uuid], | ||
); | ||
} | ||
return JSON.parse(this.stringifiedStoriesCache[uuid]); | ||
} | ||
@@ -400,11 +413,12 @@ | ||
fields: string | Array<string>, | ||
resolveId: string | ||
resolveId: string, | ||
): void { | ||
if (fields.indexOf(`${jtree.component}.${treeItem}`) > -1) { | ||
if (fields.includes(`${jtree.component}.${treeItem}`)) { | ||
if (typeof jtree[treeItem] === 'string') { | ||
jtree[treeItem] = this.getStoryReference(resolveId, jtree[treeItem]) | ||
} else if (Array.isArray(jtree[treeItem])) { | ||
jtree[treeItem] = this.getStoryReference(resolveId, jtree[treeItem]); | ||
} | ||
else if (Array.isArray(jtree[treeItem])) { | ||
jtree[treeItem] = jtree[treeItem as keyof ISbStoriesParams] | ||
.map((uuid: string) => this.getStoryReference(resolveId, uuid)) | ||
.filter(Boolean) | ||
.filter(Boolean); | ||
} | ||
@@ -417,15 +431,16 @@ } | ||
fields: string | Array<string>, | ||
resolveId: string | ||
resolveId: string, | ||
): void { | ||
const enrich = (jtree: ISbStoriesParams | any) => { | ||
if (jtree == null) { | ||
return | ||
return; | ||
} | ||
if (jtree.constructor === Array) { | ||
for (let item = 0; item < jtree.length; item++) { | ||
enrich(jtree[item]) | ||
enrich(jtree[item]); | ||
} | ||
} else if (jtree.constructor === Object) { | ||
} | ||
else if (jtree.constructor === Object) { | ||
if (jtree._stopResolving) { | ||
return | ||
return; | ||
} | ||
@@ -438,16 +453,16 @@ for (const treeItem in jtree) { | ||
fields, | ||
resolveId | ||
) | ||
resolveId, | ||
); | ||
this._insertLinks( | ||
jtree, | ||
treeItem as keyof ISbStoriesParams, | ||
resolveId | ||
) | ||
resolveId, | ||
); | ||
} | ||
enrich(jtree[treeItem]) | ||
enrich(jtree[treeItem]); | ||
} | ||
} | ||
} | ||
}; | ||
enrich(story.content) | ||
enrich(story.content); | ||
} | ||
@@ -458,14 +473,14 @@ | ||
params: ISbStoriesParams, | ||
resolveId: string | ||
resolveId: string, | ||
): Promise<void> { | ||
let links: (ISbStoryData | ISbLinkURLObject | string)[] = [] | ||
let links: (ISbStoryData | ISbLinkURLObject | string)[] = []; | ||
if (responseData.link_uuids) { | ||
const relSize = responseData.link_uuids.length | ||
const chunks = [] | ||
const chunkSize = 50 | ||
const relSize = responseData.link_uuids.length; | ||
const chunks = []; | ||
const chunkSize = 50; | ||
for (let i = 0; i < relSize; i += chunkSize) { | ||
const end = Math.min(relSize, i + chunkSize) | ||
chunks.push(responseData.link_uuids.slice(i, end)) | ||
const end = Math.min(relSize, i + chunkSize); | ||
chunks.push(responseData.link_uuids.slice(i, end)); | ||
} | ||
@@ -479,13 +494,14 @@ | ||
by_uuids: chunks[chunkIndex].join(','), | ||
}) | ||
}); | ||
linksRes.data.stories.forEach( | ||
(rel: ISbStoryData | ISbLinkURLObject | string) => { | ||
links.push(rel) | ||
} | ||
) | ||
links.push(rel); | ||
}, | ||
); | ||
} | ||
} else { | ||
links = responseData.links | ||
} | ||
else { | ||
links = responseData.links; | ||
} | ||
@@ -496,4 +512,4 @@ links.forEach((story: ISbStoryData | any) => { | ||
...{ _stopResolving: true }, | ||
} | ||
}) | ||
}; | ||
}); | ||
} | ||
@@ -504,14 +520,14 @@ | ||
params: ISbStoriesParams, | ||
resolveId: string | ||
resolveId: string, | ||
): Promise<void> { | ||
let relations = [] | ||
let relations = []; | ||
if (responseData.rel_uuids) { | ||
const relSize = responseData.rel_uuids.length | ||
const chunks = [] | ||
const chunkSize = 50 | ||
const relSize = responseData.rel_uuids.length; | ||
const chunks = []; | ||
const chunkSize = 50; | ||
for (let i = 0; i < relSize; i += chunkSize) { | ||
const end = Math.min(relSize, i + chunkSize) | ||
chunks.push(responseData.rel_uuids.slice(i, end)) | ||
const end = Math.min(relSize, i + chunkSize); | ||
chunks.push(responseData.rel_uuids.slice(i, end)); | ||
} | ||
@@ -526,11 +542,12 @@ | ||
excluding_fields: params.excluding_fields, | ||
}) | ||
}); | ||
relationsRes.data.stories.forEach((rel: ISbStoryData) => { | ||
relations.push(rel) | ||
}) | ||
relations.push(rel); | ||
}); | ||
} | ||
} else { | ||
relations = responseData.rels | ||
} | ||
else { | ||
relations = responseData.rels; | ||
} | ||
@@ -542,4 +559,4 @@ if (relations && relations.length > 0) { | ||
...{ _stopResolving: true }, | ||
} | ||
}) | ||
}; | ||
}); | ||
} | ||
@@ -560,25 +577,25 @@ } | ||
params: ISbStoriesParams, | ||
resolveId: string | ||
resolveId: string, | ||
): Promise<void> { | ||
let relationParams: string[] = [] | ||
let relationParams: string[] = []; | ||
this.links[resolveId] = {} | ||
this.relations[resolveId] = {} | ||
this.links[resolveId] = {}; | ||
this.relations[resolveId] = {}; | ||
if ( | ||
typeof params.resolve_relations !== 'undefined' && | ||
params.resolve_relations.length > 0 | ||
typeof params.resolve_relations !== 'undefined' | ||
&& params.resolve_relations.length > 0 | ||
) { | ||
if (typeof params.resolve_relations === 'string') { | ||
relationParams = params.resolve_relations.split(',') | ||
relationParams = params.resolve_relations.split(','); | ||
} | ||
await this.resolveRelations(responseData, params, resolveId) | ||
await this.resolveRelations(responseData, params, resolveId); | ||
} | ||
if ( | ||
params.resolve_links && | ||
['1', 'story', 'url', 'link'].indexOf(params.resolve_links) > -1 && | ||
(responseData.links?.length || responseData.link_uuids?.length) | ||
params.resolve_links | ||
&& ['1', 'story', 'url', 'link'].includes(params.resolve_links) | ||
&& (responseData.links?.length || responseData.link_uuids?.length) | ||
) { | ||
await this.resolveLinks(responseData, params, resolveId) | ||
await this.resolveLinks(responseData, params, resolveId); | ||
} | ||
@@ -591,4 +608,4 @@ | ||
relationParams, | ||
resolveId | ||
) | ||
resolveId, | ||
); | ||
} | ||
@@ -598,13 +615,14 @@ } | ||
if (responseData.story) { | ||
this.iterateTree(responseData.story, relationParams, resolveId) | ||
} else { | ||
this.iterateTree(responseData.story, relationParams, resolveId); | ||
} | ||
else { | ||
responseData.stories.forEach((story: ISbStoryData) => { | ||
this.iterateTree(story, relationParams, resolveId) | ||
}) | ||
this.iterateTree(story, relationParams, resolveId); | ||
}); | ||
} | ||
this.stringifiedStoriesCache = {} | ||
this.stringifiedStoriesCache = {}; | ||
delete this.links[resolveId] | ||
delete this.relations[resolveId] | ||
delete this.links[resolveId]; | ||
delete this.relations[resolveId]; | ||
} | ||
@@ -616,15 +634,15 @@ | ||
retries?: number, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<ISbResult> { | ||
const cacheKey = this.helpers.stringify({ url: url, params: params }) | ||
const provider = this.cacheProvider() | ||
const cacheKey = this.helpers.stringify({ url, params }); | ||
const provider = this.cacheProvider(); | ||
if (this.cache.clear === 'auto' && params.version === 'draft') { | ||
await this.flushCache() | ||
await this.flushCache(); | ||
} | ||
if (params.version === 'published' && url != '/cdn/spaces/me') { | ||
const cache = await provider.get(cacheKey) | ||
if (params.version === 'published' && url !== '/cdn/spaces/me') { | ||
const cache = await provider.get(cacheKey); | ||
if (cache) { | ||
return Promise.resolve(cache) | ||
return Promise.resolve(cache); | ||
} | ||
@@ -635,8 +653,13 @@ } | ||
try { | ||
const res = await this.throttle('get', url, params, fetchOptions) | ||
const res = (await this.throttle( | ||
'get', | ||
url, | ||
params, | ||
fetchOptions, | ||
)) as ISbResponse; | ||
if (res.status !== 200) { | ||
return reject(res) | ||
return reject(res); | ||
} | ||
let response = { data: res.data, headers: res.headers } as ISbResult | ||
let response = { data: res.data, headers: res.headers } as ISbResult; | ||
@@ -646,44 +669,49 @@ if (res.headers?.['per-page']) { | ||
perPage: res.headers['per-page'] | ||
? parseInt(res.headers['per-page']) | ||
? Number.parseInt(res.headers['per-page']) | ||
: 0, | ||
total: res.headers['per-page'] ? parseInt(res.headers['total']) : 0, | ||
}) | ||
total: res.headers['per-page'] | ||
? Number.parseInt(res.headers.total) | ||
: 0, | ||
}); | ||
} | ||
if (response.data.story || response.data.stories) { | ||
const resolveId = (this.resolveCounter = ++this.resolveCounter % 1000) | ||
await this.resolveStories(response.data, params, `${resolveId}`) | ||
const resolveId = (this.resolveCounter | ||
= ++this.resolveCounter % 1000); | ||
await this.resolveStories(response.data, params, `${resolveId}`); | ||
} | ||
if (params.version === 'published' && url != '/cdn/spaces/me') { | ||
await provider.set(cacheKey, response) | ||
if (params.version === 'published' && url !== '/cdn/spaces/me') { | ||
await provider.set(cacheKey, response); | ||
} | ||
if ( | ||
response.data.cv | ||
&& params.token | ||
&& cacheVersions[params.token] != response.data.cv | ||
response.data.cv | ||
&& params.token | ||
&& cacheVersions[params.token] !== response.data.cv | ||
) { | ||
await this.flushCache() | ||
cacheVersions[params.token] = response.data.cv | ||
await this.flushCache(); | ||
cacheVersions[params.token] = response.data.cv; | ||
} | ||
return resolve(response) | ||
} catch (error: Error | any) { | ||
return resolve(response); | ||
} | ||
catch (error: Error | any) { | ||
if (error.response && error.status === 429) { | ||
retries = typeof retries === 'undefined' ? 0 : retries + 1 | ||
retries = typeof retries === 'undefined' ? 0 : retries + 1; | ||
if (retries < this.maxRetries) { | ||
// eslint-disable-next-line no-console | ||
console.log( | ||
`Hit rate limit. Retrying in ${this.retriesDelay / 1000} seconds.` | ||
) | ||
await this.helpers.delay(this.retriesDelay) | ||
`Hit rate limit. Retrying in ${this.retriesDelay / 1000} seconds.`, | ||
); | ||
await this.helpers.delay(this.retriesDelay); | ||
return this.cacheResponse(url, params, retries) | ||
.then(resolve) | ||
.catch(reject) | ||
.catch(reject); | ||
} | ||
} | ||
reject(error) | ||
reject(error); | ||
} | ||
}) | ||
}); | ||
} | ||
@@ -695,14 +723,14 @@ | ||
params: ISbStoriesParams, | ||
fetchOptions?: ISbCustomFetch | ||
fetchOptions?: ISbCustomFetch, | ||
): Promise<unknown> { | ||
this.client.setFetchOptions(fetchOptions) | ||
return this.client[type](url, params) | ||
this.client.setFetchOptions(fetchOptions); | ||
return this.client[type](url, params); | ||
} | ||
public cacheVersions(): CachedVersions { | ||
return cacheVersions | ||
return cacheVersions; | ||
} | ||
public cacheVersion(): number { | ||
return cacheVersions[this.accessToken] | ||
return cacheVersions[this.accessToken]; | ||
} | ||
@@ -712,3 +740,3 @@ | ||
if (this.accessToken) { | ||
cacheVersions[this.accessToken] = cv | ||
cacheVersions[this.accessToken] = cv; | ||
} | ||
@@ -719,3 +747,3 @@ } | ||
if (this.accessToken) { | ||
cacheVersions[this.accessToken] = 0 | ||
cacheVersions[this.accessToken] = 0; | ||
} | ||
@@ -729,18 +757,20 @@ } | ||
get(key: string) { | ||
return Promise.resolve(memory[key]) | ||
return Promise.resolve(memory[key]); | ||
}, | ||
getAll() { | ||
return Promise.resolve(memory as IMemoryType) | ||
return Promise.resolve(memory as IMemoryType); | ||
}, | ||
set(key: string, content: ISbResult) { | ||
memory[key] = content | ||
return Promise.resolve(undefined) | ||
memory[key] = content; | ||
return Promise.resolve(undefined); | ||
}, | ||
flush() { | ||
memory = {} | ||
return Promise.resolve(undefined) | ||
memory = {}; | ||
return Promise.resolve(undefined); | ||
}, | ||
}; | ||
case 'custom': | ||
if (this.cache.custom) { | ||
return this.cache.custom; | ||
} | ||
case 'custom': | ||
if (this.cache.custom) return this.cache.custom | ||
// eslint-disable-next-line no-fallthrough | ||
@@ -750,14 +780,14 @@ default: | ||
get() { | ||
return Promise.resolve() | ||
return Promise.resolve(); | ||
}, | ||
getAll() { | ||
return Promise.resolve(undefined) | ||
return Promise.resolve(undefined); | ||
}, | ||
set() { | ||
return Promise.resolve(undefined) | ||
return Promise.resolve(undefined); | ||
}, | ||
flush() { | ||
return Promise.resolve(undefined) | ||
return Promise.resolve(undefined); | ||
}, | ||
} | ||
}; | ||
} | ||
@@ -767,8 +797,8 @@ } | ||
public async flushCache(): Promise<this> { | ||
await this.cacheProvider().flush() | ||
this.clearCacheVersion() | ||
return this | ||
await this.cacheProvider().flush(); | ||
this.clearCacheVersion(); | ||
return this; | ||
} | ||
} | ||
export default Storyblok | ||
export default Storyblok; |
@@ -1,67 +0,68 @@ | ||
import { ResponseFn } from './sbFetch' | ||
import type { ResponseFn } from './sbFetch'; | ||
import type Method from './constants'; | ||
export interface ISbStoriesParams | ||
extends Partial<ISbStoryData>, | ||
ISbMultipleStoriesData, | ||
ISbAssetsParams { | ||
resolve_level?: number | ||
_stopResolving?: boolean | ||
by_slugs?: string | ||
by_uuids?: string | ||
by_uuids_ordered?: string | ||
component?: string | ||
content_type?: string | ||
cv?: number | ||
datasource?: string | ||
dimension?: string | ||
excluding_fields?: string | ||
excluding_ids?: string | ||
excluding_slugs?: string | ||
fallback_lang?: string | ||
filename?: string | ||
filter_query?: any | ||
first_published_at_gt?: string | ||
first_published_at_lt?: string | ||
from_release?: string | ||
is_startpage?: boolean | ||
language?: string | ||
level?: number | ||
page?: number | ||
per_page?: number | ||
published_at_gt?: string | ||
published_at_lt?: string | ||
resolve_assets?: number | ||
resolve_links?: 'link' | 'url' | 'story' | '0' | '1' | 'link' | ||
resolve_links_level?: 1 | 2 | ||
resolve_relations?: string | string[] | ||
search_term?: string | ||
size?: string | ||
sort_by?: string | ||
starts_with?: string | ||
token?: string | ||
version?: 'draft' | 'published' | ||
with_tag?: string | ||
ISbMultipleStoriesData, | ||
ISbAssetsParams { | ||
resolve_level?: number; | ||
_stopResolving?: boolean; | ||
by_slugs?: string; | ||
by_uuids?: string; | ||
by_uuids_ordered?: string; | ||
component?: string; | ||
content_type?: string; | ||
cv?: number; | ||
datasource?: string; | ||
dimension?: string; | ||
excluding_fields?: string; | ||
excluding_ids?: string; | ||
excluding_slugs?: string; | ||
fallback_lang?: string; | ||
filename?: string; | ||
filter_query?: any; | ||
first_published_at_gt?: string; | ||
first_published_at_lt?: string; | ||
from_release?: string; | ||
is_startpage?: boolean; | ||
language?: string; | ||
level?: number; | ||
page?: number; | ||
per_page?: number; | ||
published_at_gt?: string; | ||
published_at_lt?: string; | ||
resolve_assets?: number; | ||
resolve_links?: 'link' | 'url' | 'story' | '0' | '1' | 'link'; | ||
resolve_links_level?: 1 | 2; | ||
resolve_relations?: string | string[]; | ||
search_term?: string; | ||
size?: string; | ||
sort_by?: string; | ||
starts_with?: string; | ||
token?: string; | ||
version?: 'draft' | 'published'; | ||
with_tag?: string; | ||
} | ||
export interface ISbStoryParams { | ||
resolve_level?: number | ||
token?: string | ||
find_by?: 'uuid' | ||
version?: 'draft' | 'published' | ||
resolve_links?: 'link' | 'url' | 'story' | '0' | '1' | ||
resolve_links_level?: 1 | 2 | ||
resolve_relations?: string | string[] | ||
cv?: number | ||
from_release?: string | ||
language?: string | ||
fallback_lang?: string | ||
resolve_level?: number; | ||
token?: string; | ||
find_by?: 'uuid'; | ||
version?: 'draft' | 'published'; | ||
resolve_links?: 'link' | 'url' | 'story' | '0' | '1'; | ||
resolve_links_level?: 1 | 2; | ||
resolve_relations?: string | string[]; | ||
cv?: number; | ||
from_release?: string; | ||
language?: string; | ||
fallback_lang?: string; | ||
} | ||
type Dimension = { | ||
id: number | ||
name: string | ||
entry_value: string | ||
datasource_id: number | ||
created_at: string | ||
updated_at: string | ||
interface Dimension { | ||
id: number; | ||
name: string; | ||
entry_value: string; | ||
datasource_id: number; | ||
created_at: string; | ||
updated_at: string; | ||
} | ||
@@ -75,9 +76,9 @@ | ||
export interface ISbDimensions { | ||
dimensions: Dimension[] | ||
dimensions: Dimension[]; | ||
} | ||
export interface ISbComponentType<T extends string> { | ||
_uid?: string | ||
component?: T | ||
_editable?: string | ||
_uid?: string; | ||
component?: T; | ||
_editable?: string; | ||
} | ||
@@ -88,85 +89,85 @@ | ||
> extends ISbMultipleStoriesData { | ||
alternates: ISbAlternateObject[] | ||
breadcrumbs?: ISbLinkURLObject[] | ||
content: Content | ||
created_at: string | ||
default_full_slug?: string | ||
default_root?: string | ||
disble_fe_editor?: boolean | ||
first_published_at?: string | ||
full_slug: string | ||
group_id: string | ||
id: number | ||
imported_at?: string | ||
is_folder?: boolean | ||
is_startpage?: boolean | ||
lang: string | ||
alternates: ISbAlternateObject[]; | ||
breadcrumbs?: ISbLinkURLObject[]; | ||
content: Content; | ||
created_at: string; | ||
default_full_slug?: string; | ||
default_root?: string; | ||
disble_fe_editor?: boolean; | ||
first_published_at?: string; | ||
full_slug: string; | ||
group_id: string; | ||
id: number; | ||
imported_at?: string; | ||
is_folder?: boolean; | ||
is_startpage?: boolean; | ||
lang: string; | ||
last_author?: { | ||
id: number | ||
userid: string | ||
} | ||
meta_data: any | ||
name: string | ||
parent?: ISbStoryData | ||
parent_id: number | ||
path?: string | ||
pinned?: '1' | boolean | ||
position: number | ||
published?: boolean | ||
published_at: string | null | ||
release_id?: number | ||
slug: string | ||
sort_by_date: string | null | ||
tag_list: string[] | ||
id: number; | ||
userid: string; | ||
}; | ||
meta_data: any; | ||
name: string; | ||
parent?: ISbStoryData; | ||
parent_id: number; | ||
path?: string; | ||
pinned?: '1' | boolean; | ||
position: number; | ||
published?: boolean; | ||
published_at: string | null; | ||
release_id?: number; | ||
slug: string; | ||
sort_by_date: string | null; | ||
tag_list: string[]; | ||
translated_slugs?: { | ||
path: string | ||
name: string | null | ||
lang: ISbStoryData['lang'] | ||
}[] | ||
unpublished_changes?: boolean | ||
updated_at?: string | ||
uuid: string | ||
path: string; | ||
name: string | null; | ||
lang: ISbStoryData['lang']; | ||
}[]; | ||
unpublished_changes?: boolean; | ||
updated_at?: string; | ||
uuid: string; | ||
} | ||
export interface ISbMultipleStoriesData { | ||
by_ids?: string | ||
by_uuids?: string | ||
contain_component?: string | ||
excluding_ids?: string | ||
filter_query?: any | ||
folder_only?: boolean | ||
full_slug?: string | ||
in_release?: string | ||
in_trash?: boolean | ||
is_published?: boolean | ||
in_workflow_stages?: string | ||
page?: number | ||
pinned?: '1' | boolean | ||
search?: string | ||
sort_by?: string | ||
starts_with?: string | ||
story_only?: boolean | ||
text_search?: string | ||
with_parent?: number | ||
with_slug?: string | ||
with_tag?: string | ||
by_ids?: string; | ||
by_uuids?: string; | ||
contain_component?: string; | ||
excluding_ids?: string; | ||
filter_query?: any; | ||
folder_only?: boolean; | ||
full_slug?: string; | ||
in_release?: string; | ||
in_trash?: boolean; | ||
is_published?: boolean; | ||
in_workflow_stages?: string; | ||
page?: number; | ||
pinned?: '1' | boolean; | ||
search?: string; | ||
sort_by?: string; | ||
starts_with?: string; | ||
story_only?: boolean; | ||
text_search?: string; | ||
with_parent?: number; | ||
with_slug?: string; | ||
with_tag?: string; | ||
} | ||
export interface ISbAlternateObject { | ||
id: number | ||
name: string | ||
slug: string | ||
published: boolean | ||
full_slug: string | ||
is_folder: boolean | ||
parent_id: number | ||
id: number; | ||
name: string; | ||
slug: string; | ||
published: boolean; | ||
full_slug: string; | ||
is_folder: boolean; | ||
parent_id: number; | ||
} | ||
export interface ISbLinkURLObject { | ||
id: number | ||
name: string | ||
slug: string | ||
full_slug: string | ||
url: string | ||
uuid: string | ||
id: number; | ||
name: string; | ||
slug: string; | ||
full_slug: string; | ||
url: string; | ||
uuid: string; | ||
} | ||
@@ -178,10 +179,10 @@ | ||
data: { | ||
cv: number | ||
links: (ISbStoryData | ISbLinkURLObject)[] | ||
rels: ISbStoryData[] | ||
stories: ISbStoryData<Content>[] | ||
} | ||
perPage: number | ||
total: number | ||
headers: any | ||
cv: number; | ||
links: (ISbStoryData | ISbLinkURLObject)[]; | ||
rels: ISbStoryData[]; | ||
stories: ISbStoryData<Content>[]; | ||
}; | ||
perPage: number; | ||
total: number; | ||
headers: any; | ||
} | ||
@@ -193,83 +194,84 @@ | ||
data: { | ||
cv: number | ||
links: (ISbStoryData | ISbLinkURLObject)[] | ||
rels: ISbStoryData[] | ||
story: ISbStoryData<Content> | ||
} | ||
headers: any | ||
cv: number; | ||
links: (ISbStoryData | ISbLinkURLObject)[]; | ||
rels: ISbStoryData[]; | ||
story: ISbStoryData<Content>; | ||
}; | ||
headers: any; | ||
} | ||
export interface IMemoryType extends ISbResult { | ||
[key: string]: any | ||
[key: string]: any; | ||
} | ||
export interface ICacheProvider { | ||
get: (key: string) => Promise<IMemoryType | void> | ||
set: (key: string, content: ISbResult) => Promise<void> | ||
getAll: () => Promise<IMemoryType | void> | ||
flush: () => Promise<void> | ||
get: (key: string) => Promise<IMemoryType | void>; | ||
set: (key: string, content: ISbResult) => Promise<void>; | ||
getAll: () => Promise<IMemoryType | void>; | ||
flush: () => Promise<void>; | ||
} | ||
export interface ISbCache { | ||
type?: 'none' | 'memory' | 'custom' | ||
clear?: 'auto' | 'manual' | ||
custom?: ICacheProvider | ||
type?: 'none' | 'memory' | 'custom'; | ||
clear?: 'auto' | 'manual'; | ||
custom?: ICacheProvider; | ||
} | ||
export interface ISbConfig { | ||
accessToken?: string | ||
oauthToken?: string | ||
resolveNestedRelations?: boolean | ||
cache?: ISbCache | ||
responseInterceptor?: ResponseFn | ||
fetch?: typeof fetch | ||
timeout?: number | ||
headers?: any | ||
region?: string | ||
maxRetries?: number | ||
https?: boolean | ||
rateLimit?: number | ||
componentResolver?: (component: string, data: any) => void | ||
richTextSchema?: ISbSchema | ||
endpoint?: string | ||
accessToken?: string; | ||
oauthToken?: string; | ||
resolveNestedRelations?: boolean; | ||
cache?: ISbCache; | ||
responseInterceptor?: ResponseFn; | ||
fetch?: typeof fetch; | ||
timeout?: number; | ||
headers?: any; | ||
region?: string; | ||
maxRetries?: number; | ||
https?: boolean; | ||
rateLimit?: number; | ||
componentResolver?: (component: string, data: any) => void; | ||
richTextSchema?: ISbSchema; | ||
endpoint?: string; | ||
} | ||
export interface ISbResult { | ||
data: any | ||
perPage: number | ||
total: number | ||
headers: Headers | ||
data: any; | ||
perPage: number; | ||
total: number; | ||
headers: Headers; | ||
} | ||
export interface ISbResponse { | ||
data: any | ||
status: number | ||
statusText: string | ||
data: any; | ||
status: number; | ||
statusText: string; | ||
headers: any; | ||
} | ||
export interface ISbError { | ||
message?: string | ||
status?: number | ||
response?: ISbResponse | ||
message?: string; | ||
status?: number; | ||
response?: ISbResponse; | ||
} | ||
export interface ISbNode extends Element { | ||
content: object[] | ||
content: object[]; | ||
attrs: { | ||
anchor?: string | ||
body?: Array<ISbComponentType<any>> | ||
href?: string | ||
level?: number | ||
linktype?: string | ||
custom?: LinkCustomAttributes | ||
[key: string]: any | undefined | ||
} | ||
anchor?: string; | ||
body?: Array<ISbComponentType<any>>; | ||
href?: string; | ||
level?: number; | ||
linktype?: string; | ||
custom?: LinkCustomAttributes; | ||
[key: string]: any | undefined; | ||
}; | ||
} | ||
export type NodeSchema = { | ||
(node: ISbNode): object | ||
export interface NodeSchema { | ||
(node: ISbNode): object; | ||
} | ||
export type MarkSchema = { | ||
(node: ISbNode): object | ||
export interface MarkSchema { | ||
(node: ISbNode): object; | ||
} | ||
@@ -281,59 +283,59 @@ | ||
story: { | ||
name: string | ||
slug: string | ||
content?: Content | ||
default_root?: boolean | ||
is_folder?: boolean | ||
parent_id?: string | ||
disble_fe_editor?: boolean | ||
path?: string | ||
is_startpage?: boolean | ||
position?: number | ||
first_published_at?: string | ||
name: string; | ||
slug: string; | ||
content?: Content; | ||
default_root?: boolean; | ||
is_folder?: boolean; | ||
parent_id?: string; | ||
disble_fe_editor?: boolean; | ||
path?: string; | ||
is_startpage?: boolean; | ||
position?: number; | ||
first_published_at?: string; | ||
translated_slugs_attributes?: { | ||
path: string | ||
name: string | null | ||
lang: ISbContentMangmntAPI['lang'] | ||
}[] | ||
} | ||
force_update?: '1' | unknown | ||
release_id?: number | ||
publish?: '1' | unknown | ||
lang?: string | ||
path: string; | ||
name: string | null; | ||
lang: ISbContentMangmntAPI['lang']; | ||
}[]; | ||
}; | ||
force_update?: '1' | unknown; | ||
release_id?: number; | ||
publish?: '1' | unknown; | ||
lang?: string; | ||
} | ||
export interface ISbManagmentApiResult { | ||
data: any | ||
headers: any | ||
data: any; | ||
headers: any; | ||
} | ||
export interface ISbSchema { | ||
nodes: any | ||
marks: any | ||
nodes: any; | ||
marks: any; | ||
} | ||
export interface ISbRichtext { | ||
content?: ISbRichtext[] | ||
marks?: ISbRichtext[] | ||
attrs?: any | ||
text?: string | ||
type: string | ||
content?: ISbRichtext[]; | ||
marks?: ISbRichtext[]; | ||
attrs?: any; | ||
text?: string; | ||
type: string; | ||
} | ||
export interface LinkCustomAttributes { | ||
rel?: string | ||
title?: string | ||
[key: string]: any | ||
rel?: string; | ||
title?: string; | ||
[key: string]: any; | ||
} | ||
export interface ISbLink { | ||
id?: number | ||
slug?: string | ||
name?: string | ||
is_folder?: boolean | ||
parent_id?: number | ||
published?: boolean | ||
position?: number | ||
uuid?: string | ||
is_startpage?: boolean | ||
id?: number; | ||
slug?: string; | ||
name?: string; | ||
is_folder?: boolean; | ||
parent_id?: number; | ||
published?: boolean; | ||
position?: number; | ||
uuid?: string; | ||
is_startpage?: boolean; | ||
} | ||
@@ -343,16 +345,41 @@ | ||
links?: { | ||
[key: string]: ISbLink | ||
} | ||
[key: string]: ISbLink; | ||
}; | ||
} | ||
export type ThrottleFn = { | ||
(...args: any): any | ||
export interface Queue<T> { | ||
resolve: (value: unknown) => void; | ||
reject: (reason?: unknown) => void; | ||
args: T; | ||
} | ||
export type AsyncFn = (...args: any) => [] | Promise<ISbResult> | ||
export interface ISbResponseData { | ||
link_uuids: string[]; | ||
links: string[]; | ||
rel_uuids: string[]; | ||
rels: any; | ||
story: ISbStoryData; | ||
stories: Array<ISbStoryData>; | ||
} | ||
export type ArrayFn = (...args: any) => void | ||
export interface ISbThrottle< | ||
T extends (...args: Parameters<T>) => ReturnType<T>, | ||
> { | ||
abort?: () => void; | ||
(...args: Parameters<T>): Promise<unknown>; | ||
} | ||
export type HtmlEscapes = { | ||
[key: string]: string | ||
export type ISbThrottledRequest = ( | ||
type: Method, | ||
url: string, | ||
params: ISbStoriesParams, | ||
fetchOptions?: ISbCustomFetch | ||
) => Promise<unknown>; | ||
export type AsyncFn = (...args: any) => [] | Promise<ISbResult>; | ||
export type ArrayFn = (...args: any) => void; | ||
export interface HtmlEscapes { | ||
[key: string]: string; | ||
} | ||
@@ -363,7 +390,7 @@ | ||
export interface ISbAssetsParams { | ||
in_folder?: string | ||
is_private?: boolean | ||
by_alt?: string | ||
by_copyright?: string | ||
by_title?: string | ||
in_folder?: string; | ||
is_private?: boolean; | ||
by_alt?: string; | ||
by_copyright?: string; | ||
by_title?: string; | ||
} |
@@ -1,6 +0,6 @@ | ||
import defaultHtmlSerializer from './schema' | ||
import { ISbSchema, ISbRichtext } from './interfaces' | ||
import defaultHtmlSerializer from './schema'; | ||
import type { ISbRichtext, ISbSchema } from './interfaces'; | ||
type HtmlEscapes = { | ||
[key: string]: string | ||
interface HtmlEscapes { | ||
[key: string]: string; | ||
} | ||
@@ -11,21 +11,21 @@ | ||
| { | ||
class?: string | ||
filters?: { | ||
blur?: number | ||
brightness?: number | ||
fill?: string | ||
format?: 'webp' | 'jpeg' | 'png' | ||
grayscale?: boolean | ||
quality?: number | ||
rotate?: 90 | 180 | 270 | ||
} | ||
height?: number | ||
loading?: 'lazy' | 'eager' | ||
sizes?: string[] | ||
srcset?: (number | [number, number])[] | ||
width?: number | ||
} | ||
class?: string; | ||
filters?: { | ||
blur?: number; | ||
brightness?: number; | ||
fill?: string; | ||
format?: 'webp' | 'jpeg' | 'png'; | ||
grayscale?: boolean; | ||
quality?: number; | ||
rotate?: 90 | 180 | 270; | ||
}; | ||
height?: number; | ||
loading?: 'lazy' | 'eager'; | ||
sizes?: string[]; | ||
srcset?: (number | [number, number])[]; | ||
width?: number; | ||
}; | ||
type RenderOptions = { | ||
optimizeImages?: OptimizeImagesOptions | ||
interface RenderOptions { | ||
optimizeImages?: OptimizeImagesOptions; | ||
} | ||
@@ -39,46 +39,46 @@ | ||
'"': '"', | ||
"'": ''', | ||
} as HtmlEscapes | ||
'\'': ''', | ||
} as HtmlEscapes; | ||
const reUnescapedHtml = /[&<>"']/g | ||
const reHasUnescapedHtml = RegExp(reUnescapedHtml.source) | ||
const reUnescapedHtml = /[&<>"']/g; | ||
const reHasUnescapedHtml = new RegExp(reUnescapedHtml.source); | ||
return string && reHasUnescapedHtml.test(string) | ||
? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr]) | ||
: string | ||
} | ||
? string.replace(reUnescapedHtml, chr => htmlEscapes[chr]) | ||
: string; | ||
}; | ||
interface ISbTag extends Element { | ||
[key: string]: any | ||
[key: string]: any; | ||
} | ||
interface ISbNode { | ||
[key: string]: ISbSchema | ((arg: ISbRichtext) => any) | ||
[key: string]: ISbSchema | ((arg: ISbRichtext) => any); | ||
} | ||
interface ISbFunction<T extends any[], R> { | ||
(...args: T): R | ||
(...args: T): R; | ||
} | ||
let hasWarnedAboutDeprecation = false; | ||
let hasWarnedAboutDeprecation = false; | ||
class RichTextResolver { | ||
private marks: ISbNode | ||
private nodes: ISbNode | ||
private marks: ISbNode; | ||
private nodes: ISbNode; | ||
public constructor(schema?: ISbSchema) { | ||
if (!schema) { | ||
schema = defaultHtmlSerializer as ISbSchema | ||
schema = defaultHtmlSerializer as ISbSchema; | ||
} | ||
this.marks = schema.marks || [] | ||
this.nodes = schema.nodes || [] | ||
this.marks = schema.marks || []; | ||
this.nodes = schema.nodes || []; | ||
} | ||
public addNode(key: string, schema: ISbSchema | ISbFunction<any, any>) { | ||
this.nodes[key] = schema | ||
this.nodes[key] = schema; | ||
} | ||
public addMark(key: string, schema: ISbSchema) { | ||
this.marks[key] = schema | ||
this.marks[key] = schema; | ||
} | ||
@@ -89,22 +89,22 @@ | ||
options: RenderOptions = { optimizeImages: false }, | ||
deprecationWarning = true | ||
deprecationWarning = true, | ||
) { | ||
if(!hasWarnedAboutDeprecation && deprecationWarning) { | ||
if (!hasWarnedAboutDeprecation && deprecationWarning) { | ||
console.warn( | ||
"Warning ⚠️: The RichTextResolver class is deprecated and will be removed in the next major release. Please use the `@storyblok/richtext` package instead. https://github.com/storyblok/richtext/" | ||
'Warning ⚠️: The RichTextResolver class is deprecated and will be removed in the next major release. Please use the `@storyblok/richtext` package instead. https://github.com/storyblok/richtext/', | ||
); | ||
hasWarnedAboutDeprecation = true | ||
hasWarnedAboutDeprecation = true; | ||
} | ||
if (data && data.content && Array.isArray(data.content)) { | ||
let html = '' | ||
let html = ''; | ||
data.content.forEach((node) => { | ||
html += this.renderNode(node) | ||
}) | ||
html += this.renderNode(node); | ||
}); | ||
if (options.optimizeImages) { | ||
return this.optimizeImages(html, options.optimizeImages) | ||
return this.optimizeImages(html, options.optimizeImages); | ||
} | ||
return html | ||
return html; | ||
} | ||
@@ -114,52 +114,52 @@ | ||
`The render method must receive an Object with a "content" field. | ||
The "content" field must be an array of nodes as the type ISbRichtext. | ||
ISbRichtext: | ||
content?: ISbRichtext[] | ||
marks?: ISbRichtext[] | ||
attrs?: any | ||
text?: string | ||
type: string | ||
Example: | ||
{ | ||
content: [ | ||
{ | ||
content: [ | ||
{ | ||
text: 'Hello World', | ||
type: 'text' | ||
} | ||
], | ||
type: 'paragraph' | ||
} | ||
], | ||
type: 'doc' | ||
}` | ||
) | ||
return '' | ||
The "content" field must be an array of nodes as the type ISbRichtext. | ||
ISbRichtext: | ||
content?: ISbRichtext[] | ||
marks?: ISbRichtext[] | ||
attrs?: any | ||
text?: string | ||
type: string | ||
Example: | ||
{ | ||
content: [ | ||
{ | ||
content: [ | ||
{ | ||
text: 'Hello World', | ||
type: 'text' | ||
} | ||
], | ||
type: 'paragraph' | ||
} | ||
], | ||
type: 'doc' | ||
}`, | ||
); | ||
return ''; | ||
} | ||
private optimizeImages(html: string, options: OptimizeImagesOptions): string { | ||
let w = 0 | ||
let h = 0 | ||
let imageAttributes = '' | ||
let filters = '' | ||
let w = 0; | ||
let h = 0; | ||
let imageAttributes = ''; | ||
let filters = ''; | ||
if (typeof options !== 'boolean') { | ||
if (typeof options.width === 'number' && options.width > 0) { | ||
imageAttributes += `width="${options.width}" ` | ||
w = options.width | ||
imageAttributes += `width="${options.width}" `; | ||
w = options.width; | ||
} | ||
if (typeof options.height === 'number' && options.height > 0) { | ||
imageAttributes += `height="${options.height}" ` | ||
h = options.height | ||
imageAttributes += `height="${options.height}" `; | ||
h = options.height; | ||
} | ||
if (options.loading === 'lazy' || options.loading === 'eager') { | ||
imageAttributes += `loading="${options.loading}" ` | ||
imageAttributes += `loading="${options.loading}" `; | ||
} | ||
if (typeof options.class === 'string' && options.class.length > 0) { | ||
imageAttributes += `class="${options.class}" ` | ||
imageAttributes += `class="${options.class}" `; | ||
} | ||
@@ -169,55 +169,57 @@ | ||
if ( | ||
typeof options.filters.blur === 'number' && | ||
options.filters.blur >= 0 && | ||
options.filters.blur <= 100 | ||
typeof options.filters.blur === 'number' | ||
&& options.filters.blur >= 0 | ||
&& options.filters.blur <= 100 | ||
) { | ||
filters += `:blur(${options.filters.blur})` | ||
filters += `:blur(${options.filters.blur})`; | ||
} | ||
if ( | ||
typeof options.filters.brightness === 'number' && | ||
options.filters.brightness >= -100 && | ||
options.filters.brightness <= 100 | ||
typeof options.filters.brightness === 'number' | ||
&& options.filters.brightness >= -100 | ||
&& options.filters.brightness <= 100 | ||
) { | ||
filters += `:brightness(${options.filters.brightness})` | ||
filters += `:brightness(${options.filters.brightness})`; | ||
} | ||
if ( | ||
options.filters.fill && | ||
(options.filters.fill.match(/[0-9A-Fa-f]{6}/g) || | ||
options.filters.fill === 'transparent') | ||
options.filters.fill | ||
&& (options.filters.fill.match(/[0-9A-F]{6}/gi) | ||
|| options.filters.fill === 'transparent') | ||
) { | ||
filters += `:fill(${options.filters.fill})` | ||
filters += `:fill(${options.filters.fill})`; | ||
} | ||
if ( | ||
options.filters.format && | ||
['webp', 'png', 'jpeg'].includes(options.filters.format) | ||
options.filters.format | ||
&& ['webp', 'png', 'jpeg'].includes(options.filters.format) | ||
) { | ||
filters += `:format(${options.filters.format})` | ||
filters += `:format(${options.filters.format})`; | ||
} | ||
if ( | ||
typeof options.filters.grayscale === 'boolean' && | ||
options.filters.grayscale | ||
typeof options.filters.grayscale === 'boolean' | ||
&& options.filters.grayscale | ||
) { | ||
filters += ':grayscale()' | ||
filters += ':grayscale()'; | ||
} | ||
if ( | ||
typeof options.filters.quality === 'number' && | ||
options.filters.quality >= 0 && | ||
options.filters.quality <= 100 | ||
typeof options.filters.quality === 'number' | ||
&& options.filters.quality >= 0 | ||
&& options.filters.quality <= 100 | ||
) { | ||
filters += `:quality(${options.filters.quality})` | ||
filters += `:quality(${options.filters.quality})`; | ||
} | ||
if ( | ||
options.filters.rotate && | ||
[90, 180, 270].includes(options.filters.rotate) | ||
options.filters.rotate | ||
&& [90, 180, 270].includes(options.filters.rotate) | ||
) { | ||
filters += `:rotate(${options.filters.rotate})` | ||
filters += `:rotate(${options.filters.rotate})`; | ||
} | ||
if (filters.length > 0) filters = '/filters' + filters | ||
if (filters.length > 0) { | ||
filters = `/filters${filters}`; | ||
} | ||
} | ||
@@ -227,12 +229,12 @@ } | ||
if (imageAttributes.length > 0) { | ||
html = html.replace(/<img/g, `<img ${imageAttributes.trim()}`) | ||
html = html.replace(/<img/g, `<img ${imageAttributes.trim()}`); | ||
} | ||
const parameters = | ||
w > 0 || h > 0 || filters.length > 0 ? `${w}x${h}${filters}` : '' | ||
const parameters | ||
= w > 0 || h > 0 || filters.length > 0 ? `${w}x${h}${filters}` : ''; | ||
html = html.replace( | ||
/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|tiff|bmp)/g, | ||
`a.storyblok.com/f/$1/$2.$3/m/${parameters}` | ||
) | ||
/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g, | ||
`a.storyblok.com/f/$1/$2.$3/m/${parameters}`, | ||
); | ||
@@ -242,4 +244,4 @@ if (typeof options !== 'boolean' && (options.sizes || options.srcset)) { | ||
const url = value.match( | ||
/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|tiff|bmp)/g | ||
) | ||
/a.storyblok.com\/f\/(\d+)\/([^.]+)\.(gif|jpg|jpeg|png|tif|bmp)/g, | ||
); | ||
@@ -251,52 +253,58 @@ if (url && url.length > 0) { | ||
if (typeof value === 'number') { | ||
return `//${url}/m/${value}x0${filters} ${value}w` | ||
return `//${url}/m/${value}x0${filters} ${value}w`; | ||
} | ||
if (typeof value === 'object' && value.length === 2) { | ||
let w = 0 | ||
let h = 0 | ||
if (typeof value[0] === 'number') w = value[0] | ||
if (typeof value[1] === 'number') h = value[1] | ||
return `//${url}/m/${w}x${h}${filters} ${w}w` | ||
let w = 0; | ||
let h = 0; | ||
if (typeof value[0] === 'number') { | ||
w = value[0]; | ||
} | ||
if (typeof value[1] === 'number') { | ||
h = value[1]; | ||
} | ||
return `//${url}/m/${w}x${h}${filters} ${w}w`; | ||
} | ||
return ''; | ||
}) | ||
.join(', '), | ||
sizes: options.sizes?.map((size) => size).join(', '), | ||
} | ||
sizes: options.sizes?.map(size => size).join(', '), | ||
}; | ||
let renderImageAttributes = '' | ||
let renderImageAttributes = ''; | ||
if (imageAttributes.srcset) { | ||
renderImageAttributes += `srcset="${imageAttributes.srcset}" ` | ||
renderImageAttributes += `srcset="${imageAttributes.srcset}" `; | ||
} | ||
if (imageAttributes.sizes) { | ||
renderImageAttributes += `sizes="${imageAttributes.sizes}" ` | ||
renderImageAttributes += `sizes="${imageAttributes.sizes}" `; | ||
} | ||
return value.replace(/<img/g, `<img ${renderImageAttributes.trim()}`) | ||
return value.replace(/<img/g, `<img ${renderImageAttributes.trim()}`); | ||
} | ||
return value | ||
}) | ||
return value; | ||
}); | ||
} | ||
return html | ||
return html; | ||
} | ||
private renderNode(item: ISbRichtext) { | ||
const html = [] | ||
const html = []; | ||
if (item.marks) { | ||
item.marks.forEach((m) => { | ||
const mark = this.getMatchingMark(m) | ||
const mark = this.getMatchingMark(m); | ||
if (mark && mark.tag !== '') { | ||
html.push(this.renderOpeningTag(mark.tag)) | ||
html.push(this.renderOpeningTag(mark.tag)); | ||
} | ||
}) | ||
}); | ||
} | ||
const node = this.getMatchingNode(item) | ||
const node = this.getMatchingNode(item); | ||
if (node && node.tag) { | ||
html.push(this.renderOpeningTag(node.tag)) | ||
html.push(this.renderOpeningTag(node.tag)); | ||
} | ||
@@ -306,16 +314,20 @@ | ||
item.content.forEach((content) => { | ||
html.push(this.renderNode(content)) | ||
}) | ||
} else if (item.text) { | ||
html.push(escapeHTML(item.text)) | ||
} else if (node && node.singleTag) { | ||
html.push(this.renderTag(node.singleTag, ' /')) | ||
} else if (node && node.html) { | ||
html.push(node.html) | ||
} else if (item.type === 'emoji') { | ||
html.push(this.renderEmoji(item)) | ||
html.push(this.renderNode(content)); | ||
}); | ||
} | ||
else if (item.text) { | ||
html.push(escapeHTML(item.text)); | ||
} | ||
else if (node && node.singleTag) { | ||
html.push(this.renderTag(node.singleTag, ' /')); | ||
} | ||
else if (node && node.html) { | ||
html.push(node.html); | ||
} | ||
else if (item.type === 'emoji') { | ||
html.push(this.renderEmoji(item)); | ||
} | ||
if (node && node.tag) { | ||
html.push(this.renderClosingTag(node.tag)) | ||
html.push(this.renderClosingTag(node.tag)); | ||
} | ||
@@ -328,11 +340,11 @@ | ||
.forEach((m) => { | ||
const mark = this.getMatchingMark(m) | ||
const mark = this.getMatchingMark(m); | ||
if (mark && mark.tag !== '') { | ||
html.push(this.renderClosingTag(mark.tag)) | ||
html.push(this.renderClosingTag(mark.tag)); | ||
} | ||
}) | ||
}); | ||
} | ||
return html.join('') | ||
return html.join(''); | ||
} | ||
@@ -342,3 +354,3 @@ | ||
if (tags.constructor === String) { | ||
return `<${tags}${ending}>` | ||
return `<${tags}${ending}>`; | ||
} | ||
@@ -348,11 +360,12 @@ | ||
if (tag.constructor === String) { | ||
return `<${tag}${ending}>` | ||
} else { | ||
let h = `<${tag.tag}` | ||
return `<${tag}${ending}>`; | ||
} | ||
else { | ||
let h = `<${tag.tag}`; | ||
if (tag.attrs) { | ||
for (const key in tag.attrs) { | ||
if (Object.prototype.hasOwnProperty.call(tag.attrs, key)) { | ||
const value = tag.attrs[key] | ||
const value = tag.attrs[key]; | ||
if (value !== null) { | ||
h += ` ${key}="${value}"` | ||
h += ` ${key}="${value}"`; | ||
} | ||
@@ -363,10 +376,10 @@ } | ||
return `${h}${ending}>` | ||
return `${h}${ending}>`; | ||
} | ||
}) | ||
return all.join('') | ||
}); | ||
return all.join(''); | ||
} | ||
private renderOpeningTag(tags: ISbTag[]) { | ||
return this.renderTag(tags, '') | ||
return this.renderTag(tags, ''); | ||
} | ||
@@ -376,3 +389,3 @@ | ||
if (tags.constructor === String) { | ||
return `</${tags}>` | ||
return `</${tags}>`; | ||
} | ||
@@ -385,15 +398,16 @@ | ||
if (tag.constructor === String) { | ||
return `</${tag}>` | ||
} else { | ||
return `</${tag.tag}>` | ||
return `</${tag}>`; | ||
} | ||
}) | ||
else { | ||
return `</${tag.tag}>`; | ||
} | ||
}); | ||
return all.join('') | ||
return all.join(''); | ||
} | ||
private getMatchingNode(item: ISbRichtext) { | ||
const node = this.nodes[item.type] | ||
const node = this.nodes[item.type]; | ||
if (typeof node === 'function') { | ||
return node(item) | ||
return node(item); | ||
} | ||
@@ -403,5 +417,5 @@ } | ||
private getMatchingMark(item: ISbRichtext) { | ||
const node = this.marks[item.type] | ||
const node = this.marks[item.type]; | ||
if (typeof node === 'function') { | ||
return node(item) | ||
return node(item); | ||
} | ||
@@ -412,3 +426,3 @@ } | ||
if (item.attrs.emoji) { | ||
return item.attrs.emoji | ||
return item.attrs.emoji; | ||
} | ||
@@ -426,8 +440,8 @@ | ||
}, | ||
] as unknown as ISbTag[] | ||
] as unknown as ISbTag[]; | ||
return this.renderTag(emojiImageContainer, ' /') | ||
return this.renderTag(emojiImageContainer, ' /'); | ||
} | ||
} | ||
export default RichTextResolver | ||
export default RichTextResolver; |
@@ -1,17 +0,18 @@ | ||
import { describe, it, expect, vi, afterEach } from 'vitest' | ||
import SbFetch, { ISbFetch } from './sbFetch' | ||
import { headersToObject } from '../tests/utils' | ||
import { afterEach, describe, expect, it, vi } from 'vitest'; | ||
import type { ISbFetch } from './sbFetch'; | ||
import SbFetch from './sbFetch'; | ||
import { headersToObject } from '../tests/utils'; | ||
describe('SbFetch', () => { | ||
let sbFetch: SbFetch | ||
const mockFetch = vi.fn() | ||
describe('sbFetch', () => { | ||
let sbFetch: SbFetch; | ||
const mockFetch = vi.fn(); | ||
afterEach(() => { | ||
vi.restoreAllMocks() | ||
}) | ||
vi.restoreAllMocks(); | ||
}); | ||
it('should initialize', () => { | ||
sbFetch = new SbFetch({} as ISbFetch) | ||
expect(sbFetch).toBeInstanceOf(SbFetch) | ||
}) | ||
sbFetch = new SbFetch({} as ISbFetch); | ||
expect(sbFetch).toBeInstanceOf(SbFetch); | ||
}); | ||
@@ -23,28 +24,28 @@ describe('get', () => { | ||
fetch: mockFetch, | ||
} as ISbFetch) | ||
} as ISbFetch); | ||
const response = new Response(JSON.stringify({ data: 'test' }), { | ||
status: 200, | ||
headers: { 'Content-Type': 'application/json' }, | ||
}) | ||
mockFetch.mockResolvedValue(response) | ||
}); | ||
mockFetch.mockResolvedValue(response); | ||
await sbFetch.get('test', { | ||
is_startpage: false, | ||
search_term: 'test', | ||
}) | ||
}); | ||
expect(mockFetch).toHaveBeenCalledWith( | ||
'https://api.storyblok.com/v2/test?is_startpage=false&search_term=test', | ||
expect.anything() | ||
) | ||
}) | ||
}) | ||
expect.anything(), | ||
); | ||
}); | ||
}); | ||
describe('post', () => { | ||
it('should handle POST requests correctly', async () => { | ||
const testPayload = { title: 'New Story' } | ||
const testPayload = { title: 'New Story' }; | ||
const response = new Response(JSON.stringify({ data: 'test' }), { | ||
status: 200, | ||
headers: { 'Content-Type': 'application/json' }, | ||
}) | ||
mockFetch.mockResolvedValue(response) | ||
await sbFetch.post('stories', testPayload) | ||
}); | ||
mockFetch.mockResolvedValue(response); | ||
await sbFetch.post('stories', testPayload); | ||
expect(mockFetch).toHaveBeenCalledWith( | ||
@@ -57,5 +58,5 @@ 'https://api.storyblok.com/v2/stories', | ||
signal: expect.any(AbortSignal), | ||
} | ||
) | ||
}) | ||
}, | ||
); | ||
}); | ||
@@ -69,28 +70,28 @@ it('should set specific headers for POST requests', async () => { | ||
fetch: mockFetch, | ||
} as ISbFetch) | ||
const testPayload = { title: 'New Story' } | ||
} as ISbFetch); | ||
const testPayload = { title: 'New Story' }; | ||
const response = new Response(JSON.stringify({ data: 'test' }), { | ||
status: 200, | ||
headers: { 'Content-Type': 'application/json' }, | ||
}) | ||
mockFetch.mockResolvedValue(response) | ||
}); | ||
mockFetch.mockResolvedValue(response); | ||
await sbFetch.post('stories', testPayload) | ||
await sbFetch.post('stories', testPayload); | ||
// Get the last call to fetch and extract the headers | ||
const lastCall = mockFetch.mock.calls[mockFetch.mock.calls.length - 1] | ||
const actualHeaders = headersToObject(lastCall[1].headers) | ||
expect(actualHeaders['content-type']).toBe('application/json') | ||
}) | ||
}) | ||
const lastCall = mockFetch.mock.calls[mockFetch.mock.calls.length - 1]; | ||
const actualHeaders = headersToObject(lastCall[1].headers); | ||
expect(actualHeaders['content-type']).toBe('application/json'); | ||
}); | ||
}); | ||
describe('put', () => { | ||
it('should handle PUT requests correctly', async () => { | ||
const testPayload = { title: 'Updated Story' } | ||
const testPayload = { title: 'Updated Story' }; | ||
const response = new Response(JSON.stringify({ data: 'test' }), { | ||
status: 200, | ||
headers: { 'Content-Type': 'application/json' }, | ||
}) | ||
mockFetch.mockResolvedValue(response) | ||
await sbFetch.put('stories/1', testPayload) | ||
}); | ||
mockFetch.mockResolvedValue(response); | ||
await sbFetch.put('stories/1', testPayload); | ||
expect(mockFetch).toHaveBeenCalledWith( | ||
@@ -103,6 +104,6 @@ 'https://api.storyblok.com/v2/stories/1', | ||
signal: expect.any(AbortSignal), | ||
} | ||
) | ||
}) | ||
}) | ||
}, | ||
); | ||
}); | ||
}); | ||
@@ -113,5 +114,5 @@ describe('delete', () => { | ||
status: 204, // Typically, DELETE operations might not return content | ||
}) | ||
mockFetch.mockResolvedValue(response) | ||
await sbFetch.delete('stories/1', {}) | ||
}); | ||
mockFetch.mockResolvedValue(response); | ||
await sbFetch.delete('stories/1', {}); | ||
expect(mockFetch).toHaveBeenCalledWith( | ||
@@ -124,9 +125,9 @@ 'https://api.storyblok.com/v2/stories/1', | ||
signal: expect.any(AbortSignal), | ||
} | ||
) | ||
}) | ||
}) | ||
}, | ||
); | ||
}); | ||
}); | ||
it('should handle network errors gracefully', async () => { | ||
const mockFetch = vi.fn().mockRejectedValue(new Error('Network Failure')) | ||
const mockFetch = vi.fn().mockRejectedValue(new Error('Network Failure')); | ||
const sbFetch = new SbFetch({ | ||
@@ -136,6 +137,6 @@ baseURL: 'https://api.example.com', | ||
fetch: mockFetch, | ||
}) | ||
}); | ||
// Assuming your implementation wraps the error message inside an object under `message`. | ||
const result = await sbFetch.get('/test', {}) | ||
const result = await sbFetch.get('/test', {}); | ||
@@ -145,7 +146,7 @@ // Check if the error object format matches your implementation. | ||
message: expect.any(Error), // Checks if `message` is an instance of Error | ||
}) | ||
}); | ||
// If you want to be more specific and check the message of the error: | ||
expect(result.message.message).toEqual('Network Failure') // This path needs to match the structure you actually use. | ||
}) | ||
}) | ||
expect(result.message.message).toEqual('Network Failure'); // This path needs to match the structure you actually use. | ||
}); | ||
}); |
@@ -1,45 +0,45 @@ | ||
import { SbHelpers } from './sbHelpers' | ||
import { SbHelpers } from './sbHelpers'; | ||
import { | ||
import type { | ||
ISbCustomFetch, | ||
ISbError, | ||
ISbResponse, | ||
ISbError, | ||
ISbStoriesParams, | ||
ISbCustomFetch, | ||
} from './interfaces' | ||
import Method from './constants' | ||
} from './interfaces'; | ||
import type Method from './constants'; | ||
export type ResponseFn = { | ||
(arg?: ISbResponse | any): any | ||
export interface ResponseFn { | ||
(arg?: ISbResponse | any): any; | ||
} | ||
export interface ISbFetch { | ||
baseURL: string | ||
timeout?: number | ||
headers: Headers | ||
responseInterceptor?: ResponseFn | ||
fetch?: typeof fetch | ||
baseURL: string; | ||
timeout?: number; | ||
headers: Headers; | ||
responseInterceptor?: ResponseFn; | ||
fetch?: typeof fetch; | ||
} | ||
class SbFetch { | ||
private baseURL: string | ||
private timeout?: number | ||
private headers: Headers | ||
private responseInterceptor?: ResponseFn | ||
private fetch: typeof fetch | ||
private ejectInterceptor?: boolean | ||
private url: string | ||
private parameters: ISbStoriesParams | ||
private fetchOptions: ISbCustomFetch | ||
private baseURL: string; | ||
private timeout?: number; | ||
private headers: Headers; | ||
private responseInterceptor?: ResponseFn; | ||
private fetch: typeof fetch; | ||
private ejectInterceptor?: boolean; | ||
private url: string; | ||
private parameters: ISbStoriesParams; | ||
private fetchOptions: ISbCustomFetch; | ||
public constructor($c: ISbFetch) { | ||
this.baseURL = $c.baseURL | ||
this.headers = $c.headers || new Headers() | ||
this.timeout = $c?.timeout ? $c.timeout * 1000 : 0 | ||
this.responseInterceptor = $c.responseInterceptor | ||
this.baseURL = $c.baseURL; | ||
this.headers = $c.headers || new Headers(); | ||
this.timeout = $c?.timeout ? $c.timeout * 1000 : 0; | ||
this.responseInterceptor = $c.responseInterceptor; | ||
this.fetch = (...args: [any]) => | ||
$c.fetch ? $c.fetch(...args) : fetch(...args) | ||
this.ejectInterceptor = false | ||
this.url = '' | ||
this.parameters = {} as ISbStoriesParams | ||
this.fetchOptions = {} | ||
$c.fetch ? $c.fetch(...args) : fetch(...args); | ||
this.ejectInterceptor = false; | ||
this.url = ''; | ||
this.parameters = {} as ISbStoriesParams; | ||
this.fetchOptions = {}; | ||
} | ||
@@ -54,27 +54,27 @@ | ||
public get(url: string, params: ISbStoriesParams) { | ||
this.url = url | ||
this.parameters = params | ||
return this._methodHandler('get') | ||
this.url = url; | ||
this.parameters = params; | ||
return this._methodHandler('get'); | ||
} | ||
public post(url: string, params: ISbStoriesParams) { | ||
this.url = url | ||
this.parameters = params | ||
return this._methodHandler('post') | ||
this.url = url; | ||
this.parameters = params; | ||
return this._methodHandler('post'); | ||
} | ||
public put(url: string, params: ISbStoriesParams) { | ||
this.url = url | ||
this.parameters = params | ||
return this._methodHandler('put') | ||
this.url = url; | ||
this.parameters = params; | ||
return this._methodHandler('put'); | ||
} | ||
public delete(url: string, params: ISbStoriesParams) { | ||
this.url = url | ||
this.parameters = params | ||
return this._methodHandler('delete') | ||
this.url = url; | ||
this.parameters = params; | ||
return this._methodHandler('delete'); | ||
} | ||
private async _responseHandler(res: Response) { | ||
const headers: string[] = [] | ||
const headers: string[] = []; | ||
const response = { | ||
@@ -85,46 +85,47 @@ data: {}, | ||
statusText: '', | ||
} | ||
}; | ||
if (res.status !== 204) { | ||
await res.json().then(($r) => { | ||
response.data = $r | ||
}) | ||
response.data = $r; | ||
}); | ||
} | ||
for (const pair of res.headers.entries()) { | ||
headers[pair[0] as any] = pair[1] | ||
headers[pair[0] as any] = pair[1]; | ||
} | ||
response.headers = { ...headers } | ||
response.status = res.status | ||
response.statusText = res.statusText | ||
response.headers = { ...headers }; | ||
response.status = res.status; | ||
response.statusText = res.statusText; | ||
return response | ||
return response; | ||
} | ||
private async _methodHandler( | ||
method: Method | ||
method: Method, | ||
): Promise<ISbResponse | ISbError> { | ||
let urlString = `${this.baseURL}${this.url}` | ||
let urlString = `${this.baseURL}${this.url}`; | ||
let body = null | ||
let body = null; | ||
if (method === 'get') { | ||
const helper = new SbHelpers() | ||
const helper = new SbHelpers(); | ||
urlString = `${this.baseURL}${this.url}?${helper.stringify( | ||
this.parameters | ||
)}` | ||
} else { | ||
body = JSON.stringify(this.parameters) | ||
this.parameters, | ||
)}`; | ||
} | ||
else { | ||
body = JSON.stringify(this.parameters); | ||
} | ||
const url = new URL(urlString) | ||
const url = new URL(urlString); | ||
const controller = new AbortController() | ||
const { signal } = controller | ||
const controller = new AbortController(); | ||
const { signal } = controller; | ||
let timeout | ||
let timeout; | ||
if (this.timeout) { | ||
timeout = setTimeout(() => controller.abort(), this.timeout) | ||
timeout = setTimeout(() => controller.abort(), this.timeout); | ||
} | ||
@@ -139,22 +140,24 @@ | ||
...this.fetchOptions, | ||
}) | ||
}); | ||
if (this.timeout) { | ||
clearTimeout(timeout) | ||
clearTimeout(timeout); | ||
} | ||
const response = (await this._responseHandler( | ||
fetchResponse | ||
)) as ISbResponse | ||
fetchResponse, | ||
)) as ISbResponse; | ||
if (this.responseInterceptor && !this.ejectInterceptor) { | ||
return this._statusHandler(this.responseInterceptor(response)) | ||
} else { | ||
return this._statusHandler(response) | ||
return this._statusHandler(this.responseInterceptor(response)); | ||
} | ||
} catch (err: any) { | ||
else { | ||
return this._statusHandler(response); | ||
} | ||
} | ||
catch (err: any) { | ||
const error: ISbError = { | ||
message: err, | ||
} | ||
return error | ||
}; | ||
return error; | ||
} | ||
@@ -165,17 +168,17 @@ } | ||
if (Object.keys(fetchOptions).length > 0 && 'method' in fetchOptions) { | ||
delete fetchOptions.method | ||
delete fetchOptions.method; | ||
} | ||
this.fetchOptions = { ...fetchOptions } | ||
this.fetchOptions = { ...fetchOptions }; | ||
} | ||
public eject() { | ||
this.ejectInterceptor = true | ||
this.ejectInterceptor = true; | ||
} | ||
private _statusHandler(res: ISbResponse): Promise<ISbResponse | ISbError> { | ||
const statusOk = /20[0-6]/g | ||
const statusOk = /20[0-6]/g; | ||
return new Promise((resolve, reject) => { | ||
if (statusOk.test(`${res.status}`)) { | ||
return resolve(res) | ||
return resolve(res); | ||
} | ||
@@ -189,9 +192,9 @@ | ||
: res.data.error || res.data.slug, | ||
} | ||
}; | ||
reject(error) | ||
}) | ||
reject(error); | ||
}); | ||
} | ||
} | ||
export default SbFetch | ||
export default SbFetch; |
@@ -1,29 +0,29 @@ | ||
import { describe, it, expect, beforeEach, vi } from 'vitest' | ||
import { SbHelpers } from './sbHelpers' | ||
import { beforeEach, describe, expect, it, vi } from 'vitest'; | ||
import { SbHelpers } from './sbHelpers'; | ||
describe('SbHelpers', () => { | ||
let helpers: SbHelpers | ||
describe('sbHelpers', () => { | ||
let helpers: SbHelpers; | ||
beforeEach(() => { | ||
helpers = new SbHelpers() | ||
}) | ||
helpers = new SbHelpers(); | ||
}); | ||
it('should create a new instance', () => { | ||
expect(helpers).toBeDefined() | ||
expect(helpers).toBeInstanceOf(SbHelpers) | ||
}) | ||
expect(helpers).toBeDefined(); | ||
expect(helpers).toBeInstanceOf(SbHelpers); | ||
}); | ||
describe('isCDNUrl', () => { | ||
it('returns true if the URL contains /cdn/', () => { | ||
expect(helpers.isCDNUrl('http://example.com/cdn/content')).toBe(true) | ||
}) | ||
expect(helpers.isCDNUrl('http://example.com/cdn/content')).toBe(true); | ||
}); | ||
it('returns false if the URL does not contain /cdn/', () => { | ||
expect(helpers.isCDNUrl('http://example.com/content')).toBe(false) | ||
}) | ||
}) | ||
expect(helpers.isCDNUrl('http://example.com/content')).toBe(false); | ||
}); | ||
}); | ||
describe('getOptionsPage', () => { | ||
it('constructs options with default pagination', () => { | ||
const options = { uuid: 'awiwi' } | ||
const options = { uuid: 'awiwi' }; | ||
expect(helpers.getOptionsPage(options)).toEqual({ | ||
@@ -33,4 +33,4 @@ uuid: 'awiwi', | ||
page: 1, | ||
}) | ||
}) | ||
}); | ||
}); | ||
@@ -42,15 +42,15 @@ it('overrides defaults when parameters are provided', () => { | ||
page: 2, | ||
}) | ||
}) | ||
}) | ||
}); | ||
}); | ||
}); | ||
describe('delay', () => { | ||
it('delays execution by specified ms', async () => { | ||
vi.useFakeTimers() | ||
const promise = helpers.delay(1000) | ||
vi.advanceTimersByTime(1000) | ||
await expect(promise).resolves.toBeUndefined() | ||
vi.useRealTimers() | ||
}) | ||
}) | ||
vi.useFakeTimers(); | ||
const promise = helpers.delay(1000); | ||
vi.advanceTimersByTime(1000); | ||
await expect(promise).resolves.toBeUndefined(); | ||
vi.useRealTimers(); | ||
}); | ||
}); | ||
@@ -60,15 +60,15 @@ describe.skip('range', () => { | ||
// TODO: This test is failing on the current implementation | ||
expect(helpers.range(1, 5)).toEqual([1, 2, 3, 4, 5]) | ||
expect(helpers.range(5, 1)).toEqual([5, 4, 3, 2, 1]) | ||
}) | ||
}) | ||
expect(helpers.range(1, 5)).toEqual([1, 2, 3, 4, 5]); | ||
expect(helpers.range(5, 1)).toEqual([5, 4, 3, 2, 1]); | ||
}); | ||
}); | ||
describe('asyncMap', () => { | ||
it('applies an async function to each element in the array', async () => { | ||
const numbers = [1, 2, 3] | ||
const doubleAsync = async (n: number) => n * 2 | ||
const results = await helpers.asyncMap(numbers, doubleAsync) | ||
expect(results).toEqual([2, 4, 6]) | ||
}) | ||
}) | ||
const numbers = [1, 2, 3]; | ||
const doubleAsync = async (n: number) => n * 2; | ||
const results = await helpers.asyncMap(numbers, doubleAsync); | ||
expect(results).toEqual([2, 4, 6]); | ||
}); | ||
}); | ||
@@ -80,77 +80,77 @@ describe('flatMap', () => { | ||
{ id: 2, values: [30, 40] }, | ||
] | ||
const flattenValues = (item: { values: number[] }) => item.values | ||
const result = helpers.flatMap(data, flattenValues) | ||
expect(result).toEqual([10, 20, 30, 40]) | ||
}) | ||
}) | ||
]; | ||
const flattenValues = (item: { values: number[] }) => item.values; | ||
const result = helpers.flatMap(data, flattenValues); | ||
expect(result).toEqual([10, 20, 30, 40]); | ||
}); | ||
}); | ||
describe('stringify', () => { | ||
it('stringifies simple objects', () => { | ||
const params = { name: 'John', age: 30 } | ||
const result = helpers.stringify(params) | ||
expect(result).toBe('name=John&age=30') | ||
}) | ||
const params = { name: 'John', age: 30 }; | ||
const result = helpers.stringify(params); | ||
expect(result).toBe('name=John&age=30'); | ||
}); | ||
it('handles arrays correctly', () => { | ||
const params = { names: ['John', 'Jane'] } | ||
const result = helpers.stringify(params, '', true) | ||
expect(result).toBe('=John&=Jane') | ||
}) | ||
}) | ||
const params = { names: ['John', 'Jane'] }; | ||
const result = helpers.stringify(params, '', true); | ||
expect(result).toBe('=John&=Jane'); | ||
}); | ||
}); | ||
describe('arrayFrom function', () => { | ||
it('arrayFrom(undefined, (v, i) => i)) should be an empty array', () => { | ||
expect(helpers.arrayFrom(undefined, (_, i) => i)).toEqual([]) | ||
}) | ||
expect(helpers.arrayFrom(undefined, (_, i) => i)).toEqual([]); | ||
}); | ||
it('arrayFrom(0, (v, i) => i)) should be an empty array', () => { | ||
expect(helpers.arrayFrom(0, (_, i) => i)).toEqual([]) | ||
}) | ||
expect(helpers.arrayFrom(0, (_, i) => i)).toEqual([]); | ||
}); | ||
it('arrayFrom(2, () => 1) should be an array with 1 and 1', () => { | ||
expect(helpers.arrayFrom(2, () => 1)).toEqual([1, 1]) | ||
}) | ||
expect(helpers.arrayFrom(2, () => 1)).toEqual([1, 1]); | ||
}); | ||
it('arrayFrom(2, (v, i) => v)) should be an array with undefined values', () => { | ||
expect(helpers.arrayFrom(2, (v) => v)).toEqual([undefined, undefined]) | ||
}) | ||
expect(helpers.arrayFrom(2, v => v)).toEqual([undefined, undefined]); | ||
}); | ||
it('arrayFrom(2, (v, i) => i) should be an array with 0 and 1', () => { | ||
expect(helpers.arrayFrom(2, (v, i) => i)).toEqual([0, 1]) | ||
}) | ||
}) | ||
expect(helpers.arrayFrom(2, (v, i) => i)).toEqual([0, 1]); | ||
}); | ||
}); | ||
describe('getRegionURL', () => { | ||
it('returns the EU API URL by default', () => { | ||
expect(helpers.getRegionURL()).toBe('api.storyblok.com') | ||
expect(helpers.getRegionURL('unknown')).toBe('api.storyblok.com') // test for unrecognized region code | ||
}) | ||
expect(helpers.getRegionURL()).toBe('api.storyblok.com'); | ||
expect(helpers.getRegionURL('unknown')).toBe('api.storyblok.com'); // test for unrecognized region code | ||
}); | ||
it('returns the US API URL when region code is "us"', () => { | ||
expect(helpers.getRegionURL('us')).toBe('api-us.storyblok.com') | ||
}) | ||
expect(helpers.getRegionURL('us')).toBe('api-us.storyblok.com'); | ||
}); | ||
it('returns the CN API URL when region code is "cn"', () => { | ||
expect(helpers.getRegionURL('cn')).toBe('app.storyblokchina.cn') | ||
}) | ||
expect(helpers.getRegionURL('cn')).toBe('app.storyblokchina.cn'); | ||
}); | ||
it('returns the AP API URL when region code is "ap"', () => { | ||
expect(helpers.getRegionURL('ap')).toBe('api-ap.storyblok.com') | ||
}) | ||
expect(helpers.getRegionURL('ap')).toBe('api-ap.storyblok.com'); | ||
}); | ||
it('returns the CA API URL when region code is "ca"', () => { | ||
expect(helpers.getRegionURL('ca')).toBe('api-ca.storyblok.com') | ||
}) | ||
}) | ||
expect(helpers.getRegionURL('ca')).toBe('api-ca.storyblok.com'); | ||
}); | ||
}); | ||
describe('escapeHTML', () => { | ||
it('escapes HTML characters', () => { | ||
const str = '<div>Test & "more" test</div>' | ||
const escaped = helpers.escapeHTML(str) | ||
const str = '<div>Test & "more" test</div>'; | ||
const escaped = helpers.escapeHTML(str); | ||
expect(escaped).toBe( | ||
'<div>Test & "more" test</div>' | ||
) | ||
}) | ||
}) | ||
}) | ||
'<div>Test & "more" test</div>', | ||
); | ||
}); | ||
}); | ||
}); |
@@ -1,14 +0,20 @@ | ||
import { ISbStoriesParams, ISbResult, AsyncFn, HtmlEscapes } from './interfaces' | ||
import type { | ||
AsyncFn, | ||
HtmlEscapes, | ||
ISbResult, | ||
ISbStoriesParams, | ||
} from './interfaces'; | ||
interface ISbParams extends ISbStoriesParams { | ||
[key: string]: any | ||
[key: string]: any; | ||
} | ||
type ArrayFn = (...args: any) => void | ||
type ArrayFn = (...args: any) => void; | ||
type FlatMapFn = (...args: any) => [] | any | ||
type FlatMapFn = (...args: any) => [] | any; | ||
type RangeFn = (...args: any) => [] | ||
type RangeFn = (...args: any) => []; | ||
export class SbHelpers { | ||
public isCDNUrl = (url = '') => url.indexOf('/cdn/') > -1 | ||
public isCDNUrl = (url = '') => url.includes('/cdn/'); | ||
@@ -18,3 +24,3 @@ public getOptionsPage = ( | ||
perPage = 25, | ||
page = 1 | ||
page = 1, | ||
) => { | ||
@@ -25,27 +31,27 @@ return { | ||
page, | ||
} | ||
} | ||
}; | ||
}; | ||
public delay = (ms: number) => new Promise((res) => setTimeout(res, ms)) | ||
public delay = (ms: number) => new Promise(res => setTimeout(res, ms)); | ||
public arrayFrom = (length = 0, func: ArrayFn) => [...Array(length)].map(func) | ||
public arrayFrom = (length = 0, func: ArrayFn) => Array.from({ length }, func); | ||
public range = (start = 0, end = start): Array<any> => { | ||
const length = Math.abs(end - start) || 0 | ||
const step = start < end ? 1 : -1 | ||
return this.arrayFrom(length, (_, i: number) => i * step + start) | ||
} | ||
const length = Math.abs(end - start) || 0; | ||
const step = start < end ? 1 : -1; | ||
return this.arrayFrom(length, (_, i: number) => i * step + start); | ||
}; | ||
public asyncMap = async (arr: RangeFn[], func: AsyncFn) => | ||
Promise.all(arr.map(func)) | ||
Promise.all(arr.map(func)); | ||
public flatMap = (arr: ISbResult[] = [], func: FlatMapFn) => | ||
arr.map(func).reduce((xs, ys) => [...xs, ...ys], []) | ||
arr.map(func).reduce((xs, ys) => [...xs, ...ys], []); | ||
/** | ||
* @method stringify | ||
* @param {Object} params | ||
* @param {String} prefix | ||
* @param {Boolean} isArray | ||
* @return {String} Stringified object | ||
* @param {object} params | ||
* @param {string} prefix | ||
* @param {boolean} isArray | ||
* @return {string} Stringified object | ||
*/ | ||
@@ -55,27 +61,27 @@ public stringify( | ||
prefix?: string, | ||
isArray?: boolean | ||
isArray?: boolean, | ||
): string { | ||
const pairs = [] | ||
const pairs = []; | ||
for (const key in params) { | ||
if (!Object.prototype.hasOwnProperty.call(params, key)) { | ||
continue | ||
continue; | ||
} | ||
const value = params[key] | ||
const enkey = isArray ? '' : encodeURIComponent(key) | ||
let pair | ||
const value = params[key]; | ||
const enkey = isArray ? '' : encodeURIComponent(key); | ||
let pair; | ||
if (typeof value === 'object') { | ||
pair = this.stringify( | ||
value, | ||
prefix ? prefix + encodeURIComponent('[' + enkey + ']') : enkey, | ||
Array.isArray(value) | ||
) | ||
} else { | ||
pair = | ||
(prefix ? prefix + encodeURIComponent('[' + enkey + ']') : enkey) + | ||
'=' + | ||
encodeURIComponent(value) | ||
prefix ? prefix + encodeURIComponent(`[${enkey}]`) : enkey, | ||
Array.isArray(value), | ||
); | ||
} | ||
pairs.push(pair) | ||
else { | ||
pair = `${ | ||
prefix ? prefix + encodeURIComponent(`[${enkey}]`) : enkey | ||
}=${encodeURIComponent(value)}`; | ||
} | ||
pairs.push(pair); | ||
} | ||
return pairs.join('&') | ||
return pairs.join('&'); | ||
} | ||
@@ -85,23 +91,23 @@ | ||
* @method getRegionURL | ||
* @param {String} regionCode region code, could be eu, us, cn, ap or ca | ||
* @return {String} The base URL of the region | ||
* @param {string} regionCode region code, could be eu, us, cn, ap or ca | ||
* @return {string} The base URL of the region | ||
*/ | ||
public getRegionURL(regionCode?: string): string { | ||
const EU_API_URL = 'api.storyblok.com' | ||
const US_API_URL = 'api-us.storyblok.com' | ||
const CN_API_URL = 'app.storyblokchina.cn' | ||
const AP_API_URL = 'api-ap.storyblok.com' | ||
const CA_API_URL = 'api-ca.storyblok.com' | ||
const EU_API_URL = 'api.storyblok.com'; | ||
const US_API_URL = 'api-us.storyblok.com'; | ||
const CN_API_URL = 'app.storyblokchina.cn'; | ||
const AP_API_URL = 'api-ap.storyblok.com'; | ||
const CA_API_URL = 'api-ca.storyblok.com'; | ||
switch (regionCode) { | ||
case 'us': | ||
return US_API_URL | ||
return US_API_URL; | ||
case 'cn': | ||
return CN_API_URL | ||
return CN_API_URL; | ||
case 'ap': | ||
return AP_API_URL | ||
return AP_API_URL; | ||
case 'ca': | ||
return CA_API_URL | ||
return CA_API_URL; | ||
default: | ||
return EU_API_URL | ||
return EU_API_URL; | ||
} | ||
@@ -112,4 +118,4 @@ } | ||
* @method escapeHTML | ||
* @param {String} string text to be parsed | ||
* @return {String} Text parsed | ||
* @param {string} string text to be parsed | ||
* @return {string} Text parsed | ||
*/ | ||
@@ -122,12 +128,12 @@ public escapeHTML = function (string: string) { | ||
'"': '"', | ||
"'": ''', | ||
} as HtmlEscapes | ||
'\'': ''', | ||
} as HtmlEscapes; | ||
const reUnescapedHtml = /[&<>"']/g | ||
const reHasUnescapedHtml = RegExp(reUnescapedHtml.source) | ||
const reUnescapedHtml = /[&<>"']/g; | ||
const reHasUnescapedHtml = new RegExp(reUnescapedHtml.source); | ||
return string && reHasUnescapedHtml.test(string) | ||
? string.replace(reUnescapedHtml, (chr) => htmlEscapes[chr]) | ||
: string | ||
} | ||
? string.replace(reUnescapedHtml, chr => htmlEscapes[chr]) | ||
: string; | ||
}; | ||
} |
@@ -1,20 +0,25 @@ | ||
import { ISbNode, NodeSchema, MarkSchema, ISbComponentType } from './interfaces' | ||
import { SbHelpers } from './sbHelpers' | ||
import type { | ||
ISbComponentType, | ||
ISbNode, | ||
MarkSchema, | ||
NodeSchema, | ||
} from './interfaces'; | ||
import { SbHelpers } from './sbHelpers'; | ||
const pick = function (attrs: Attrs, allowed: string[]) { | ||
const h = {} as Attrs | ||
const h = {} as Attrs; | ||
for (const key in attrs) { | ||
const value = attrs[key] | ||
if (allowed.indexOf(key) > -1 && value !== null) { | ||
h[key] = value | ||
const value = attrs[key]; | ||
if (allowed.includes(key) && value !== null) { | ||
h[key] = value; | ||
} | ||
} | ||
return h | ||
} | ||
return h; | ||
}; | ||
const isEmailLinkType = (type: string) => type === 'email' | ||
const isEmailLinkType = (type: string) => type === 'email'; | ||
type Attrs = { | ||
[key: string]: string | number | Array<ISbComponentType<any>> | ||
interface Attrs { | ||
[key: string]: string | number | Array<ISbComponentType<any>>; | ||
} | ||
@@ -26,14 +31,14 @@ | ||
singleTag: 'hr', | ||
} | ||
} | ||
}; | ||
}; | ||
const blockquote: NodeSchema = () => { | ||
return { | ||
tag: 'blockquote', | ||
} | ||
} | ||
}; | ||
}; | ||
const bullet_list: NodeSchema = () => { | ||
return { | ||
tag: 'ul', | ||
} | ||
} | ||
}; | ||
}; | ||
const code_block: NodeSchema = (node: ISbNode) => { | ||
@@ -48,14 +53,14 @@ return { | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
const hard_break: NodeSchema = () => { | ||
return { | ||
singleTag: 'br', | ||
} | ||
} | ||
}; | ||
}; | ||
const heading: NodeSchema = (node: ISbNode) => { | ||
return { | ||
tag: `h${node.attrs.level}`, | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -70,26 +75,26 @@ const image: NodeSchema = (node: ISbNode) => { | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
const list_item: NodeSchema = () => { | ||
return { | ||
tag: 'li', | ||
} | ||
} | ||
}; | ||
}; | ||
const ordered_list: NodeSchema = () => { | ||
return { | ||
tag: 'ol', | ||
} | ||
} | ||
}; | ||
}; | ||
const paragraph: NodeSchema = () => { | ||
return { | ||
tag: 'p', | ||
} | ||
} | ||
}; | ||
}; | ||
const emoji: NodeSchema = (node: ISbNode) => { | ||
const attrs = { | ||
['data-type']: 'emoji', | ||
['data-name']: node.attrs.name, | ||
emoji: node.attrs.emoji, | ||
} | ||
'data-type': 'emoji', | ||
'data-name': node.attrs.name, | ||
'emoji': node.attrs.emoji, | ||
}; | ||
@@ -100,7 +105,7 @@ return { | ||
tag: 'span', | ||
attrs: attrs, | ||
attrs, | ||
}, | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -111,29 +116,29 @@ // marks | ||
tag: 'b', | ||
} | ||
} | ||
}; | ||
}; | ||
const strike: MarkSchema = () => { | ||
return { | ||
tag: 's', | ||
} | ||
} | ||
}; | ||
}; | ||
const underline: MarkSchema = () => { | ||
return { | ||
tag: 'u', | ||
} | ||
} | ||
}; | ||
}; | ||
const strong: MarkSchema = () => { | ||
return { | ||
tag: 'strong', | ||
} | ||
} | ||
}; | ||
}; | ||
const code: MarkSchema = () => { | ||
return { | ||
tag: 'code', | ||
} | ||
} | ||
}; | ||
}; | ||
const italic: MarkSchema = () => { | ||
return { | ||
tag: 'i', | ||
} | ||
} | ||
}; | ||
}; | ||
const link: MarkSchema = (node: ISbNode) => { | ||
@@ -143,20 +148,20 @@ if (!node.attrs) { | ||
tag: '', | ||
} | ||
}; | ||
} | ||
const escapeHTML = new SbHelpers().escapeHTML | ||
const attrs = { ...node.attrs } | ||
const { linktype = 'url' } = node.attrs | ||
delete attrs.linktype | ||
const escapeHTML = new SbHelpers().escapeHTML; | ||
const attrs = { ...node.attrs }; | ||
const { linktype = 'url' } = node.attrs; | ||
delete attrs.linktype; | ||
if (attrs.href) { | ||
attrs.href = escapeHTML(node.attrs.href || '') | ||
attrs.href = escapeHTML(node.attrs.href || ''); | ||
} | ||
if (isEmailLinkType(linktype)) { | ||
attrs.href = `mailto:${attrs.href}` | ||
attrs.href = `mailto:${attrs.href}`; | ||
} | ||
if (attrs.anchor) { | ||
attrs.href = `${attrs.href}#${attrs.anchor}` | ||
delete attrs.anchor | ||
attrs.href = `${attrs.href}#${attrs.anchor}`; | ||
delete attrs.anchor; | ||
} | ||
@@ -166,5 +171,5 @@ | ||
for (const key in attrs.custom) { | ||
attrs[key] = attrs.custom[key] | ||
attrs[key] = attrs.custom[key]; | ||
} | ||
delete attrs.custom | ||
delete attrs.custom; | ||
} | ||
@@ -176,7 +181,7 @@ | ||
tag: 'a', | ||
attrs: attrs, | ||
attrs, | ||
}, | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -191,4 +196,4 @@ const styled: MarkSchema = (node: ISbNode) => { | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -198,4 +203,4 @@ const subscript: MarkSchema = () => { | ||
tag: 'sub', | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -205,4 +210,4 @@ const superscript: MarkSchema = () => { | ||
tag: 'sup', | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -217,14 +222,15 @@ const anchor: MarkSchema = (node: ISbNode) => { | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
const highlight: MarkSchema = (node: ISbNode) => { | ||
if (!node.attrs?.color) | ||
if (!node.attrs?.color) { | ||
return { | ||
tag: '', | ||
} | ||
}; | ||
} | ||
const attrs = { | ||
['style']: `background-color:${node.attrs.color};`, | ||
} | ||
style: `background-color:${node.attrs.color};`, | ||
}; | ||
return { | ||
@@ -237,14 +243,15 @@ tag: [ | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
const textStyle: MarkSchema = (node: ISbNode) => { | ||
if (!node.attrs?.color) | ||
if (!node.attrs?.color) { | ||
return { | ||
tag: '', | ||
} | ||
}; | ||
} | ||
const attrs = { | ||
['style']: `color:${node.attrs.color}`, | ||
} | ||
style: `color:${node.attrs.color}`, | ||
}; | ||
return { | ||
@@ -257,4 +264,4 @@ tag: [ | ||
], | ||
} | ||
} | ||
}; | ||
}; | ||
@@ -290,2 +297,2 @@ export default { | ||
}, | ||
} | ||
}; |
@@ -1,104 +0,88 @@ | ||
import { ThrottleFn } from './interfaces' | ||
import type { ISbThrottle, Queue } from './interfaces'; | ||
type Shifted = { | ||
args: any | ||
self: any | ||
resolve: (args: any) => any | ||
} | ||
type Queue = { | ||
resolve: (args: any) => any | ||
reject: (args: any) => any | ||
args: any[] | ||
self: any | ||
} | ||
interface ISbThrottle { | ||
abort: () => any | ||
(args: []): Promise<Queue> | ||
name: string | ||
AbortError?: () => void | ||
} | ||
function isFinite(value: number) { | ||
if (value !== value || value === Infinity || value === -Infinity) { | ||
return false | ||
class AbortError extends Error { | ||
constructor(msg: string) { | ||
super(msg); | ||
this.name = 'AbortError'; | ||
} | ||
return true | ||
} | ||
function throttledQueue(fn: ThrottleFn, limit: number, interval: number) { | ||
if (!isFinite(limit)) { | ||
throw new TypeError('Expected `limit` to be a finite number') | ||
function throttledQueue<T extends (...args: Parameters<T>) => ReturnType<T>>( | ||
fn: T, | ||
limit: number, | ||
interval: number, | ||
): ISbThrottle<T> { | ||
if (!Number.isFinite(limit)) { | ||
throw new TypeError('Expected `limit` to be a finite number'); | ||
} | ||
if (!isFinite(interval)) { | ||
throw new TypeError('Expected `interval` to be a finite number') | ||
if (!Number.isFinite(interval)) { | ||
throw new TypeError('Expected `interval` to be a finite number'); | ||
} | ||
const queue: Queue[] = [] | ||
let timeouts: ReturnType<typeof setTimeout>[] = [] | ||
let activeCount = 0 | ||
const queue: Queue<Parameters<T>>[] = []; | ||
let timeouts: ReturnType<typeof setTimeout>[] = []; | ||
let activeCount = 0; | ||
let isAborted = false; | ||
const next = function () { | ||
activeCount++ | ||
const next = async () => { | ||
activeCount++; | ||
const id = setTimeout(function () { | ||
activeCount-- | ||
const x = queue.shift(); | ||
if (x) { | ||
const res = await fn(...x.args); | ||
x.resolve(res); | ||
} | ||
const id = setTimeout(() => { | ||
activeCount--; | ||
if (queue.length > 0) { | ||
next() | ||
next(); | ||
} | ||
timeouts = timeouts.filter(function (currentId) { | ||
return currentId !== id | ||
}) | ||
}, interval) | ||
timeouts = timeouts.filter(currentId => currentId !== id); | ||
}, interval); | ||
if (timeouts.indexOf(id) < 0) { | ||
timeouts.push(id) | ||
if (!timeouts.includes(id)) { | ||
timeouts.push(id); | ||
} | ||
}; | ||
const x = queue.shift() as unknown as Shifted | ||
x.resolve(fn.apply(x.self, x.args)) | ||
} | ||
const throttled: ISbThrottle<T> = (...args) => { | ||
if (isAborted) { | ||
return Promise.reject( | ||
new Error( | ||
'Throttled function is already aborted and not accepting new promises', | ||
), | ||
); | ||
} | ||
const throttled: ISbThrottle = function ( | ||
this: ISbThrottle, | ||
...args: [] | ||
): Promise<Queue> { | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const self = this | ||
return new Promise(function (resolve, reject) { | ||
return new Promise((resolve, reject) => { | ||
queue.push({ | ||
resolve: resolve, | ||
reject: reject, | ||
args: args, | ||
self, | ||
}) | ||
resolve, | ||
reject, | ||
args, | ||
}); | ||
if (activeCount < limit) { | ||
next() | ||
next(); | ||
} | ||
}) | ||
} | ||
}); | ||
}; | ||
throttled.abort = function () { | ||
timeouts.forEach(clearTimeout) | ||
timeouts = [] | ||
throttled.abort = () => { | ||
isAborted = true; | ||
timeouts.forEach(clearTimeout); | ||
timeouts = []; | ||
queue.forEach(function (x) { | ||
x.reject(function (this: ISbThrottle) { | ||
Error.call(this, 'Throttled function aborted') | ||
this.name = 'AbortError' | ||
}) | ||
}) | ||
queue.length = 0 | ||
} | ||
queue.forEach(x => | ||
x.reject(() => new AbortError('Throttle function aborted')), | ||
); | ||
queue.length = 0; | ||
}; | ||
return throttled | ||
return throttled; | ||
} | ||
export default throttledQueue | ||
export default throttledQueue; |
@@ -1,6 +0,6 @@ | ||
import StoryblokClient from 'storyblok-js-client' | ||
import { describe, it, expect, beforeEach } from 'vitest' | ||
import StoryblokClient from 'storyblok-js-client'; | ||
import { beforeEach, describe, expect, it } from 'vitest'; | ||
describe('StoryblokClient', () => { | ||
let client | ||
let client: StoryblokClient; | ||
@@ -12,4 +12,4 @@ beforeEach(() => { | ||
cache: { type: 'memory', clear: 'auto' }, | ||
}) | ||
}) | ||
}); | ||
}); | ||
// TODO: Uncomment when we have a valid token | ||
@@ -32,37 +32,37 @@ /* if (process.env.VITE_OAUTH_TOKEN) { | ||
describe('get function', () => { | ||
it("get('cdn/spaces/me') should return the space information", async () => { | ||
const { data } = await client.get('cdn/spaces/me') | ||
expect(data.space.id).toBe(Number(process.env.VITE_SPACE_ID)) | ||
}) | ||
it('get(\'cdn/spaces/me\') should return the space information', async () => { | ||
const { data } = await client.get('cdn/spaces/me'); | ||
expect(data.space.id).toBe(Number(process.env.VITE_SPACE_ID)); | ||
}); | ||
it("get('cdn/stories') should return all stories", async () => { | ||
const { data } = await client.get('cdn/stories') | ||
expect(data.stories.length).toBeGreaterThan(0) | ||
}) | ||
it('get(\'cdn/stories\') should return all stories', async () => { | ||
const { data } = await client.get('cdn/stories'); | ||
expect(data.stories.length).toBeGreaterThan(0); | ||
}); | ||
it("get('cdn/stories/testcontent-0' should return the specific story", async () => { | ||
const { data } = await client.get('cdn/stories/testcontent-0') | ||
expect(data.story.slug).toBe('testcontent-0') | ||
}) | ||
it('get(\'cdn/stories/testcontent-0\' should return the specific story', async () => { | ||
const { data } = await client.get('cdn/stories/testcontent-0'); | ||
expect(data.story.slug).toBe('testcontent-0'); | ||
}); | ||
it("get('cdn/stories' { starts_with: testcontent-0 } should return the specific story", async () => { | ||
it('get(\'cdn/stories\' { starts_with: testcontent-0 } should return the specific story', async () => { | ||
const { data } = await client.get('cdn/stories', { | ||
starts_with: 'testcontent-0', | ||
}) | ||
expect(data.stories.length).toBe(1) | ||
}) | ||
}); | ||
expect(data.stories.length).toBe(1); | ||
}); | ||
it("get('cdn/stories/testcontent-draft', { version: 'draft' }) should return the specific story draft", async () => { | ||
it('get(\'cdn/stories/testcontent-draft\', { version: \'draft\' }) should return the specific story draft', async () => { | ||
const { data } = await client.get('cdn/stories/testcontent-draft', { | ||
version: 'draft' | ||
}) | ||
expect(data.story.slug).toBe('testcontent-draft') | ||
}) | ||
version: 'draft', | ||
}); | ||
expect(data.story.slug).toBe('testcontent-draft'); | ||
}); | ||
it("get('cdn/stories/testcontent-0', { version: 'published' }) should return the specific story published", async () => { | ||
it('get(\'cdn/stories/testcontent-0\', { version: \'published\' }) should return the specific story published', async () => { | ||
const { data } = await client.get('cdn/stories/testcontent-0', { | ||
version: 'published', | ||
}) | ||
expect(data.story.slug).toBe('testcontent-0') | ||
}) | ||
}); | ||
expect(data.story.slug).toBe('testcontent-0'); | ||
}); | ||
@@ -72,29 +72,29 @@ it('cdn/stories/testcontent-0 should resolve author relations', async () => { | ||
resolve_relations: 'root.author', | ||
}) | ||
console.log(data) | ||
expect(data.story.content.author[0].slug).toBe('edgar-allan-poe') | ||
}) | ||
}); | ||
it("get('cdn/stories', { by_slugs: 'folder/*' }) should return the specific story", async () => { | ||
expect(data.story.content.author[0].slug).toBe('edgar-allan-poe'); | ||
}); | ||
it('get(\'cdn/stories\', { by_slugs: \'folder/*\' }) should return the specific story', async () => { | ||
const { data } = await client.get('cdn/stories', { | ||
by_slugs: 'folder/*', | ||
}) | ||
expect(data.stories.length).toBeGreaterThan(0) | ||
}) | ||
}) | ||
}); | ||
expect(data.stories.length).toBeGreaterThan(0); | ||
}); | ||
}); | ||
describe('getAll function', () => { | ||
it("getAll('cdn/stories') should return all stories", async () => { | ||
const result = await client.getAll('cdn/stories') | ||
expect(result.length).toBeGreaterThan(0) | ||
}) | ||
it('getAll(\'cdn/stories\') should return all stories', async () => { | ||
const result = await client.getAll('cdn/stories'); | ||
expect(result.length).toBeGreaterThan(0); | ||
}); | ||
it("getAll('cdn/stories') should return all stories with filtered results", async () => { | ||
it('getAll(\'cdn/stories\') should return all stories with filtered results', async () => { | ||
const result = await client.getAll('cdn/stories', { | ||
starts_with: 'testcontent-0', | ||
}) | ||
expect(result.length).toBe(1) | ||
}) | ||
}); | ||
expect(result.length).toBe(1); | ||
}); | ||
it("getAll('cdn/stories', filter_query: { __or: [{ category: { any_in_array: 'Category 1' } }, { category: { any_in_array: 'Category 2' } }]}) should return all stories with the specific filter applied", async () => { | ||
it('getAll(\'cdn/stories\', filter_query: { __or: [{ category: { any_in_array: \'Category 1\' } }, { category: { any_in_array: \'Category 2\' } }]}) should return all stories with the specific filter applied', async () => { | ||
const result = await client.getAll('cdn/stories', { | ||
@@ -107,45 +107,45 @@ filter_query: { | ||
}, | ||
}) | ||
expect(result.length).toBeGreaterThan(0) | ||
}) | ||
}); | ||
expect(result.length).toBeGreaterThan(0); | ||
}); | ||
it("getAll('cdn/stories', {by_slugs: 'folder/*'}) should return all stories with the specific filter applied", async () => { | ||
it('getAll(\'cdn/stories\', {by_slugs: \'folder/*\'}) should return all stories with the specific filter applied', async () => { | ||
const result = await client.getAll('cdn/stories', { | ||
by_slugs: 'folder/*', | ||
}) | ||
expect(result.length).toBeGreaterThan(0) | ||
}) | ||
}); | ||
expect(result.length).toBeGreaterThan(0); | ||
}); | ||
it("getAll('cdn/links') should return all links", async () => { | ||
const result = await client.getAll('cdn/links') | ||
expect(result.length).toBeGreaterThan(0) | ||
}) | ||
}) | ||
it('getAll(\'cdn/links\') should return all links', async () => { | ||
const result = await client.getAll('cdn/links'); | ||
expect(result.length).toBeGreaterThan(0); | ||
}); | ||
}); | ||
describe('caching', () => { | ||
it("get('cdn/spaces/me') should not be cached", async () => { | ||
const provider = client.cacheProvider() | ||
await provider.flush() | ||
await client.get('cdn/spaces/me') | ||
expect(Object.values(provider.getAll()).length).toBe(0) | ||
}) | ||
it('get(\'cdn/spaces/me\') should not be cached', async () => { | ||
const provider = client.cacheProvider(); | ||
await provider.flush(); | ||
await client.get('cdn/spaces/me'); | ||
expect(Object.values(provider.getAll()).length).toBe(0); | ||
}); | ||
it("get('cdn/stories') should be cached when is a published version", async () => { | ||
const cacheVersion = client.cacheVersion() | ||
it('get(\'cdn/stories\') should be cached when is a published version', async () => { | ||
const cacheVersion = client.cacheVersion(); | ||
await client.get('cdn/stories') | ||
await client.get('cdn/stories'); | ||
expect(cacheVersion).not.toBe(undefined) | ||
expect(cacheVersion).not.toBe(undefined); | ||
const newCacheVersion = client.cacheVersion() | ||
const newCacheVersion = client.cacheVersion(); | ||
await client.get('cdn/stories') | ||
await client.get('cdn/stories'); | ||
expect(newCacheVersion).toBe(client.cacheVersion()) | ||
expect(newCacheVersion).toBe(client.cacheVersion()); | ||
await client.get('cdn/stories') | ||
await client.get('cdn/stories'); | ||
expect(newCacheVersion).toBe(client.cacheVersion()) | ||
}) | ||
}) | ||
}) | ||
expect(newCacheVersion).toBe(client.cacheVersion()); | ||
}); | ||
}); | ||
}); |
@@ -1,1 +0,1 @@ | ||
import 'isomorphic-fetch' | ||
import 'isomorphic-fetch'; |
@@ -1,7 +0,7 @@ | ||
export function headersToObject(headers) { | ||
const obj = {} | ||
export function headersToObject(headers: Headers) { | ||
const obj: { [key: string]: string } = {}; | ||
for (const [key, value] of headers.entries()) { | ||
obj[key] = value | ||
obj[key] = value; | ||
} | ||
return obj | ||
return obj; | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
209887
36
5182