@dismissible/react-client 2.0.0 → 2.1.0-canary.6.0a7a428
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +253 -1
- package/dist/clients/defaultClient.d.ts +25 -0
- package/dist/contexts/DismissibleProvider.d.ts +22 -0
- package/dist/dismissible-client.es.js +683 -504
- package/dist/dismissible-client.umd.js +1 -1
- package/dist/hooks/useDismissibleItem.d.ts +1 -3
- package/dist/root.d.ts +2 -0
- package/dist/types/dismissible.types.d.ts +113 -7
- package/dist/utils/BatchScheduler.d.ts +72 -0
- package/dist/utils/url.utils.d.ts +9 -0
- package/package.json +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
(function(c,d){typeof exports=="object"&&typeof module!="undefined"?d(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],d):(c=typeof globalThis!="undefined"?globalThis:c||self,d(c.DismissibleClient={},c.React.jsxRuntime,c.React))})(this,(function(c,d,o){"use strict";var je=Object.defineProperty,$e=Object.defineProperties;var Te=Object.getOwnPropertyDescriptors;var Q=Object.getOwnPropertySymbols;var me=Object.prototype.hasOwnProperty,ye=Object.prototype.propertyIsEnumerable;var he=(c,d,o)=>d in c?je(c,d,{enumerable:!0,configurable:!0,writable:!0,value:o}):c[d]=o,g=(c,d)=>{for(var o in d||(d={}))me.call(d,o)&&he(c,o,d[o]);if(Q)for(var o of Q(d))ye.call(d,o)&&he(c,o,d[o]);return c},x=(c,d)=>$e(c,Te(d));var te=(c,d)=>{var o={};for(var R in c)me.call(c,R)&&d.indexOf(R)<0&&(o[R]=c[R]);if(c!=null&&Q)for(var R of Q(c))d.indexOf(R)<0&&ye.call(c,R)&&(o[R]=c[R]);return o};var q=(c,d,o)=>new Promise((R,J)=>{var V=j=>{try{H(o.next(j))}catch(F){J(F)}},X=j=>{try{H(o.throw(j))}catch(F){J(F)}},H=j=>j.done?R(j.value):Promise.resolve(j.value).then(V,X);H((o=o.apply(c,d)).next())});const R=/\{[^{}]+\}/g,J=()=>{var e,r;return typeof process=="object"&&Number.parseInt((r=(e=process==null?void 0:process.versions)==null?void 0:e.node)==null?void 0:r.substring(0,2))>=18&&process.versions.undici};function V(){return Math.random().toString(36).slice(2,11)}function X(e){let I=g({},e),{baseUrl:r="",Request:t=globalThis.Request,fetch:n=globalThis.fetch,querySerializer:i,bodySerializer:s,headers:a,requestInitExt:h=void 0}=I,b=te(I,["baseUrl","Request","fetch","querySerializer","bodySerializer","headers","requestInitExt"]);h=J()?h:void 0,r=ne(r);const m=[];function C(u,l){return q(this,null,function*(){var de;const fe=l||{},{baseUrl:A,fetch:$=n,Request:L=t,headers:D,params:S={},parseAs:U="json",querySerializer:T,bodySerializer:W=s!=null?s:we,body:_,middleware:f=[]}=fe,p=te(fe,["baseUrl","fetch","Request","headers","params","parseAs","querySerializer","bodySerializer","body","middleware"]);let y=r;A&&(y=(de=ne(A))!=null?de:r);let k=typeof i=="function"?i:re(i);T&&(k=typeof T=="function"?T:re(g(g({},typeof i=="object"?i:{}),T)));const N=_===void 0?void 0:W(_,se(a,D,S.header)),Ae=se(N===void 0||N instanceof FormData?{}:{"Content-Type":"application/json"},a,D,S.header),P=[...m,...f],De=x(g(g({redirect:"follow"},b),p),{body:N,headers:Ae});let B,G,O=new L(pe(u,{baseUrl:y,params:S,querySerializer:k}),De),w;for(const E in p)E in O||(O[E]=p[E]);if(P.length){B=V(),G=Object.freeze({baseUrl:y,fetch:$,parseAs:U,querySerializer:k,bodySerializer:W});for(const E of P)if(E&&typeof E=="object"&&typeof E.onRequest=="function"){const v=yield E.onRequest({request:O,schemaPath:u,params:S,options:G,id:B});if(v)if(v instanceof L)O=v;else if(v instanceof Response){w=v;break}else throw new Error("onRequest: must return new Request() or Response() when modifying the request")}}if(!w){try{w=yield $(O,h)}catch(E){let v=E;if(P.length)for(let z=P.length-1;z>=0;z--){const K=P[z];if(K&&typeof K=="object"&&typeof K.onError=="function"){const M=yield K.onError({request:O,error:v,schemaPath:u,params:S,options:G,id:B});if(M){if(M instanceof Response){v=void 0,w=M;break}if(M instanceof Error){v=M;continue}throw new Error("onError: must return new Response() or instance of Error")}}}if(v)throw v}if(P.length)for(let E=P.length-1;E>=0;E--){const v=P[E];if(v&&typeof v=="object"&&typeof v.onResponse=="function"){const z=yield v.onResponse({request:O,response:w,schemaPath:u,params:S,options:G,id:B});if(z){if(!(z instanceof Response))throw new Error("onResponse: must return new Response() when modifying the response");w=z}}}}if(w.status===204||O.method==="HEAD"||w.headers.get("Content-Length")==="0")return w.ok?{data:void 0,response:w}:{error:void 0,response:w};if(w.ok)return U==="stream"?{data:w.body,response:w}:{data:yield w[U](),response:w};let ee=yield w.text();try{ee=JSON.parse(ee)}catch(E){}return{error:ee,response:w}})}return{request(u,l,A){return C(l,x(g({},A),{method:u.toUpperCase()}))},GET(u,l){return C(u,x(g({},l),{method:"GET"}))},PUT(u,l){return C(u,x(g({},l),{method:"PUT"}))},POST(u,l){return C(u,x(g({},l),{method:"POST"}))},DELETE(u,l){return C(u,x(g({},l),{method:"DELETE"}))},OPTIONS(u,l){return C(u,x(g({},l),{method:"OPTIONS"}))},HEAD(u,l){return C(u,x(g({},l),{method:"HEAD"}))},PATCH(u,l){return C(u,x(g({},l),{method:"PATCH"}))},TRACE(u,l){return C(u,x(g({},l),{method:"TRACE"}))},use(...u){for(const l of u)if(l){if(typeof l!="object"||!("onRequest"in l||"onResponse"in l||"onError"in l))throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");m.push(l)}},eject(...u){for(const l of u){const A=m.indexOf(l);A!==-1&&m.splice(A,1)}}}}function H(e,r,t){if(r==null)return"";if(typeof r=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${e}=${(t==null?void 0:t.allowReserved)===!0?r:encodeURIComponent(r)}`}function j(e,r,t){if(!r||typeof r!="object")return"";const n=[],i={simple:",",label:".",matrix:";"}[t.style]||"&";if(t.style!=="deepObject"&&t.explode===!1){for(const h in r)n.push(h,t.allowReserved===!0?r[h]:encodeURIComponent(r[h]));const a=n.join(",");switch(t.style){case"form":return`${e}=${a}`;case"label":return`.${a}`;case"matrix":return`;${e}=${a}`;default:return a}}for(const a in r){const h=t.style==="deepObject"?`${e}[${a}]`:a;n.push(H(h,r[a],t))}const s=n.join(i);return t.style==="label"||t.style==="matrix"?`${i}${s}`:s}function F(e,r,t){if(!Array.isArray(r))return"";if(t.explode===!1){const s={form:",",spaceDelimited:"%20",pipeDelimited:"|"}[t.style]||",",a=(t.allowReserved===!0?r:r.map(h=>encodeURIComponent(h))).join(s);switch(t.style){case"simple":return a;case"label":return`.${a}`;case"matrix":return`;${e}=${a}`;default:return`${e}=${a}`}}const n={simple:",",label:".",matrix:";"}[t.style]||"&",i=[];for(const s of r)t.style==="simple"||t.style==="label"?i.push(t.allowReserved===!0?s:encodeURIComponent(s)):i.push(H(e,s,t));return t.style==="label"||t.style==="matrix"?`${n}${i.join(n)}`:i.join(n)}function re(e){return function(t){const n=[];if(t&&typeof t=="object")for(const i in t){const s=t[i];if(s!=null){if(Array.isArray(s)){if(s.length===0)continue;n.push(F(i,s,x(g({style:"form",explode:!0},e==null?void 0:e.array),{allowReserved:(e==null?void 0:e.allowReserved)||!1})));continue}if(typeof s=="object"){n.push(j(i,s,x(g({style:"deepObject",explode:!0},e==null?void 0:e.object),{allowReserved:(e==null?void 0:e.allowReserved)||!1})));continue}n.push(H(i,s,e))}}return n.join("&")}}function be(e,r){var n;let t=e;for(const i of(n=e.match(R))!=null?n:[]){let s=i.substring(1,i.length-1),a=!1,h="simple";if(s.endsWith("*")&&(a=!0,s=s.substring(0,s.length-1)),s.startsWith(".")?(h="label",s=s.substring(1)):s.startsWith(";")&&(h="matrix",s=s.substring(1)),!r||r[s]===void 0||r[s]===null)continue;const b=r[s];if(Array.isArray(b)){t=t.replace(i,F(s,b,{style:h,explode:a}));continue}if(typeof b=="object"){t=t.replace(i,j(s,b,{style:h,explode:a}));continue}if(h==="matrix"){t=t.replace(i,`;${H(s,b)}`);continue}t=t.replace(i,h==="label"?`.${encodeURIComponent(b)}`:encodeURIComponent(b))}return t}function we(e,r){var t,n;return e instanceof FormData?e:r&&(r.get instanceof Function?(t=r.get("Content-Type"))!=null?t:r.get("content-type"):(n=r["Content-Type"])!=null?n:r["content-type"])==="application/x-www-form-urlencoded"?new URLSearchParams(e).toString():JSON.stringify(e)}function pe(e,r){var i,s;let t=`${r.baseUrl}${e}`;(i=r.params)!=null&&i.path&&(t=be(t,r.params.path));let n=r.querySerializer((s=r.params.query)!=null?s:{});return n.startsWith("?")&&(n=n.substring(1)),n&&(t+=`?${n}`),t}function se(...e){const r=new Headers;for(const t of e){if(!t||typeof t!="object")continue;const n=t instanceof Headers?t.entries():Object.entries(t);for(const[i,s]of n)if(s===null)r.delete(i);else if(Array.isArray(s))for(const a of s)r.append(i,a);else s!==void 0&&r.set(i,s)}return r}function ne(e){return e.endsWith("/")?e.substring(0,e.length-1):e}const ie=(e,r,t)=>{try{const n=`${r}_${e}`,i=localStorage.getItem(n);if(!i)return null;const{data:s,timestamp:a}=JSON.parse(i);return t&&Date.now()-a>t?(localStorage.removeItem(n),null):s}catch(n){return null}},Y=(e,r,t)=>{try{const n=`${t}_${e}`,i={data:r,timestamp:Date.now()};localStorage.setItem(n,JSON.stringify(i))}catch(n){console.warn("Failed to cache dismissible item:",n)}},oe=(e,r)=>{try{const t=`${r}_${e}`;localStorage.removeItem(t)}catch(t){console.warn("Failed to remove cached dismissible item:",t)}},Z=o.createContext(null),ae=()=>{const e=o.useContext(Z);if(!e)throw new Error("useDismissibleContext must be used within a DismissibleProvider");return e},ge="dismissible",ce=(e,r={})=>{const{initialData:t,enableCache:n=!0,cachePrefix:i=ge,cacheExpiration:s}=r,a=ae(),{userId:h}=a,b=o.useMemo(()=>X({baseUrl:a.baseUrl,headers:{}}),[a.baseUrl]),m=o.useMemo(()=>`${h}-${e}`,[h,e]),C=o.useRef({enableCache:n,cachePrefix:i,cacheExpiration:s}),I=o.useRef(e),u=o.useRef(m),l=o.useRef(null),[A,$]=o.useState(!1),[L,D]=o.useState(),[S,U]=o.useState(()=>{if(t)return t;if(n){const f=ie(m,i,s);if(f)return f}}),T=o.useCallback(()=>q(null,null,function*(){var p;if(n){const y=ie(m,i,s);if(y!=null&&y.dismissedAt){U(y),$(!1);return}}(p=l.current)==null||p.abort();const f=new AbortController;l.current=f,$(!0),D(void 0);try{const y=yield a.getAuthHeaders(),{data:k,error:N}=yield b.GET("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:y,signal:f.signal});if(N||!k)throw new Error("Failed to fetch dismissible item");U(k.data),n&&Y(m,k.data,i)}catch(y){if(y instanceof Error&&y.name==="AbortError")return;D(y instanceof Error?y:new Error("Unknown error occurred"))}finally{$(!1)}}),[e,h,m,n,i,s,b,a]);o.useEffect(()=>{const f=I.current!==e,p=u.current!==m;f||p?(I.current=e,u.current=m,T()):t||T()},[e,m,t,T]),o.useEffect(()=>()=>{var f;(f=l.current)==null||f.abort()},[]),o.useEffect(()=>{const f=C.current;(f.enableCache!==n||f.cachePrefix!==i||f.cacheExpiration!==s)&&(f.cachePrefix!==i&&oe(m,f.cachePrefix),!n&&f.enableCache&&oe(m,f.cachePrefix),C.current={enableCache:n,cachePrefix:i,cacheExpiration:s},T())},[n,i,s,m,T]);const W=o.useCallback(()=>q(null,null,function*(){D(void 0);try{const f=yield a.getAuthHeaders(),{data:p,error:y}=yield b.DELETE("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:f});if(y||!p)throw new Error("Failed to dismiss item");U(p.data),n&&Y(m,p.data,i)}catch(f){throw D(f instanceof Error?f:new Error("Failed to dismiss item")),f}}),[e,h,m,n,i,b,a]),_=o.useCallback(()=>q(null,null,function*(){D(void 0);try{const f=yield a.getAuthHeaders(),{data:p,error:y}=yield b.POST("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:h,itemId:e}},headers:f});if(y||!p)throw new Error("Failed to restore item");U(p.data),n&&Y(m,p.data,i)}catch(f){throw D(f instanceof Error?f:new Error("Failed to restore item")),f}}),[e,h,m,n,i,b,a]);return{dismissedAt:S==null?void 0:S.dismissedAt,dismiss:W,restore:_,isLoading:A,error:L,item:S}},ve=()=>d.jsx("div",{className:"dismissible-loading","aria-live":"polite",children:"Loading..."}),Ee=()=>d.jsx("div",{className:"dismissible-error",role:"alert",children:"Unable to load content. Please try again later."}),Ce=({onDismiss:e,ariaLabel:r})=>d.jsx("button",{className:"dismissible-button",onClick:e,"aria-label":r,type:"button",children:"×"}),Re=({itemId:e,children:r,onDismiss:t,LoadingComponent:n=ve,ErrorComponent:i=Ee,DismissButtonComponent:s=Ce,enableCache:a,cachePrefix:h,cacheExpiration:b,ignoreErrors:m=!1})=>{const{dismissedAt:C,isLoading:I,error:u,dismiss:l}=ce(e,{enableCache:a,cachePrefix:h,cacheExpiration:b}),[A,$]=o.useState(!1),[L,D]=o.useState(e);e!==L&&(D(e),$(!1));const S=()=>q(null,null,function*(){$(!0);try{yield l(),t==null||t()}catch(U){$(!1)}});return I&&n?d.jsx(n,{itemId:e}):I&&!n?null:u&&i&&!m?d.jsx(i,{itemId:e,error:u}):C||A?null:d.jsxs("div",{className:"dismissible-container",children:[d.jsx("div",{className:"dismissible-content",children:r}),s?d.jsx(s,{onDismiss:S,ariaLabel:`Dismiss ${e}`}):null]})},le=e=>q(null,null,function*(){if(typeof e=="function")try{const r=e();return yield Promise.resolve(r)}catch(r){console.warn("Failed to resolve JWT from function:",r);return}return e}),ue=e=>q(null,null,function*(){const r=yield le(e);return r?{Authorization:`Bearer ${r}`}:{}}),Se=e=>{try{const r=new URL(e),t=r.hostname==="localhost"||r.hostname==="127.0.0.1"||r.hostname==="[::1]",n=r.protocol==="https:";return{isSecure:n||t,isLocalhost:t,isHttps:n}}catch(r){return{isSecure:!1,isLocalhost:!1,isHttps:!1}}},xe=({userId:e,jwt:r,baseUrl:t,children:n})=>{const{isSecure:i}=Se(t);i||console.warn(`[dismissible] Insecure baseUrl "${t}". Use https:// in production (or localhost for development). JWT tokens may be exposed over insecure connections.`);const s=o.useMemo(()=>({userId:e,jwt:r,baseUrl:t,getAuthHeaders:()=>q(null,null,function*(){return yield ue(r)})}),[e,r,t]);return d.jsx(Z.Provider,{value:s,children:n})};c.Dismissible=Re,c.DismissibleContext=Z,c.DismissibleProvider=xe,c.getAuthHeaders=ue,c.resolveJwt=le,c.useDismissibleContext=ae,c.useDismissibleItem=ce,Object.defineProperty(c,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(l,d){typeof exports=="object"&&typeof module!="undefined"?d(exports,require("react/jsx-runtime"),require("react")):typeof define=="function"&&define.amd?define(["exports","react/jsx-runtime","react"],d):(l=typeof globalThis!="undefined"?globalThis:l||self,d(l.DismissibleClient={},l.React.jsxRuntime,l.React))})(this,(function(l,d,a){"use strict";var $e=Object.defineProperty,Te=Object.defineProperties;var He=Object.getOwnPropertyDescriptors;var Y=Object.getOwnPropertySymbols;var me=Object.prototype.hasOwnProperty,ye=Object.prototype.propertyIsEnumerable;var te=(l,d,a)=>d in l?$e(l,d,{enumerable:!0,configurable:!0,writable:!0,value:a}):l[d]=a,v=(l,d)=>{for(var a in d||(d={}))me.call(d,a)&&te(l,a,d[a]);if(Y)for(var a of Y(d))ye.call(d,a)&&te(l,a,d[a]);return l},q=(l,d)=>Te(l,He(d));var re=(l,d)=>{var a={};for(var S in l)me.call(l,S)&&d.indexOf(S)<0&&(a[S]=l[S]);if(l!=null&&Y)for(var S of Y(l))d.indexOf(S)<0&&ye.call(l,S)&&(a[S]=l[S]);return a};var N=(l,d,a)=>te(l,typeof d!="symbol"?d+"":d,a);var I=(l,d,a)=>new Promise((S,k)=>{var G=$=>{try{M(a.next($))}catch(L){k(L)}},J=$=>{try{M(a.throw($))}catch(L){k(L)}},M=$=>$.done?S($.value):Promise.resolve($.value).then(G,J);M((a=a.apply(l,d)).next())});const S=(r,t,e)=>{try{const n=`${t}_${r}`,i=localStorage.getItem(n);if(!i)return null;const{data:s,timestamp:o}=JSON.parse(i);return e&&Date.now()-o>e?(localStorage.removeItem(n),null):s}catch(n){return null}},k=(r,t,e)=>{try{const n=`${e}_${r}`,i={data:t,timestamp:Date.now()};localStorage.setItem(n,JSON.stringify(i))}catch(n){console.warn("Failed to cache dismissible item:",n)}},G=(r,t)=>{try{const e=`${t}_${r}`;localStorage.removeItem(e)}catch(e){console.warn("Failed to remove cached dismissible item:",e)}},J=a.createContext(null),M=()=>{const r=a.useContext(J);if(!r)throw new Error("useDismissibleContext must be used within a DismissibleProvider");return r},$="dismissible",L=(r,t={})=>{const{initialData:e,enableCache:n=!0,cachePrefix:i=$,cacheExpiration:s}=t,{userId:o,client:c,baseUrl:m,getAuthHeaders:b,batchScheduler:p}=M(),y=`${o}-${r}`,u=a.useRef({enableCache:n,cachePrefix:i,cacheExpiration:s}),h=a.useRef(r),x=a.useRef(y),A=a.useRef(null),[B,H]=a.useState(!1),[D,j]=a.useState(),[T,U]=a.useState(()=>{if(e)return e;if(n){const f=S(y,i,s);if(f)return f}}),P=a.useCallback(()=>I(null,null,function*(){var C;if(n){const w=S(y,i,s);if(w!=null&&w.dismissedAt){p.primeCache(w),U(w),H(!1);return}}(C=A.current)==null||C.abort();const f=new AbortController;A.current=f,H(!0),j(void 0);try{const w=yield p.getItem(r);if(f.signal.aborted)return;U(w),n&&k(y,w,i)}catch(w){if(w instanceof Error&&w.name==="AbortError"||f.signal.aborted)return;j(w instanceof Error?w:new Error("Unknown error occurred"))}finally{f.signal.aborted||H(!1)}}),[r,y,n,i,s,p]);a.useEffect(()=>{const f=h.current!==r,C=x.current!==y;f||C?(h.current=r,x.current=y,P()):e||P()},[r,y,e,P]),a.useEffect(()=>()=>{var f;(f=A.current)==null||f.abort()},[]),a.useEffect(()=>{const f=u.current;(f.enableCache!==n||f.cachePrefix!==i||f.cacheExpiration!==s)&&(f.cachePrefix!==i&&G(y,f.cachePrefix),!n&&f.enableCache&&G(y,f.cachePrefix),u.current={enableCache:n,cachePrefix:i,cacheExpiration:s},P())},[n,i,s,y,P]);const Z=a.useCallback(()=>I(null,null,function*(){j(void 0);try{const f=yield b(),C=yield c.dismiss({userId:o,itemId:r,baseUrl:m,authHeaders:f});U(C),p.updateCache(C),n&&k(y,C,i)}catch(f){throw j(f instanceof Error?f:new Error("Failed to dismiss item")),f}}),[r,o,y,n,i,c,m,b,p]),W=a.useCallback(()=>I(null,null,function*(){j(void 0);try{const f=yield b(),C=yield c.restore({userId:o,itemId:r,baseUrl:m,authHeaders:f});U(C),p.updateCache(C),n&&k(y,C,i)}catch(f){throw j(f instanceof Error?f:new Error("Failed to restore item")),f}}),[r,o,y,n,i,c,m,b,p]);return{dismissedAt:T==null?void 0:T.dismissedAt,dismiss:Z,restore:W,isLoading:B,error:D,item:T}},be=()=>d.jsx("div",{className:"dismissible-loading","aria-live":"polite",children:"Loading..."}),pe=()=>d.jsx("div",{className:"dismissible-error",role:"alert",children:"Unable to load content. Please try again later."}),we=({onDismiss:r,ariaLabel:t})=>d.jsx("button",{className:"dismissible-button",onClick:r,"aria-label":t,type:"button",children:"×"}),ge=({itemId:r,children:t,onDismiss:e,LoadingComponent:n=be,ErrorComponent:i=pe,DismissButtonComponent:s=we,enableCache:o,cachePrefix:c,cacheExpiration:m,ignoreErrors:b=!1})=>{const{dismissedAt:p,isLoading:y,error:u,dismiss:h}=L(r,{enableCache:o,cachePrefix:c,cacheExpiration:m}),[x,A]=a.useState(!1),[B,H]=a.useState(r);r!==B&&(H(r),A(!1));const D=()=>I(null,null,function*(){A(!0);try{yield h(),e==null||e()}catch(j){A(!1)}});return y&&n?d.jsx(n,{itemId:r}):y&&!n?null:u&&i&&!b?d.jsx(i,{itemId:r,error:u}):p||x?null:d.jsxs("div",{className:"dismissible-container",children:[d.jsx("div",{className:"dismissible-content",children:t}),s?d.jsx(s,{onDismiss:D,ariaLabel:`Dismiss ${r}`}):null]})},se=r=>I(null,null,function*(){if(typeof r=="function")try{const t=r();return yield Promise.resolve(t)}catch(t){console.warn("Failed to resolve JWT from function:",t);return}return r}),ne=r=>I(null,null,function*(){const t=yield se(r);return t?{Authorization:`Bearer ${t}`}:{}}),ve=/\{[^{}]+\}/g,Ce=()=>{var r,t;return typeof process=="object"&&Number.parseInt((t=(r=process==null?void 0:process.versions)==null?void 0:r.node)==null?void 0:t.substring(0,2))>=18&&process.versions.undici};function Re(){return Math.random().toString(36).slice(2,11)}function Ee(r){let y=v({},r),{baseUrl:t="",Request:e=globalThis.Request,fetch:n=globalThis.fetch,querySerializer:i,bodySerializer:s,headers:o,requestInitExt:c=void 0}=y,m=re(y,["baseUrl","Request","fetch","querySerializer","bodySerializer","headers","requestInitExt"]);c=Ce()?c:void 0,t=le(t);const b=[];function p(u,h){return I(this,null,function*(){var he;const de=h||{},{baseUrl:x,fetch:A=n,Request:B=e,headers:H,params:D={},parseAs:j="json",querySerializer:T,bodySerializer:U=s!=null?s:qe,body:P,middleware:Z=[]}=de,W=re(de,["baseUrl","fetch","Request","headers","params","parseAs","querySerializer","bodySerializer","body","middleware"]);let f=t;x&&(f=(he=le(x))!=null?he:t);let C=typeof i=="function"?i:ae(i);T&&(C=typeof T=="function"?T:ae(v(v({},typeof i=="object"?i:{}),T)));const w=P===void 0?void 0:U(P,ce(o,H,D.header)),Ae=ce(w===void 0||w instanceof FormData?{}:{"Content-Type":"application/json"},o,H,D.header),O=[...b,...Z],De=q(v(v({redirect:"follow"},m),W),{body:w,headers:Ae});let Q,V,F=new B(Ie(u,{baseUrl:f,params:D,querySerializer:C}),De),g;for(const E in W)E in F||(F[E]=W[E]);if(O.length){Q=Re(),V=Object.freeze({baseUrl:f,fetch:A,parseAs:j,querySerializer:C,bodySerializer:U});for(const E of O)if(E&&typeof E=="object"&&typeof E.onRequest=="function"){const R=yield E.onRequest({request:F,schemaPath:u,params:D,options:V,id:Q});if(R)if(R instanceof B)F=R;else if(R instanceof Response){g=R;break}else throw new Error("onRequest: must return new Request() or Response() when modifying the request")}}if(!g){try{g=yield A(F,c)}catch(E){let R=E;if(O.length)for(let z=O.length-1;z>=0;z--){const X=O[z];if(X&&typeof X=="object"&&typeof X.onError=="function"){const _=yield X.onError({request:F,error:R,schemaPath:u,params:D,options:V,id:Q});if(_){if(_ instanceof Response){R=void 0,g=_;break}if(_ instanceof Error){R=_;continue}throw new Error("onError: must return new Response() or instance of Error")}}}if(R)throw R}if(O.length)for(let E=O.length-1;E>=0;E--){const R=O[E];if(R&&typeof R=="object"&&typeof R.onResponse=="function"){const z=yield R.onResponse({request:F,response:g,schemaPath:u,params:D,options:V,id:Q});if(z){if(!(z instanceof Response))throw new Error("onResponse: must return new Response() when modifying the response");g=z}}}}if(g.status===204||F.method==="HEAD"||g.headers.get("Content-Length")==="0")return g.ok?{data:void 0,response:g}:{error:void 0,response:g};if(g.ok)return j==="stream"?{data:g.body,response:g}:{data:yield g[j](),response:g};let ee=yield g.text();try{ee=JSON.parse(ee)}catch(E){}return{error:ee,response:g}})}return{request(u,h,x){return p(h,q(v({},x),{method:u.toUpperCase()}))},GET(u,h){return p(u,q(v({},h),{method:"GET"}))},PUT(u,h){return p(u,q(v({},h),{method:"PUT"}))},POST(u,h){return p(u,q(v({},h),{method:"POST"}))},DELETE(u,h){return p(u,q(v({},h),{method:"DELETE"}))},OPTIONS(u,h){return p(u,q(v({},h),{method:"OPTIONS"}))},HEAD(u,h){return p(u,q(v({},h),{method:"HEAD"}))},PATCH(u,h){return p(u,q(v({},h),{method:"PATCH"}))},TRACE(u,h){return p(u,q(v({},h),{method:"TRACE"}))},use(...u){for(const h of u)if(h){if(typeof h!="object"||!("onRequest"in h||"onResponse"in h||"onError"in h))throw new Error("Middleware must be an object with one of `onRequest()`, `onResponse() or `onError()`");b.push(h)}},eject(...u){for(const h of u){const x=b.indexOf(h);x!==-1&&b.splice(x,1)}}}}function K(r,t,e){if(t==null)return"";if(typeof t=="object")throw new Error("Deeply-nested arrays/objects aren’t supported. Provide your own `querySerializer()` to handle these.");return`${r}=${(e==null?void 0:e.allowReserved)===!0?t:encodeURIComponent(t)}`}function ie(r,t,e){if(!t||typeof t!="object")return"";const n=[],i={simple:",",label:".",matrix:";"}[e.style]||"&";if(e.style!=="deepObject"&&e.explode===!1){for(const c in t)n.push(c,e.allowReserved===!0?t[c]:encodeURIComponent(t[c]));const o=n.join(",");switch(e.style){case"form":return`${r}=${o}`;case"label":return`.${o}`;case"matrix":return`;${r}=${o}`;default:return o}}for(const o in t){const c=e.style==="deepObject"?`${r}[${o}]`:o;n.push(K(c,t[o],e))}const s=n.join(i);return e.style==="label"||e.style==="matrix"?`${i}${s}`:s}function oe(r,t,e){if(!Array.isArray(t))return"";if(e.explode===!1){const s={form:",",spaceDelimited:"%20",pipeDelimited:"|"}[e.style]||",",o=(e.allowReserved===!0?t:t.map(c=>encodeURIComponent(c))).join(s);switch(e.style){case"simple":return o;case"label":return`.${o}`;case"matrix":return`;${r}=${o}`;default:return`${r}=${o}`}}const n={simple:",",label:".",matrix:";"}[e.style]||"&",i=[];for(const s of t)e.style==="simple"||e.style==="label"?i.push(e.allowReserved===!0?s:encodeURIComponent(s)):i.push(K(r,s,e));return e.style==="label"||e.style==="matrix"?`${n}${i.join(n)}`:i.join(n)}function ae(r){return function(e){const n=[];if(e&&typeof e=="object")for(const i in e){const s=e[i];if(s!=null){if(Array.isArray(s)){if(s.length===0)continue;n.push(oe(i,s,q(v({style:"form",explode:!0},r==null?void 0:r.array),{allowReserved:(r==null?void 0:r.allowReserved)||!1})));continue}if(typeof s=="object"){n.push(ie(i,s,q(v({style:"deepObject",explode:!0},r==null?void 0:r.object),{allowReserved:(r==null?void 0:r.allowReserved)||!1})));continue}n.push(K(i,s,r))}}return n.join("&")}}function Se(r,t){var n;let e=r;for(const i of(n=r.match(ve))!=null?n:[]){let s=i.substring(1,i.length-1),o=!1,c="simple";if(s.endsWith("*")&&(o=!0,s=s.substring(0,s.length-1)),s.startsWith(".")?(c="label",s=s.substring(1)):s.startsWith(";")&&(c="matrix",s=s.substring(1)),!t||t[s]===void 0||t[s]===null)continue;const m=t[s];if(Array.isArray(m)){e=e.replace(i,oe(s,m,{style:c,explode:o}));continue}if(typeof m=="object"){e=e.replace(i,ie(s,m,{style:c,explode:o}));continue}if(c==="matrix"){e=e.replace(i,`;${K(s,m)}`);continue}e=e.replace(i,c==="label"?`.${encodeURIComponent(m)}`:encodeURIComponent(m))}return e}function qe(r,t){var e,n;return r instanceof FormData?r:t&&(t.get instanceof Function?(e=t.get("Content-Type"))!=null?e:t.get("content-type"):(n=t["Content-Type"])!=null?n:t["content-type"])==="application/x-www-form-urlencoded"?new URLSearchParams(r).toString():JSON.stringify(r)}function Ie(r,t){var i,s;let e=`${t.baseUrl}${r}`;(i=t.params)!=null&&i.path&&(e=Se(e,t.params.path));let n=t.querySerializer((s=t.params.query)!=null?s:{});return n.startsWith("?")&&(n=n.substring(1)),n&&(e+=`?${n}`),e}function ce(...r){const t=new Headers;for(const e of r){if(!e||typeof e!="object")continue;const n=e instanceof Headers?e.entries():Object.entries(e);for(const[i,s]of n)if(s===null)t.delete(i);else if(Array.isArray(s))for(const o of s)t.append(i,o);else s!==void 0&&t.set(i,s)}return t}function le(r){return r.endsWith("/")?r.substring(0,r.length-1):r}const ue=r=>{const t=Ee({baseUrl:r,headers:{}});return{getOrCreate:e=>I(null,null,function*(){const{userId:n,itemId:i,authHeaders:s,signal:o}=e,{data:c,error:m}=yield t.GET("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:n,itemId:i}},headers:s,signal:o});if(m||!c)throw new Error("Failed to fetch dismissible item");return c.data}),batchGetOrCreate:e=>I(null,null,function*(){const{userId:n,itemIds:i,authHeaders:s,signal:o}=e,{data:c,error:m}=yield t.POST("/v1/users/{userId}/items",{params:{path:{userId:n}},body:{items:i},headers:s,signal:o});if(m||!c)throw new Error("Failed to batch fetch dismissible items");return c.data}),dismiss:e=>I(null,null,function*(){const{userId:n,itemId:i,authHeaders:s}=e,{data:o,error:c}=yield t.DELETE("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:n,itemId:i}},headers:s});if(c||!o)throw new Error("Failed to dismiss item");return o.data}),restore:e=>I(null,null,function*(){const{userId:n,itemId:i,authHeaders:s}=e,{data:o,error:c}=yield t.POST("/v1/users/{userId}/items/{itemId}",{params:{path:{userId:n,itemId:i}},headers:s});if(c||!o)throw new Error("Failed to restore item");return o.data})}},xe=r=>{try{const t=new URL(r),e=t.hostname==="localhost"||t.hostname==="127.0.0.1"||t.hostname==="[::1]",n=t.protocol==="https:";return{isSecure:n||e,isLocalhost:e,isHttps:n}}catch(t){return{isSecure:!1,isLocalhost:!1,isHttps:!1}}};class fe{constructor(t){N(this,"config");N(this,"pendingRequests",[]);N(this,"isScheduled",!1);N(this,"cache",new Map);N(this,"inFlightRequests",new Map);var e;this.config=q(v({},t),{maxBatchSize:(e=t.maxBatchSize)!=null?e:50})}getItem(t){const e=this.cache.get(t);if(e)return Promise.resolve(e);const n=this.inFlightRequests.get(t);if(n)return n;let i,s;const o=new Promise((c,m)=>{i=c,s=m});return this.pendingRequests.push({itemId:t,resolve:i,reject:s}),this.inFlightRequests.set(t,o),o.catch(()=>{}).finally(()=>{this.inFlightRequests.delete(t)}),this.isScheduled||(this.isScheduled=!0,queueMicrotask(()=>this.executeBatch())),o}primeCache(t){this.cache.set(t.itemId,t)}updateCache(t){this.cache.set(t.itemId,t)}clearCache(){this.cache.clear()}executeBatch(){return I(this,null,function*(){this.isScheduled=!1;const t=this.pendingRequests;if(this.pendingRequests=[],t.length===0)return;const e=new Map;for(const s of t){const o=e.get(s.itemId);o?o.push(s):e.set(s.itemId,[s])}const n=Array.from(e.keys()),i=[];for(let s=0;s<n.length;s+=this.config.maxBatchSize)i.push(n.slice(s,s+this.config.maxBatchSize));try{const s=yield this.config.getAuthHeaders(),c=(yield Promise.all(i.map(b=>this.config.client.batchGetOrCreate({userId:this.config.userId,itemIds:b,baseUrl:this.config.baseUrl,authHeaders:s})))).flat(),m=new Map;for(const b of c)m.set(b.itemId,b),this.cache.set(b.itemId,b);for(const[b,p]of e){const y=m.get(b);if(y)for(const u of p)u.resolve(y);else{const u=new Error(`Item ${b} not found in batch response`);for(const h of p)h.reject(u)}}}catch(s){const o=s instanceof Error?s:new Error("Batch request failed");for(const c of t)c.reject(o)}})}}const je=({userId:r,jwt:t,baseUrl:e,client:n,children:i})=>{a.useEffect(()=>{const{isSecure:b}=xe(e);b||console.warn(`[dismissible] Insecure baseUrl "${e}". Use https:// in production (or localhost for development). JWT tokens may be exposed over insecure connections.`)},[e]);const s=a.useMemo(()=>n!=null?n:ue(e),[n,e]),o=a.useMemo(()=>()=>I(null,null,function*(){return yield ne(t)}),[t]),c=a.useMemo(()=>new fe({userId:r,baseUrl:e,client:s,getAuthHeaders:o}),[r,e,s,o]),m=a.useMemo(()=>({userId:r,jwt:t,baseUrl:e,getAuthHeaders:o,client:s,batchScheduler:c}),[r,t,e,o,s,c]);return d.jsx(J.Provider,{value:m,children:i})};l.BatchScheduler=fe,l.Dismissible=ge,l.DismissibleContext=J,l.DismissibleProvider=je,l.createDefaultClient=ue,l.getAuthHeaders=ne,l.resolveJwt=se,l.useDismissibleContext=M,l.useDismissibleItem=L,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
type DismissibleItem = components["schemas"]["DismissibleItemResponseDto"];
|
|
1
|
+
import { DismissibleItem } from '../types/dismissible.types';
|
|
3
2
|
export type IDismissibleItem = DismissibleItem;
|
|
4
3
|
export interface UseDismissibleItemOptions {
|
|
5
4
|
/** Initial data for the dismissible item */
|
|
@@ -32,4 +31,3 @@ export interface UseDismissibleItemResponse {
|
|
|
32
31
|
* @returns Object with dismissedAt, dismiss and restore functions
|
|
33
32
|
*/
|
|
34
33
|
export declare const useDismissibleItem: (itemId: string, options?: UseDismissibleItemOptions) => UseDismissibleItemResponse;
|
|
35
|
-
export {};
|
package/dist/root.d.ts
CHANGED
|
@@ -5,6 +5,100 @@
|
|
|
5
5
|
* JWT token can be either a static string, a function that returns a string, or an async function that returns a string
|
|
6
6
|
*/
|
|
7
7
|
export type JwtToken = string | (() => string) | (() => Promise<string>);
|
|
8
|
+
/**
|
|
9
|
+
* Authentication headers type
|
|
10
|
+
*/
|
|
11
|
+
export interface AuthHeaders {
|
|
12
|
+
Authorization?: string;
|
|
13
|
+
[key: string]: string | undefined;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Dismissible item data returned from the API
|
|
17
|
+
*/
|
|
18
|
+
export interface DismissibleItem {
|
|
19
|
+
/** Unique identifier for the item */
|
|
20
|
+
itemId: string;
|
|
21
|
+
/** User identifier who created the item */
|
|
22
|
+
userId: string;
|
|
23
|
+
/** When the item was created (ISO 8601) */
|
|
24
|
+
createdAt: string;
|
|
25
|
+
/** When the item was dismissed (ISO 8601), undefined if not dismissed */
|
|
26
|
+
dismissedAt?: string;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Base parameters shared by all client methods
|
|
30
|
+
*/
|
|
31
|
+
export interface BaseDismissibleClientParams {
|
|
32
|
+
/** User ID for the current user */
|
|
33
|
+
userId: string;
|
|
34
|
+
/** The dismissible item ID */
|
|
35
|
+
itemId: string;
|
|
36
|
+
/** Base URL for API requests */
|
|
37
|
+
baseUrl: string;
|
|
38
|
+
/** Authentication headers */
|
|
39
|
+
authHeaders: AuthHeaders;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Parameters for the getOrCreate client method
|
|
43
|
+
*/
|
|
44
|
+
export interface GetOrCreateClientParams extends BaseDismissibleClientParams {
|
|
45
|
+
/** AbortSignal for request cancellation */
|
|
46
|
+
signal?: AbortSignal;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Parameters for the dismiss client method
|
|
50
|
+
*/
|
|
51
|
+
export type DismissClientParams = BaseDismissibleClientParams;
|
|
52
|
+
/**
|
|
53
|
+
* Parameters for the restore client method
|
|
54
|
+
*/
|
|
55
|
+
export type RestoreClientParams = BaseDismissibleClientParams;
|
|
56
|
+
/**
|
|
57
|
+
* Parameters for the batchGetOrCreate client method
|
|
58
|
+
*/
|
|
59
|
+
export interface BatchGetOrCreateClientParams {
|
|
60
|
+
/** User ID for the current user */
|
|
61
|
+
userId: string;
|
|
62
|
+
/** Array of dismissible item IDs to fetch (max 50) */
|
|
63
|
+
itemIds: string[];
|
|
64
|
+
/** Base URL for API requests */
|
|
65
|
+
baseUrl: string;
|
|
66
|
+
/** Authentication headers */
|
|
67
|
+
authHeaders: AuthHeaders;
|
|
68
|
+
/** AbortSignal for request cancellation */
|
|
69
|
+
signal?: AbortSignal;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Interface for custom HTTP clients
|
|
73
|
+
*
|
|
74
|
+
* Users can implement this interface to provide their own HTTP client
|
|
75
|
+
* with custom headers, tracking, interceptors, etc.
|
|
76
|
+
*
|
|
77
|
+
* @example
|
|
78
|
+
* ```typescript
|
|
79
|
+
* const myClient: DismissibleClient = {
|
|
80
|
+
* getOrCreate: async ({ userId, itemId, baseUrl, authHeaders, signal }) => {
|
|
81
|
+
* const res = await axios.get(`${baseUrl}/v1/users/${userId}/items/${itemId}`, {
|
|
82
|
+
* headers: { ...authHeaders, 'X-Correlation-ID': uuid() },
|
|
83
|
+
* signal
|
|
84
|
+
* });
|
|
85
|
+
* return res.data.data;
|
|
86
|
+
* },
|
|
87
|
+
* dismiss: async ({ userId, itemId, baseUrl, authHeaders }) => { ... },
|
|
88
|
+
* restore: async ({ userId, itemId, baseUrl, authHeaders }) => { ... },
|
|
89
|
+
* };
|
|
90
|
+
* ```
|
|
91
|
+
*/
|
|
92
|
+
export interface DismissibleClient {
|
|
93
|
+
/** Get or create a dismissible item */
|
|
94
|
+
getOrCreate: (params: GetOrCreateClientParams) => Promise<DismissibleItem>;
|
|
95
|
+
/** Batch get or create multiple dismissible items (max 50) */
|
|
96
|
+
batchGetOrCreate: (params: BatchGetOrCreateClientParams) => Promise<DismissibleItem[]>;
|
|
97
|
+
/** Dismiss an item */
|
|
98
|
+
dismiss: (params: DismissClientParams) => Promise<DismissibleItem>;
|
|
99
|
+
/** Restore a dismissed item */
|
|
100
|
+
restore: (params: RestoreClientParams) => Promise<DismissibleItem>;
|
|
101
|
+
}
|
|
8
102
|
/**
|
|
9
103
|
* Configuration options for the DismissibleProvider
|
|
10
104
|
*/
|
|
@@ -15,9 +109,24 @@ export interface DismissibleProviderProps {
|
|
|
15
109
|
jwt?: JwtToken;
|
|
16
110
|
/** Base URL for API requests */
|
|
17
111
|
baseUrl: string;
|
|
112
|
+
/** Custom HTTP client implementation (optional - uses default if not provided) */
|
|
113
|
+
client?: DismissibleClient;
|
|
18
114
|
/** Child components */
|
|
19
115
|
children: React.ReactNode;
|
|
20
116
|
}
|
|
117
|
+
/**
|
|
118
|
+
* Batch scheduler interface for request coalescing
|
|
119
|
+
*/
|
|
120
|
+
export interface IBatchScheduler {
|
|
121
|
+
/** Request a dismissible item (batched with other requests in same tick) */
|
|
122
|
+
getItem: (itemId: string) => Promise<DismissibleItem>;
|
|
123
|
+
/** Pre-populate cache with an item */
|
|
124
|
+
primeCache: (item: DismissibleItem) => void;
|
|
125
|
+
/** Update an item in the cache */
|
|
126
|
+
updateCache: (item: DismissibleItem) => void;
|
|
127
|
+
/** Clear the in-memory cache */
|
|
128
|
+
clearCache: () => void;
|
|
129
|
+
}
|
|
21
130
|
/**
|
|
22
131
|
* Context value provided by DismissibleProvider
|
|
23
132
|
*/
|
|
@@ -30,11 +139,8 @@ export interface DismissibleContextValue {
|
|
|
30
139
|
baseUrl: string;
|
|
31
140
|
/** Helper function to get authentication headers */
|
|
32
141
|
getAuthHeaders: () => Promise<AuthHeaders>;
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
export interface AuthHeaders {
|
|
38
|
-
Authorization?: string;
|
|
39
|
-
[key: string]: string | undefined;
|
|
142
|
+
/** The HTTP client to use for API requests */
|
|
143
|
+
client: DismissibleClient;
|
|
144
|
+
/** Batch scheduler for coalescing getOrCreate requests */
|
|
145
|
+
batchScheduler: IBatchScheduler;
|
|
40
146
|
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { DismissibleClient, DismissibleItem, AuthHeaders } from '../types/dismissible.types';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration for the BatchScheduler
|
|
4
|
+
*/
|
|
5
|
+
export interface BatchSchedulerConfig {
|
|
6
|
+
/** User ID for API requests */
|
|
7
|
+
userId: string;
|
|
8
|
+
/** Base URL for API requests */
|
|
9
|
+
baseUrl: string;
|
|
10
|
+
/** The HTTP client to use */
|
|
11
|
+
client: DismissibleClient;
|
|
12
|
+
/** Function to get auth headers */
|
|
13
|
+
getAuthHeaders: () => Promise<AuthHeaders>;
|
|
14
|
+
/** Maximum items per batch (API limit is 50) */
|
|
15
|
+
maxBatchSize?: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* BatchScheduler implements DataLoader-style request batching.
|
|
19
|
+
*
|
|
20
|
+
* When multiple components request items in the same JavaScript tick,
|
|
21
|
+
* the scheduler coalesces them into a single batch API call using
|
|
22
|
+
* queueMicrotask to defer execution until after synchronous code completes.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const scheduler = new BatchScheduler({
|
|
27
|
+
* userId: "user-123",
|
|
28
|
+
* baseUrl: "https://api.example.com",
|
|
29
|
+
* client: dismissibleClient,
|
|
30
|
+
* getAuthHeaders: async () => ({ Authorization: "Bearer ..." }),
|
|
31
|
+
* });
|
|
32
|
+
*
|
|
33
|
+
* // These three calls in the same tick will be batched into one API call
|
|
34
|
+
* const [item1, item2, item3] = await Promise.all([
|
|
35
|
+
* scheduler.getItem("item-1"),
|
|
36
|
+
* scheduler.getItem("item-2"),
|
|
37
|
+
* scheduler.getItem("item-3"),
|
|
38
|
+
* ]);
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare class BatchScheduler {
|
|
42
|
+
private config;
|
|
43
|
+
private pendingRequests;
|
|
44
|
+
private isScheduled;
|
|
45
|
+
private cache;
|
|
46
|
+
private inFlightRequests;
|
|
47
|
+
constructor(config: BatchSchedulerConfig);
|
|
48
|
+
/**
|
|
49
|
+
* Request a dismissible item. If called multiple times in the same
|
|
50
|
+
* JavaScript tick, requests will be batched into a single API call.
|
|
51
|
+
*
|
|
52
|
+
* @param itemId - The item ID to fetch
|
|
53
|
+
* @returns Promise resolving to the DismissibleItem
|
|
54
|
+
*/
|
|
55
|
+
getItem(itemId: string): Promise<DismissibleItem>;
|
|
56
|
+
/**
|
|
57
|
+
* Pre-populate the cache with items (e.g., from localStorage)
|
|
58
|
+
*/
|
|
59
|
+
primeCache(item: DismissibleItem): void;
|
|
60
|
+
/**
|
|
61
|
+
* Update an item in the cache (e.g., after dismiss/restore)
|
|
62
|
+
*/
|
|
63
|
+
updateCache(item: DismissibleItem): void;
|
|
64
|
+
/**
|
|
65
|
+
* Clear the in-memory cache
|
|
66
|
+
*/
|
|
67
|
+
clearCache(): void;
|
|
68
|
+
/**
|
|
69
|
+
* Execute the batched requests
|
|
70
|
+
*/
|
|
71
|
+
private executeBatch;
|
|
72
|
+
}
|