@djvlc/runtime-host-react 1.1.6 → 1.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -1 +1 @@
1
- var e=require("react"),r=require("@djvlc/runtime-core"),n=require("react/jsx-runtime"),t={initTime:0,loadTime:0,renderTime:0,totalTime:0,initTimestamp:null,readyTimestamp:null};function o(n){const[o,a]=e.useState(null),[i,l]=e.useState(!0),[s,c]=e.useState("idle"),[u,d]=e.useState(null),[p,m]=e.useState(null),[h,f]=e.useState(null),[w,y]=e.useState(t),v=e.useRef(null),g=e.useRef({startTime:0,initStartTime:0,loadStartTime:0,renderStartTime:0}),x=e.useMemo(()=>"ready"===s,[s]),T=e.useMemo(()=>"error"===s||null!==p,[s,p]),b=e.useCallback(async()=>{const e=n.containerRef?.current;if(!e)throw new Error("Container element not found");g.current.startTime=performance.now(),g.current.initStartTime=g.current.startTime,n.enableMetrics&&y(e=>({...e,initTimestamp:Date.now()}));const t=r.createRuntime({...n,container:e,onError:e=>{m(e),n.onError?.(e)},onEvent:e=>{n.onEvent?.(e)}});v.current=t,a(t),t.onStateChange(e=>{c(e.phase),l("ready"!==e.phase&&"error"!==e.phase),e.page&&d(e.page),e.error&&m(e.error)}),await t.init(),f(t.getHostApi()),n.enableMetrics&&y(e=>({...e,initTime:performance.now()-g.current.initStartTime}))},[n]),E=e.useCallback(async()=>{if(!v.current)throw new Error("Runtime not initialized");g.current.loadStartTime=performance.now();const e=await v.current.load();return d(e),f(v.current.getHostApi()),n.enableMetrics&&y(e=>({...e,loadTime:performance.now()-g.current.loadStartTime})),e},[n.enableMetrics]),I=e.useCallback(async()=>{if(!v.current)throw new Error("Runtime not initialized");if(g.current.renderStartTime=performance.now(),await v.current.render(),l(!1),n.enableMetrics){const e=performance.now();y(r=>({...r,renderTime:e-g.current.renderStartTime,totalTime:e-g.current.startTime,readyTimestamp:Date.now()}))}},[n.enableMetrics]),A=e.useCallback(()=>{v.current?.destroy(),v.current=null,a(null),f(null),d(null),m(null),c("idle"),l(!0),y(t)},[]),N=e.useCallback(async()=>{if(!v.current)throw new Error("Runtime not initialized");m(null),l(!0),await E(),await I()},[E,I]),j=e.useCallback((e,r)=>{v.current?.setVariable(e,r)},[]),R=e.useCallback(e=>{v.current&&Object.entries(e).forEach(([e,r])=>{v.current?.setVariable(e,r)})},[]),C=e.useCallback(e=>v.current?.getState().variables[e],[]),P=e.useCallback(async e=>{await(v.current?.refreshData(e))},[]),S=e.useCallback(async(e,r)=>{const n=h;if(!n)throw new Error("HostAPI not available");const t=await n.executeAction(e,r||{});if(t.success)return t.data;throw new Error(t.errorMessage||"Action failed")},[h]);return{runtime:o,loading:i,phase:s,page:u,error:p,hostApi:h,isReady:x,hasError:T,metrics:w,init:b,load:E,render:I,destroy:A,reload:N,setVariable:j,setVariables:R,getVariable:C,refreshData:P,executeAction:S}}var a={phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1},i=e.createContext({runtime:null,state:a,hostApi:null});function l({runtime:e,state:r,hostApi:t,children:o}){const a={runtime:e,state:r,hostApi:t};return n.jsx(i.Provider,{value:a,children:o})}function s(){return e.useContext(i)}var c={phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1};function u(r,n){const t=s(),[o,a]=e.useState(!1),[i,l]=e.useState(),[c,u]=e.useState(null),[d,p]=e.useState(0),m=e.useRef(!0);e.useEffect(()=>(m.current=!0,()=>{m.current=!1}),[]);const h=e.useCallback(()=>{l(void 0),u(null),p(0)},[]),f=e.useCallback(async(e,o)=>{const a=t.hostApi;if(!a)throw new Error("HostAPI not available");try{const n=await a.executeAction(r,e);if(n.success)return n.data;throw new Error(n.errorMessage||"Action failed")}catch(r){if(o>0)return await new Promise(e=>setTimeout(e,n?.retryDelay||1e3)),f(e,o-1);throw r}},[t.hostApi,r,n?.retryDelay]);return{execute:e.useCallback(async e=>{if(m.current){a(!0),u(null),p(e=>e+1);try{const r=await f(e,n?.retryCount||0);return m.current&&(l(r),n?.onSuccess?.(r)),r}catch(e){const r=e;throw m.current&&(u(r),n?.onError?.(r)),e}finally{m.current&&a(!1)}}},[f,n]),loading:o,result:i,error:c,reset:h,executionCount:d}}exports.DJVProvider=function({runtime:r,hostApi:t,children:o,className:a,debug:i=!1,onStateChange:s,onPhaseChange:u,onError:d}){const[p,m]=e.useState(r?.getState()||c),h=e.useRef(p.phase);return e.useEffect(()=>{if(!r)return void m(c);m(r.getState());const e=r.onStateChange(e=>{m(e),s?.(e),e.phase!==h.current&&(u?.(e.phase),h.current=e.phase),e.error&&d?.(e.error)});return()=>{e()}},[r,s,u,d,i]),e.useEffect(()=>{},[t]),n.jsx(l,{runtime:r,state:p,hostApi:t,children:n.jsx("div",{className:["djvlc-provider",a].filter(Boolean).join(" "),"data-phase":p.phase,children:o})})},exports.DJVRenderer=function({pageId:r,apiBaseUrl:t,cdnBaseUrl:a,channel:i="prod",userId:s,deviceId:c,authToken:u,previewToken:d,debug:p=!1,enableSRI:m=!0,onLoad:h,onError:f,onReady:w,onPhaseChange:y,loadingComponent:v,errorComponent:g,emptyComponent:x,children:T,className:b,style:E,retryCount:I=3,retryDelay:A=1e3,timeout:N=3e4}){const j=e.useRef(null),R=e.useRef(!0),C=e.useRef(0),[P,S]=e.useState({phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1}),k={pageId:r,apiBaseUrl:t,cdnBaseUrl:a,channel:i,userId:s,deviceId:c,authToken:u,previewToken:d,debug:p,enableSRI:m,containerRef:j,onError:e=>{f?.(e)}},{runtime:q,loading:B,phase:D,page:M,error:U,hostApi:V,init:z,load:L,render:H,destroy:O}=o(k);e.useEffect(()=>{y?.(D)},[D,y]);const J=e.useCallback(async()=>{if(!j.current||!R.current)return;const e=new Promise((e,r)=>{setTimeout(()=>r(new Error("加载超时")),N)});try{await Promise.race([(async()=>{await z();const e=await L();R.current&&h?.(e),await H(),R.current&&(w?.(),C.current=0,q&&q.onStateChange(e=>{R.current&&S(e)}))})(),e])}catch(e){if(!R.current)return;C.current<I&&(C.current++,setTimeout(()=>{R.current&&J()},A))}},[z,L,H,q,h,w,N,I,A,p]),F=e.useCallback(()=>{C.current=0,J()},[J]);return e.useEffect(()=>(R.current=!0,J(),()=>{R.current=!1,O()}),[r]),n.jsx(l,{runtime:q,state:P,hostApi:V,children:n.jsxs("div",{ref:j,className:`djvlc-renderer ${b||""}`,style:E,"data-phase":D,"data-page-id":r,children:[B?v||n.jsxs("div",{className:"djvlc-loading",children:[n.jsx("div",{className:"djvlc-loading-spinner"}),n.jsx("span",{children:"加载中..."}),C.current>0&&n.jsxs("span",{className:"djvlc-loading-retry",children:["重试 ",C.current,"/",I]})]}):null,U?"function"==typeof g?g(U,F):g||n.jsxs("div",{className:"djvlc-error",children:[n.jsx("div",{className:"djvlc-error-icon",children:"⚠️"}),n.jsxs("span",{className:"djvlc-error-message",children:["加载失败:",U.message]}),n.jsx("button",{className:"djvlc-error-retry-btn",onClick:F,type:"button",children:"重试"})]}):null,B||U||M||"ready"!==D||M?null:x||n.jsx("div",{className:"djvlc-empty",children:n.jsx("span",{children:"暂无内容"})}),T]})})},exports.RuntimeContext=i,exports.RuntimeProvider=l,exports.useAction=u,exports.useAsync=function(r,n=[],t){const[o,a]=e.useState(),[i,l]=e.useState(!1),[s,c]=e.useState(null),u=e.useRef(!0);e.useEffect(()=>(u.current=!0,()=>{u.current=!1}),[]);const d=e.useCallback(async()=>{if(u.current){l(!0),c(null);try{const e=await r();return u.current&&(a(e),t?.onSuccess?.(e)),e}catch(e){const r=e;return void(u.current&&(c(r),t?.onError?.(r)))}finally{u.current&&l(!1)}}},[r,...n]);return e.useEffect(()=>{!1!==t?.immediate&&d()},[]),{data:o,loading:i,error:s,execute:d}},exports.useComponentState=function(r){const n=s().state.components.get(r);return e.useMemo(()=>({isLoaded:"loaded"===n?.status,isLoading:"loading"===n?.status,hasError:"failed"===n?.status,loadTime:n?.loadTime,info:n}),[n])},exports.useDJVRuntime=function(){const r=s(),n=e.useMemo(()=>{const e=r.state.phase;return"ready"!==e&&"error"!==e},[r.state.phase]),t="ready"===r.state.phase,o="error"===r.state.phase||null!==r.state.error,a=e.useCallback(async()=>{if(!r.runtime)throw new Error("Runtime not available");await r.runtime.load(),await r.runtime.render()},[r.runtime]);return{runtime:r.runtime,state:r.state,loading:n,phase:r.state.phase,error:r.state.error,page:r.state.page,isReady:t,hasError:o,reload:a}},exports.useData=function(r,n,t){const o=s(),[a,i]=e.useState(),[l,c]=e.useState(!1),[u,d]=e.useState(null),[p,m]=e.useState(!1),h=e.useRef(!0),f=e.useRef(n);e.useEffect(()=>(h.current=!0,()=>{h.current=!1}),[]);const w=e.useCallback(async e=>{const n=o.hostApi;if(n&&h.current){c(!0),d(null);try{const o=await n.requestData(r,e||f.current);if(!h.current)return;i(o),m(!0),t?.onSuccess?.(o)}catch(e){if(h.current){const r=e;d(r),t?.onError?.(r)}}finally{h.current&&c(!1)}}},[o.hostApi,r,t]);return e.useEffect(()=>{const e=f.current;f.current=n,!1!==t?.refreshOnParamsChange&&o.hostApi&&p&&JSON.stringify(e)!==JSON.stringify(n)&&w()},[n,o.hostApi,t?.refreshOnParamsChange,p,w]),e.useEffect(()=>{!1!==t?.immediate&&o.hostApi&&w()},[o.hostApi]),e.useEffect(()=>{if(!t?.refreshInterval||t.refreshInterval<=0)return;const e=setInterval(()=>{w()},t.refreshInterval);return()=>clearInterval(e)},[t?.refreshInterval,w]),{data:a,loading:l,error:u,refetch:w,isFetched:p}},exports.useDebouncedAction=function(r,n=300){const{execute:t,loading:o,result:a,error:i}=u(r),l=e.useRef(null),s=e.useRef(!0);return e.useEffect(()=>(s.current=!0,()=>{s.current=!1,l.current&&clearTimeout(l.current)}),[]),{execute:e.useCallback(e=>{l.current&&clearTimeout(l.current),l.current=setTimeout(()=>{s.current&&t(e).catch(()=>{})},n)},[t,n]),loading:o,result:a,error:i,cancel:e.useCallback(()=>{l.current&&(clearTimeout(l.current),l.current=null)},[])}},exports.useHostApi=function(){const e=s();if(!e.hostApi)throw new Error("HostAPI not available. Make sure runtime is initialized.");return e.hostApi},exports.useLifecycle=function(r){const n=s(),[t,o]=e.useState(!1),[a,i]=e.useState(!1),l=e.useRef(n.state.phase),c=e.useRef(!0);return e.useEffect(()=>(c.current=!0,()=>{c.current=!1}),[]),e.useEffect(()=>{const e=n.state.phase;e!==l.current&&(r?.onPhaseChange?.(e),l.current=e),t||"idle"===e||(o(!0),Promise.resolve(r?.onMounted?.()).catch(e=>{r?.onError?.(e)})),a||"ready"!==e||(i(!0),Promise.resolve(r?.onReady?.()).catch(e=>{r?.onError?.(e)})),"error"===e&&n.state.error&&r?.onError?.(n.state.error)},[n.state.phase,n.state.error,t,a,r]),{phase:n.state.phase,hasMounted:t,hasReady:a}},exports.usePageInfo=function(){const r=s().state.page;return e.useMemo(()=>({pageId:r?.pageId,pageVersionId:r?.pageVersionId,schemaVersion:r?.pageJson?.schemaVersion,title:r?.title,config:r?.config,isLoaded:null!==r}),[r])},exports.usePrevious=function(r){const n=e.useRef();return e.useEffect(()=>{n.current=r},[r]),n.current},exports.useQuery=function(r,n){const t=s(),[o,a]=e.useState(!1),[i,l]=e.useState(null),[c,u]=e.useState(null),d=e.useRef(!0),p=t.state.queries[r],m=e.useCallback(async()=>{if(d.current){a(!0),l(null);try{await(t.runtime?.refreshData(r)),d.current&&u(Date.now())}catch(e){d.current&&l(e)}finally{d.current&&a(!1)}}},[t.runtime,r]);return e.useEffect(()=>(d.current=!0,n?.refreshOnMount&&t.runtime&&m(),()=>{d.current=!1}),[]),e.useEffect(()=>{if(!n?.refreshInterval||n.refreshInterval<=0)return;const e=setInterval(()=>{m()},n.refreshInterval);return()=>clearInterval(e)},[n?.refreshInterval,m]),e.useEffect(()=>{if(!n?.refreshOnFocus)return;const e=()=>{m()};return window.addEventListener("focus",e),()=>window.removeEventListener("focus",e)},[n?.refreshOnFocus,m]),{data:p,loading:o,error:i,refetch:m,lastUpdated:c}},exports.useReactRuntime=o,exports.useRuntimeContext=s,exports.useRuntimeEvent=function(r,n){const t=s();e.useEffect(()=>{const e=t.runtime?.on(r,e=>{n(e.data)});return()=>{e?.()}},[t.runtime,r,n])},exports.useRuntimeState=function(e){return s().state.variables[e]},exports.useRuntimeStateWritable=function(r,n){const t=s();return[t.state.variables[r]??n,e.useCallback(e=>{t.runtime?.setVariable(r,e)},[t.runtime,r])]},exports.useWhen=function(r,n,t){const[o,a]=e.useState(!1),{once:i=!0}=t||{};return e.useEffect(()=>{!r||i&&o||(a(!0),n())},[r,n,i,o]),{executed:o}};
1
+ "use strict";var e=require("react"),r=require("@djvlc/runtime-core"),n=require("react/jsx-runtime"),t={initTime:0,loadTime:0,renderTime:0,totalTime:0,initTimestamp:null,readyTimestamp:null};function a(n){const[a,o]=e.useState(null),[i,s]=e.useState(!0),[l,c]=e.useState("idle"),[u,d]=e.useState(null),[p,m]=e.useState(null),[h,f]=e.useState(null),[w,y]=e.useState(t),v=e.useRef(null),g=e.useRef({startTime:0,initStartTime:0,loadStartTime:0,renderStartTime:0}),x=e.useMemo(()=>"ready"===l,[l]),T=e.useMemo(()=>"error"===l||null!==p,[l,p]),b=e.useCallback(async()=>{const e=n.containerRef?.current;if(!e)throw new Error("Container element not found");g.current.startTime=performance.now(),g.current.initStartTime=g.current.startTime,n.enableMetrics&&y(e=>({...e,initTimestamp:Date.now()}));const t=r.createRuntime({...n,container:e,onError:e=>{m(e),n.onError?.(e)},onEvent:e=>{n.onEvent?.(e)}});v.current=t,o(t),t.onStateChange(e=>{c(e.phase),s("ready"!==e.phase&&"error"!==e.phase),e.page&&d(e.page),e.error&&m(e.error)}),await t.init(),f(t.getHostApi()),n.enableMetrics&&y(e=>({...e,initTime:performance.now()-g.current.initStartTime}))},[n]),E=e.useCallback(async()=>{if(!v.current)throw new Error("Runtime not initialized");g.current.loadStartTime=performance.now();const e=await v.current.load();return d(e),f(v.current.getHostApi()),n.enableMetrics&&y(e=>({...e,loadTime:performance.now()-g.current.loadStartTime})),e},[n.enableMetrics]),A=e.useCallback(async()=>{if(!v.current)throw new Error("Runtime not initialized");if(g.current.renderStartTime=performance.now(),await v.current.render(),s(!1),n.enableMetrics){const e=performance.now();y(r=>({...r,renderTime:e-g.current.renderStartTime,totalTime:e-g.current.startTime,readyTimestamp:Date.now()}))}},[n.enableMetrics]),I=e.useCallback(()=>{v.current?.destroy(),v.current=null,o(null),f(null),d(null),m(null),c("idle"),s(!0),y(t)},[]),N=e.useCallback(async()=>{if(!v.current)throw new Error("Runtime not initialized");m(null),s(!0),await E(),await A()},[E,A]),j=e.useCallback((e,r)=>{v.current?.setVariable(e,r)},[]),R=e.useCallback(e=>{v.current&&Object.entries(e).forEach(([e,r])=>{v.current?.setVariable(e,r)})},[]),C=e.useCallback(e=>v.current?.getState().variables[e],[]),P=e.useCallback(async e=>{await(v.current?.refreshData(e))},[]),S=e.useCallback(async(e,r)=>{const n=h;if(!n)throw new Error("HostAPI not available");const t=await n.executeAction(e,r||{});if(t.success)return t.data;throw new Error(t.errorMessage||"Action failed")},[h]);return{runtime:a,loading:i,phase:l,page:u,error:p,hostApi:h,isReady:x,hasError:T,metrics:w,init:b,load:E,render:A,destroy:I,reload:N,setVariable:j,setVariables:R,getVariable:C,refreshData:P,executeAction:S}}var o={phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1},i=e.createContext({runtime:null,state:o,hostApi:null});function s({runtime:e,state:r,hostApi:t,children:a}){const o={runtime:e,state:r,hostApi:t};return n.jsx(i.Provider,{value:o,children:a})}function l(){return e.useContext(i)}var c={phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1};function u(r,n){const t=l(),[a,o]=e.useState(!1),[i,s]=e.useState(),[c,u]=e.useState(null),[d,p]=e.useState(0),m=e.useRef(!0);e.useEffect(()=>(m.current=!0,()=>{m.current=!1}),[]);const h=e.useCallback(()=>{s(void 0),u(null),p(0)},[]),f=e.useCallback(async(e,a)=>{const o=t.hostApi;if(!o)throw new Error("HostAPI not available");try{const n=await o.executeAction(r,e);if(n.success)return n.data;throw new Error(n.errorMessage||"Action failed")}catch(r){if(a>0)return await new Promise(e=>setTimeout(e,n?.retryDelay||1e3)),f(e,a-1);throw r}},[t.hostApi,r,n?.retryDelay]);return{execute:e.useCallback(async e=>{if(m.current){o(!0),u(null),p(e=>e+1);try{const r=await f(e,n?.retryCount||0);return m.current&&(s(r),n?.onSuccess?.(r)),r}catch(e){const r=e;throw m.current&&(u(r),n?.onError?.(r)),e}finally{m.current&&o(!1)}}},[f,n]),loading:a,result:i,error:c,reset:h,executionCount:d}}exports.DJVProvider=function({runtime:r,hostApi:t,children:a,className:o,debug:i=!1,onStateChange:l,onPhaseChange:u,onError:d}){const[p,m]=e.useState(r?.getState()||c),h=e.useRef(p.phase);return e.useEffect(()=>{if(!r)return void m(c);m(r.getState());const e=r.onStateChange(e=>{m(e),l?.(e),e.phase!==h.current&&(u?.(e.phase),h.current=e.phase),e.error&&d?.(e.error)});return()=>{e()}},[r,l,u,d,i]),e.useEffect(()=>{},[t]),n.jsx(s,{runtime:r,state:p,hostApi:t,children:n.jsx("div",{className:["djvlc-provider",o].filter(Boolean).join(" "),"data-phase":p.phase,children:a})})},exports.DJVRenderer=function({pageId:r,apiBaseUrl:t,cdnBaseUrl:o,userApiAdapter:i,channel:l="prod",userId:c,deviceId:u,authToken:d,previewToken:p,debug:m=!1,enableSRI:h=!0,onLoad:f,onError:w,onReady:y,onPhaseChange:v,loadingComponent:g,errorComponent:x,emptyComponent:T,children:b,className:E,style:A,retryCount:I=3,retryDelay:N=1e3,timeout:j=3e4}){const R=e.useRef(null),C=e.useRef(!0),P=e.useRef(0),[S,k]=e.useState({phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1}),q={pageId:r,apiBaseUrl:t,cdnBaseUrl:o,userApiAdapter:i,channel:l,userId:c,deviceId:u,authToken:d,previewToken:p,debug:m,enableSRI:h,containerRef:R,onError:e=>{w?.(e)}},{runtime:B,loading:D,phase:M,page:U,error:V,hostApi:z,init:L,load:H,render:O,destroy:J}=a(q);e.useEffect(()=>{v?.(M)},[M,v]);const F=e.useCallback(async()=>{if(!R.current||!C.current)return;const e=new Promise((e,r)=>{setTimeout(()=>r(new Error("加载超时")),j)});try{await Promise.race([(async()=>{await L();const e=await H();C.current&&f?.(e),await O(),C.current&&(y?.(),P.current=0,B&&B.onStateChange(e=>{C.current&&k(e)}))})(),e])}catch(e){if(!C.current)return;P.current<I&&(P.current++,setTimeout(()=>{C.current&&F()},N))}},[L,H,O,B,f,y,j,I,N,m]),$=e.useCallback(()=>{P.current=0,F()},[F]);return e.useEffect(()=>(C.current=!0,F(),()=>{C.current=!1,J()}),[r]),n.jsx(s,{runtime:B,state:S,hostApi:z,children:n.jsxs("div",{ref:R,className:`djvlc-renderer ${E||""}`,style:A,"data-phase":M,"data-page-id":r,children:[D?g||n.jsxs("div",{className:"djvlc-loading",children:[n.jsx("div",{className:"djvlc-loading-spinner"}),n.jsx("span",{children:"加载中..."}),P.current>0&&n.jsxs("span",{className:"djvlc-loading-retry",children:["重试 ",P.current,"/",I]})]}):null,V?"function"==typeof x?x(V,$):x||n.jsxs("div",{className:"djvlc-error",children:[n.jsx("div",{className:"djvlc-error-icon",children:"⚠️"}),n.jsxs("span",{className:"djvlc-error-message",children:["加载失败:",V.message]}),n.jsx("button",{className:"djvlc-error-retry-btn",onClick:$,type:"button",children:"重试"})]}):null,D||V||U||"ready"!==M||U?null:T||n.jsx("div",{className:"djvlc-empty",children:n.jsx("span",{children:"暂无内容"})}),b]})})},exports.RuntimeContext=i,exports.RuntimeProvider=s,exports.useAction=u,exports.useAsync=function(r,n=[],t){const[a,o]=e.useState(),[i,s]=e.useState(!1),[l,c]=e.useState(null),u=e.useRef(!0);e.useEffect(()=>(u.current=!0,()=>{u.current=!1}),[]);const d=e.useCallback(async()=>{if(u.current){s(!0),c(null);try{const e=await r();return u.current&&(o(e),t?.onSuccess?.(e)),e}catch(e){const r=e;return void(u.current&&(c(r),t?.onError?.(r)))}finally{u.current&&s(!1)}}},[r,...n]);return e.useEffect(()=>{!1!==t?.immediate&&d()},[]),{data:a,loading:i,error:l,execute:d}},exports.useComponentState=function(r){const n=l().state.components.get(r);return e.useMemo(()=>({isLoaded:"loaded"===n?.status,isLoading:"loading"===n?.status,hasError:"failed"===n?.status,loadTime:n?.loadTime,info:n}),[n])},exports.useDJVRuntime=function(){const r=l(),n=e.useMemo(()=>{const e=r.state.phase;return"ready"!==e&&"error"!==e},[r.state.phase]),t="ready"===r.state.phase,a="error"===r.state.phase||null!==r.state.error,o=e.useCallback(async()=>{if(!r.runtime)throw new Error("Runtime not available");await r.runtime.load(),await r.runtime.render()},[r.runtime]);return{runtime:r.runtime,state:r.state,loading:n,phase:r.state.phase,error:r.state.error,page:r.state.page,isReady:t,hasError:a,reload:o}},exports.useData=function(r,n,t){const a=l(),[o,i]=e.useState(),[s,c]=e.useState(!1),[u,d]=e.useState(null),[p,m]=e.useState(!1),h=e.useRef(!0),f=e.useRef(n);e.useEffect(()=>(h.current=!0,()=>{h.current=!1}),[]);const w=e.useCallback(async e=>{const n=a.hostApi;if(n&&h.current){c(!0),d(null);try{const a=await n.requestData(r,e||f.current);if(!h.current)return;i(a),m(!0),t?.onSuccess?.(a)}catch(e){if(h.current){const r=e;d(r),t?.onError?.(r)}}finally{h.current&&c(!1)}}},[a.hostApi,r,t]);return e.useEffect(()=>{const e=f.current;f.current=n,!1!==t?.refreshOnParamsChange&&a.hostApi&&p&&JSON.stringify(e)!==JSON.stringify(n)&&w()},[n,a.hostApi,t?.refreshOnParamsChange,p,w]),e.useEffect(()=>{!1!==t?.immediate&&a.hostApi&&w()},[a.hostApi]),e.useEffect(()=>{if(!t?.refreshInterval||t.refreshInterval<=0)return;const e=setInterval(()=>{w()},t.refreshInterval);return()=>clearInterval(e)},[t?.refreshInterval,w]),{data:o,loading:s,error:u,refetch:w,isFetched:p}},exports.useDebouncedAction=function(r,n=300){const{execute:t,loading:a,result:o,error:i}=u(r),s=e.useRef(null),l=e.useRef(!0);return e.useEffect(()=>(l.current=!0,()=>{l.current=!1,s.current&&clearTimeout(s.current)}),[]),{execute:e.useCallback(e=>{s.current&&clearTimeout(s.current),s.current=setTimeout(()=>{l.current&&t(e).catch(()=>{})},n)},[t,n]),loading:a,result:o,error:i,cancel:e.useCallback(()=>{s.current&&(clearTimeout(s.current),s.current=null)},[])}},exports.useHostApi=function(){const e=l();if(!e.hostApi)throw new Error("HostAPI not available. Make sure runtime is initialized.");return e.hostApi},exports.useLifecycle=function(r){const n=l(),[t,a]=e.useState(!1),[o,i]=e.useState(!1),s=e.useRef(n.state.phase),c=e.useRef(!0);return e.useEffect(()=>(c.current=!0,()=>{c.current=!1}),[]),e.useEffect(()=>{const e=n.state.phase;e!==s.current&&(r?.onPhaseChange?.(e),s.current=e),t||"idle"===e||(a(!0),Promise.resolve(r?.onMounted?.()).catch(e=>{r?.onError?.(e)})),o||"ready"!==e||(i(!0),Promise.resolve(r?.onReady?.()).catch(e=>{r?.onError?.(e)})),"error"===e&&n.state.error&&r?.onError?.(n.state.error)},[n.state.phase,n.state.error,t,o,r]),{phase:n.state.phase,hasMounted:t,hasReady:o}},exports.usePageInfo=function(){const r=l().state.page;return e.useMemo(()=>({pageId:r?.pageId,pageVersionId:r?.pageVersionId,schemaVersion:r?.pageJson?.schemaVersion,title:r?.title,config:r?.config,isLoaded:null!==r}),[r])},exports.usePrevious=function(r){const n=e.useRef();return e.useEffect(()=>{n.current=r},[r]),n.current},exports.useQuery=function(r,n){const t=l(),[a,o]=e.useState(!1),[i,s]=e.useState(null),[c,u]=e.useState(null),d=e.useRef(!0),p=t.state.queries[r],m=e.useCallback(async()=>{if(d.current){o(!0),s(null);try{await(t.runtime?.refreshData(r)),d.current&&u(Date.now())}catch(e){d.current&&s(e)}finally{d.current&&o(!1)}}},[t.runtime,r]);return e.useEffect(()=>(d.current=!0,n?.refreshOnMount&&t.runtime&&m(),()=>{d.current=!1}),[]),e.useEffect(()=>{if(!n?.refreshInterval||n.refreshInterval<=0)return;const e=setInterval(()=>{m()},n.refreshInterval);return()=>clearInterval(e)},[n?.refreshInterval,m]),e.useEffect(()=>{if(!n?.refreshOnFocus)return;const e=()=>{m()};return window.addEventListener("focus",e),()=>window.removeEventListener("focus",e)},[n?.refreshOnFocus,m]),{data:p,loading:a,error:i,refetch:m,lastUpdated:c}},exports.useReactRuntime=a,exports.useRuntimeContext=l,exports.useRuntimeEvent=function(r,n){const t=l();e.useEffect(()=>{const e=t.runtime?.on(r,e=>{n(e.data)});return()=>{e?.()}},[t.runtime,r,n])},exports.useRuntimeState=function(e){return l().state.variables[e]},exports.useRuntimeStateWritable=function(r,n){const t=l();return[t.state.variables[r]??n,e.useCallback(e=>{t.runtime?.setVariable(r,e)},[t.runtime,r])]},exports.useWhen=function(r,n,t){const[a,o]=e.useState(!1),{once:i=!0}=t||{};return e.useEffect(()=>{!r||i&&a||(o(!0),n())},[r,n,i,a]),{executed:a}};
package/dist/index.d.cts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _djvlc_runtime_core from '@djvlc/runtime-core';
2
- import { RuntimeOptions, DjvlcRuntime, RuntimePhase, PageResolveResult, RuntimeError, HostAPI, RuntimeState } from '@djvlc/runtime-core';
2
+ import { RuntimeOptions, DjvlcRuntime, RuntimePhase, PageResolveResult, RuntimeError, HostAPI, RuntimeState, UserApiAdapter } from '@djvlc/runtime-core';
3
3
  export { HostAPI, PageResolveResult, RuntimeError, RuntimeOptions, RuntimePhase, RuntimeState } from '@djvlc/runtime-core';
4
4
  import * as react from 'react';
5
5
  import react__default, { ReactNode } from 'react';
@@ -132,6 +132,8 @@ interface DJVRendererProps {
132
132
  apiBaseUrl: string;
133
133
  /** CDN 基础 URL */
134
134
  cdnBaseUrl: string;
135
+ /** User API 适配器(必填,可由 @djvlc/runtime-web 的 OpenApiUserAdapter 提供) */
136
+ userApiAdapter: UserApiAdapter;
135
137
  /** 渠道 */
136
138
  channel?: 'preview' | 'prod' | 'gray';
137
139
  /** 用户 ID */
@@ -176,7 +178,7 @@ interface DJVRendererProps {
176
178
  /**
177
179
  * DJV 渲染器组件
178
180
  */
179
- declare function DJVRenderer({ pageId, apiBaseUrl, cdnBaseUrl, channel, userId, deviceId, authToken, previewToken, debug, enableSRI, onLoad, onError, onReady, onPhaseChange, loadingComponent, errorComponent, emptyComponent, children, className, style, retryCount, retryDelay, timeout, }: DJVRendererProps): JSX.Element;
181
+ declare function DJVRenderer({ pageId, apiBaseUrl, cdnBaseUrl, userApiAdapter, channel, userId, deviceId, authToken, previewToken, debug, enableSRI, onLoad, onError, onReady, onPhaseChange, loadingComponent, errorComponent, emptyComponent, children, className, style, retryCount, retryDelay, timeout, }: DJVRendererProps): JSX.Element;
180
182
 
181
183
  /**
182
184
  * DJV Provider 组件
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import * as _djvlc_runtime_core from '@djvlc/runtime-core';
2
- import { RuntimeOptions, DjvlcRuntime, RuntimePhase, PageResolveResult, RuntimeError, HostAPI, RuntimeState } from '@djvlc/runtime-core';
2
+ import { RuntimeOptions, DjvlcRuntime, RuntimePhase, PageResolveResult, RuntimeError, HostAPI, RuntimeState, UserApiAdapter } from '@djvlc/runtime-core';
3
3
  export { HostAPI, PageResolveResult, RuntimeError, RuntimeOptions, RuntimePhase, RuntimeState } from '@djvlc/runtime-core';
4
4
  import * as react from 'react';
5
5
  import react__default, { ReactNode } from 'react';
@@ -132,6 +132,8 @@ interface DJVRendererProps {
132
132
  apiBaseUrl: string;
133
133
  /** CDN 基础 URL */
134
134
  cdnBaseUrl: string;
135
+ /** User API 适配器(必填,可由 @djvlc/runtime-web 的 OpenApiUserAdapter 提供) */
136
+ userApiAdapter: UserApiAdapter;
135
137
  /** 渠道 */
136
138
  channel?: 'preview' | 'prod' | 'gray';
137
139
  /** 用户 ID */
@@ -176,7 +178,7 @@ interface DJVRendererProps {
176
178
  /**
177
179
  * DJV 渲染器组件
178
180
  */
179
- declare function DJVRenderer({ pageId, apiBaseUrl, cdnBaseUrl, channel, userId, deviceId, authToken, previewToken, debug, enableSRI, onLoad, onError, onReady, onPhaseChange, loadingComponent, errorComponent, emptyComponent, children, className, style, retryCount, retryDelay, timeout, }: DJVRendererProps): JSX.Element;
181
+ declare function DJVRenderer({ pageId, apiBaseUrl, cdnBaseUrl, userApiAdapter, channel, userId, deviceId, authToken, previewToken, debug, enableSRI, onLoad, onError, onReady, onPhaseChange, loadingComponent, errorComponent, emptyComponent, children, className, style, retryCount, retryDelay, timeout, }: DJVRendererProps): JSX.Element;
180
182
 
181
183
  /**
182
184
  * DJV Provider 组件
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import{createContext as e,useState as r,useRef as n,useMemo as t,useCallback as a,useContext as o,useEffect as i}from"react";import{createRuntime as l}from"@djvlc/runtime-core";import{jsx as s,jsxs as c}from"react/jsx-runtime";var u={initTime:0,loadTime:0,renderTime:0,totalTime:0,initTimestamp:null,readyTimestamp:null};function d(e){const[o,i]=r(null),[s,c]=r(!0),[d,m]=r("idle"),[f,h]=r(null),[p,w]=r(null),[y,v]=r(null),[g,T]=r(u),b=n(null),E=n({startTime:0,initStartTime:0,loadStartTime:0,renderStartTime:0}),I=t(()=>"ready"===d,[d]),A=t(()=>"error"===d||null!==p,[d,p]),N=a(async()=>{const r=e.containerRef?.current;if(!r)throw new Error("Container element not found");E.current.startTime=performance.now(),E.current.initStartTime=E.current.startTime,e.enableMetrics&&T(e=>({...e,initTimestamp:Date.now()}));const n=l({...e,container:r,onError:r=>{w(r),e.onError?.(r)},onEvent:r=>{e.onEvent?.(r)}});b.current=n,i(n),n.onStateChange(e=>{m(e.phase),c("ready"!==e.phase&&"error"!==e.phase),e.page&&h(e.page),e.error&&w(e.error)}),await n.init(),v(n.getHostApi()),e.enableMetrics&&T(e=>({...e,initTime:performance.now()-E.current.initStartTime}))},[e]),j=a(async()=>{if(!b.current)throw new Error("Runtime not initialized");E.current.loadStartTime=performance.now();const r=await b.current.load();return h(r),v(b.current.getHostApi()),e.enableMetrics&&T(e=>({...e,loadTime:performance.now()-E.current.loadStartTime})),r},[e.enableMetrics]),R=a(async()=>{if(!b.current)throw new Error("Runtime not initialized");if(E.current.renderStartTime=performance.now(),await b.current.render(),c(!1),e.enableMetrics){const e=performance.now();T(r=>({...r,renderTime:e-E.current.renderStartTime,totalTime:e-E.current.startTime,readyTimestamp:Date.now()}))}},[e.enableMetrics]),C=a(()=>{b.current?.destroy(),b.current=null,i(null),v(null),h(null),w(null),m("idle"),c(!0),T(u)},[]),P=a(async()=>{if(!b.current)throw new Error("Runtime not initialized");w(null),c(!0),await j(),await R()},[j,R]),x=a((e,r)=>{b.current?.setVariable(e,r)},[]),S=a(e=>{b.current&&Object.entries(e).forEach(([e,r])=>{b.current?.setVariable(e,r)})},[]),k=a(e=>b.current?.getState().variables[e],[]),B=a(async e=>{await(b.current?.refreshData(e))},[]),D=a(async(e,r)=>{const n=y;if(!n)throw new Error("HostAPI not available");const t=await n.executeAction(e,r||{});if(t.success)return t.data;throw new Error(t.errorMessage||"Action failed")},[y]);return{runtime:o,loading:s,phase:d,page:f,error:p,hostApi:y,isReady:I,hasError:A,metrics:g,init:N,load:j,render:R,destroy:C,reload:P,setVariable:x,setVariables:S,getVariable:k,refreshData:B,executeAction:D}}var m=e({runtime:null,state:{phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1},hostApi:null});function f({runtime:e,state:r,hostApi:n,children:t}){const a={runtime:e,state:r,hostApi:n};return s(m.Provider,{value:a,children:t})}function h(){return o(m)}function p({pageId:e,apiBaseUrl:t,cdnBaseUrl:o,channel:l="prod",userId:u,deviceId:m,authToken:h,previewToken:p,debug:w=!1,enableSRI:y=!0,onLoad:v,onError:g,onReady:T,onPhaseChange:b,loadingComponent:E,errorComponent:I,emptyComponent:A,children:N,className:j,style:R,retryCount:C=3,retryDelay:P=1e3,timeout:x=3e4}){const S=n(null),k=n(!0),B=n(0),[D,M]=r({phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1}),U={pageId:e,apiBaseUrl:t,cdnBaseUrl:o,channel:l,userId:u,deviceId:m,authToken:h,previewToken:p,debug:w,enableSRI:y,containerRef:S,onError:e=>{g?.(e)}},{runtime:V,loading:z,phase:L,page:q,error:H,hostApi:O,init:J,load:F,render:$,destroy:G}=d(U);i(()=>{b?.(L)},[L,b]);const K=a(async()=>{if(!S.current||!k.current)return;const e=new Promise((e,r)=>{setTimeout(()=>r(new Error("加载超时")),x)});try{await Promise.race([(async()=>{await J();const e=await F();k.current&&v?.(e),await $(),k.current&&(T?.(),B.current=0,V&&V.onStateChange(e=>{k.current&&M(e)}))})(),e])}catch(e){if(!k.current)return;B.current<C&&(B.current++,setTimeout(()=>{k.current&&K()},P))}},[J,F,$,V,v,T,x,C,P,w]),Q=a(()=>{B.current=0,K()},[K]);return i(()=>(k.current=!0,K(),()=>{k.current=!1,G()}),[e]),s(f,{runtime:V,state:D,hostApi:O,children:c("div",{ref:S,className:`djvlc-renderer ${j||""}`,style:R,"data-phase":L,"data-page-id":e,children:[z?E||c("div",{className:"djvlc-loading",children:[s("div",{className:"djvlc-loading-spinner"}),s("span",{children:"加载中..."}),B.current>0&&c("span",{className:"djvlc-loading-retry",children:["重试 ",B.current,"/",C]})]}):null,H?"function"==typeof I?I(H,Q):I||c("div",{className:"djvlc-error",children:[s("div",{className:"djvlc-error-icon",children:"⚠️"}),c("span",{className:"djvlc-error-message",children:["加载失败:",H.message]}),s("button",{className:"djvlc-error-retry-btn",onClick:Q,type:"button",children:"重试"})]}):null,z||H||q||"ready"!==L||q?null:A||s("div",{className:"djvlc-empty",children:s("span",{children:"暂无内容"})}),N]})})}var w={phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1};function y({runtime:e,hostApi:t,children:a,className:o,debug:l=!1,onStateChange:c,onPhaseChange:u,onError:d}){const[m,h]=r(e?.getState()||w),p=n(m.phase);return i(()=>{if(!e)return void h(w);h(e.getState());const r=e.onStateChange(e=>{h(e),c?.(e),e.phase!==p.current&&(u?.(e.phase),p.current=e.phase),e.error&&d?.(e.error)});return()=>{r()}},[e,c,u,d,l]),i(()=>{},[t]),s(f,{runtime:e,state:m,hostApi:t,children:s("div",{className:["djvlc-provider",o].filter(Boolean).join(" "),"data-phase":m.phase,children:a})})}function v(){const e=h(),r=t(()=>{const r=e.state.phase;return"ready"!==r&&"error"!==r},[e.state.phase]),n="ready"===e.state.phase,o="error"===e.state.phase||null!==e.state.error,i=a(async()=>{if(!e.runtime)throw new Error("Runtime not available");await e.runtime.load(),await e.runtime.render()},[e.runtime]);return{runtime:e.runtime,state:e.state,loading:r,phase:e.state.phase,error:e.state.error,page:e.state.page,isReady:n,hasError:o,reload:i}}function g(){const e=h();if(!e.hostApi)throw new Error("HostAPI not available. Make sure runtime is initialized.");return e.hostApi}function T(e){return h().state.variables[e]}function b(e,r){const n=h();return[n.state.variables[e]??r,a(r=>{n.runtime?.setVariable(e,r)},[n.runtime,e])]}function E(e,t){const o=h(),[l,s]=r(!1),[c,u]=r(null),[d,m]=r(null),f=n(!0),p=o.state.queries[e],w=a(async()=>{if(f.current){s(!0),u(null);try{await(o.runtime?.refreshData(e)),f.current&&m(Date.now())}catch(e){f.current&&u(e)}finally{f.current&&s(!1)}}},[o.runtime,e]);return i(()=>(f.current=!0,t?.refreshOnMount&&o.runtime&&w(),()=>{f.current=!1}),[]),i(()=>{if(!t?.refreshInterval||t.refreshInterval<=0)return;const e=setInterval(()=>{w()},t.refreshInterval);return()=>clearInterval(e)},[t?.refreshInterval,w]),i(()=>{if(!t?.refreshOnFocus)return;const e=()=>{w()};return window.addEventListener("focus",e),()=>window.removeEventListener("focus",e)},[t?.refreshOnFocus,w]),{data:p,loading:l,error:c,refetch:w,lastUpdated:d}}function I(e,t){const o=h(),[l,s]=r(!1),[c,u]=r(),[d,m]=r(null),[f,p]=r(0),w=n(!0);i(()=>(w.current=!0,()=>{w.current=!1}),[]);const y=a(()=>{u(void 0),m(null),p(0)},[]),v=a(async(r,n)=>{const a=o.hostApi;if(!a)throw new Error("HostAPI not available");try{const n=await a.executeAction(e,r);if(n.success)return n.data;throw new Error(n.errorMessage||"Action failed")}catch(e){if(n>0)return await new Promise(e=>setTimeout(e,t?.retryDelay||1e3)),v(r,n-1);throw e}},[o.hostApi,e,t?.retryDelay]);return{execute:a(async e=>{if(w.current){s(!0),m(null),p(e=>e+1);try{const r=await v(e,t?.retryCount||0);return w.current&&(u(r),t?.onSuccess?.(r)),r}catch(e){const r=e;throw w.current&&(m(r),t?.onError?.(r)),e}finally{w.current&&s(!1)}}},[v,t]),loading:l,result:c,error:d,reset:y,executionCount:f}}function A(e,t,o){const l=h(),[s,c]=r(),[u,d]=r(!1),[m,f]=r(null),[p,w]=r(!1),y=n(!0),v=n(t);i(()=>(y.current=!0,()=>{y.current=!1}),[]);const g=a(async r=>{const n=l.hostApi;if(n&&y.current){d(!0),f(null);try{const t=await n.requestData(e,r||v.current);if(!y.current)return;c(t),w(!0),o?.onSuccess?.(t)}catch(e){if(y.current){const r=e;f(r),o?.onError?.(r)}}finally{y.current&&d(!1)}}},[l.hostApi,e,o]);return i(()=>{const e=v.current;v.current=t,!1!==o?.refreshOnParamsChange&&l.hostApi&&p&&JSON.stringify(e)!==JSON.stringify(t)&&g()},[t,l.hostApi,o?.refreshOnParamsChange,p,g]),i(()=>{!1!==o?.immediate&&l.hostApi&&g()},[l.hostApi]),i(()=>{if(!o?.refreshInterval||o.refreshInterval<=0)return;const e=setInterval(()=>{g()},o.refreshInterval);return()=>clearInterval(e)},[o?.refreshInterval,g]),{data:s,loading:u,error:m,refetch:g,isFetched:p}}function N(e,r){const n=h();i(()=>{const t=n.runtime?.on(e,e=>{r(e.data)});return()=>{t?.()}},[n.runtime,e,r])}function j(){const e=h().state.page;return t(()=>({pageId:e?.pageId,pageVersionId:e?.pageVersionId,schemaVersion:e?.pageJson?.schemaVersion,title:e?.title,config:e?.config,isLoaded:null!==e}),[e])}function R(e){const r=h().state.components.get(e);return t(()=>({isLoaded:"loaded"===r?.status,isLoading:"loading"===r?.status,hasError:"failed"===r?.status,loadTime:r?.loadTime,info:r}),[r])}function C(e){const t=h(),[a,o]=r(!1),[l,s]=r(!1),c=n(t.state.phase),u=n(!0);return i(()=>(u.current=!0,()=>{u.current=!1}),[]),i(()=>{const r=t.state.phase;r!==c.current&&(e?.onPhaseChange?.(r),c.current=r),a||"idle"===r||(o(!0),Promise.resolve(e?.onMounted?.()).catch(r=>{e?.onError?.(r)})),l||"ready"!==r||(s(!0),Promise.resolve(e?.onReady?.()).catch(r=>{e?.onError?.(r)})),"error"===r&&t.state.error&&e?.onError?.(t.state.error)},[t.state.phase,t.state.error,a,l,e]),{phase:t.state.phase,hasMounted:a,hasReady:l}}function P(e,n,t){const[a,o]=r(!1),{once:l=!0}=t||{};return i(()=>{!e||l&&a||(o(!0),n())},[e,n,l,a]),{executed:a}}function x(e,r=300){const{execute:t,loading:o,result:l,error:s}=I(e),c=n(null),u=n(!0);return i(()=>(u.current=!0,()=>{u.current=!1,c.current&&clearTimeout(c.current)}),[]),{execute:a(e=>{c.current&&clearTimeout(c.current),c.current=setTimeout(()=>{u.current&&t(e).catch(()=>{})},r)},[t,r]),loading:o,result:l,error:s,cancel:a(()=>{c.current&&(clearTimeout(c.current),c.current=null)},[])}}function S(e,t=[],o){const[l,s]=r(),[c,u]=r(!1),[d,m]=r(null),f=n(!0);i(()=>(f.current=!0,()=>{f.current=!1}),[]);const h=a(async()=>{if(f.current){u(!0),m(null);try{const r=await e();return f.current&&(s(r),o?.onSuccess?.(r)),r}catch(e){const r=e;return void(f.current&&(m(r),o?.onError?.(r)))}finally{f.current&&u(!1)}}},[e,...t]);return i(()=>{!1!==o?.immediate&&h()},[]),{data:l,loading:c,error:d,execute:h}}function k(e){const r=n();return i(()=>{r.current=e},[e]),r.current}export{y as DJVProvider,p as DJVRenderer,m as RuntimeContext,f as RuntimeProvider,I as useAction,S as useAsync,R as useComponentState,v as useDJVRuntime,A as useData,x as useDebouncedAction,g as useHostApi,C as useLifecycle,j as usePageInfo,k as usePrevious,E as useQuery,d as useReactRuntime,h as useRuntimeContext,N as useRuntimeEvent,T as useRuntimeState,b as useRuntimeStateWritable,P as useWhen};
1
+ import{createContext as e,useState as r,useRef as n,useMemo as t,useCallback as a,useContext as o,useEffect as i}from"react";import{createRuntime as l}from"@djvlc/runtime-core";import{jsx as s,jsxs as c}from"react/jsx-runtime";var u={initTime:0,loadTime:0,renderTime:0,totalTime:0,initTimestamp:null,readyTimestamp:null};function d(e){const[o,i]=r(null),[s,c]=r(!0),[d,m]=r("idle"),[f,h]=r(null),[p,w]=r(null),[y,v]=r(null),[g,T]=r(u),b=n(null),E=n({startTime:0,initStartTime:0,loadStartTime:0,renderStartTime:0}),A=t(()=>"ready"===d,[d]),I=t(()=>"error"===d||null!==p,[d,p]),N=a(async()=>{const r=e.containerRef?.current;if(!r)throw new Error("Container element not found");E.current.startTime=performance.now(),E.current.initStartTime=E.current.startTime,e.enableMetrics&&T(e=>({...e,initTimestamp:Date.now()}));const n=l({...e,container:r,onError:r=>{w(r),e.onError?.(r)},onEvent:r=>{e.onEvent?.(r)}});b.current=n,i(n),n.onStateChange(e=>{m(e.phase),c("ready"!==e.phase&&"error"!==e.phase),e.page&&h(e.page),e.error&&w(e.error)}),await n.init(),v(n.getHostApi()),e.enableMetrics&&T(e=>({...e,initTime:performance.now()-E.current.initStartTime}))},[e]),j=a(async()=>{if(!b.current)throw new Error("Runtime not initialized");E.current.loadStartTime=performance.now();const r=await b.current.load();return h(r),v(b.current.getHostApi()),e.enableMetrics&&T(e=>({...e,loadTime:performance.now()-E.current.loadStartTime})),r},[e.enableMetrics]),R=a(async()=>{if(!b.current)throw new Error("Runtime not initialized");if(E.current.renderStartTime=performance.now(),await b.current.render(),c(!1),e.enableMetrics){const e=performance.now();T(r=>({...r,renderTime:e-E.current.renderStartTime,totalTime:e-E.current.startTime,readyTimestamp:Date.now()}))}},[e.enableMetrics]),C=a(()=>{b.current?.destroy(),b.current=null,i(null),v(null),h(null),w(null),m("idle"),c(!0),T(u)},[]),P=a(async()=>{if(!b.current)throw new Error("Runtime not initialized");w(null),c(!0),await j(),await R()},[j,R]),x=a((e,r)=>{b.current?.setVariable(e,r)},[]),S=a(e=>{b.current&&Object.entries(e).forEach(([e,r])=>{b.current?.setVariable(e,r)})},[]),k=a(e=>b.current?.getState().variables[e],[]),B=a(async e=>{await(b.current?.refreshData(e))},[]),D=a(async(e,r)=>{const n=y;if(!n)throw new Error("HostAPI not available");const t=await n.executeAction(e,r||{});if(t.success)return t.data;throw new Error(t.errorMessage||"Action failed")},[y]);return{runtime:o,loading:s,phase:d,page:f,error:p,hostApi:y,isReady:A,hasError:I,metrics:g,init:N,load:j,render:R,destroy:C,reload:P,setVariable:x,setVariables:S,getVariable:k,refreshData:B,executeAction:D}}var m=e({runtime:null,state:{phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1},hostApi:null});function f({runtime:e,state:r,hostApi:n,children:t}){const a={runtime:e,state:r,hostApi:n};return s(m.Provider,{value:a,children:t})}function h(){return o(m)}function p({pageId:e,apiBaseUrl:t,cdnBaseUrl:o,userApiAdapter:l,channel:u="prod",userId:m,deviceId:h,authToken:p,previewToken:w,debug:y=!1,enableSRI:v=!0,onLoad:g,onError:T,onReady:b,onPhaseChange:E,loadingComponent:A,errorComponent:I,emptyComponent:N,children:j,className:R,style:C,retryCount:P=3,retryDelay:x=1e3,timeout:S=3e4}){const k=n(null),B=n(!0),D=n(0),[M,U]=r({phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1}),V={pageId:e,apiBaseUrl:t,cdnBaseUrl:o,userApiAdapter:l,channel:u,userId:m,deviceId:h,authToken:p,previewToken:w,debug:y,enableSRI:v,containerRef:k,onError:e=>{T?.(e)}},{runtime:z,loading:L,phase:q,page:H,error:O,hostApi:J,init:F,load:$,render:G,destroy:K}=d(V);i(()=>{E?.(q)},[q,E]);const Q=a(async()=>{if(!k.current||!B.current)return;const e=new Promise((e,r)=>{setTimeout(()=>r(new Error("加载超时")),S)});try{await Promise.race([(async()=>{await F();const e=await $();B.current&&g?.(e),await G(),B.current&&(b?.(),D.current=0,z&&z.onStateChange(e=>{B.current&&U(e)}))})(),e])}catch(e){if(!B.current)return;D.current<P&&(D.current++,setTimeout(()=>{B.current&&Q()},x))}},[F,$,G,z,g,b,S,P,x,y]),W=a(()=>{D.current=0,Q()},[Q]);return i(()=>(B.current=!0,Q(),()=>{B.current=!1,K()}),[e]),s(f,{runtime:z,state:M,hostApi:J,children:c("div",{ref:k,className:`djvlc-renderer ${R||""}`,style:C,"data-phase":q,"data-page-id":e,children:[L?A||c("div",{className:"djvlc-loading",children:[s("div",{className:"djvlc-loading-spinner"}),s("span",{children:"加载中..."}),D.current>0&&c("span",{className:"djvlc-loading-retry",children:["重试 ",D.current,"/",P]})]}):null,O?"function"==typeof I?I(O,W):I||c("div",{className:"djvlc-error",children:[s("div",{className:"djvlc-error-icon",children:"⚠️"}),c("span",{className:"djvlc-error-message",children:["加载失败:",O.message]}),s("button",{className:"djvlc-error-retry-btn",onClick:W,type:"button",children:"重试"})]}):null,L||O||H||"ready"!==q||H?null:N||s("div",{className:"djvlc-empty",children:s("span",{children:"暂无内容"})}),j]})})}var w={phase:"idle",page:null,variables:{},queries:{},components:new Map,error:null,destroyed:!1};function y({runtime:e,hostApi:t,children:a,className:o,debug:l=!1,onStateChange:c,onPhaseChange:u,onError:d}){const[m,h]=r(e?.getState()||w),p=n(m.phase);return i(()=>{if(!e)return void h(w);h(e.getState());const r=e.onStateChange(e=>{h(e),c?.(e),e.phase!==p.current&&(u?.(e.phase),p.current=e.phase),e.error&&d?.(e.error)});return()=>{r()}},[e,c,u,d,l]),i(()=>{},[t]),s(f,{runtime:e,state:m,hostApi:t,children:s("div",{className:["djvlc-provider",o].filter(Boolean).join(" "),"data-phase":m.phase,children:a})})}function v(){const e=h(),r=t(()=>{const r=e.state.phase;return"ready"!==r&&"error"!==r},[e.state.phase]),n="ready"===e.state.phase,o="error"===e.state.phase||null!==e.state.error,i=a(async()=>{if(!e.runtime)throw new Error("Runtime not available");await e.runtime.load(),await e.runtime.render()},[e.runtime]);return{runtime:e.runtime,state:e.state,loading:r,phase:e.state.phase,error:e.state.error,page:e.state.page,isReady:n,hasError:o,reload:i}}function g(){const e=h();if(!e.hostApi)throw new Error("HostAPI not available. Make sure runtime is initialized.");return e.hostApi}function T(e){return h().state.variables[e]}function b(e,r){const n=h();return[n.state.variables[e]??r,a(r=>{n.runtime?.setVariable(e,r)},[n.runtime,e])]}function E(e,t){const o=h(),[l,s]=r(!1),[c,u]=r(null),[d,m]=r(null),f=n(!0),p=o.state.queries[e],w=a(async()=>{if(f.current){s(!0),u(null);try{await(o.runtime?.refreshData(e)),f.current&&m(Date.now())}catch(e){f.current&&u(e)}finally{f.current&&s(!1)}}},[o.runtime,e]);return i(()=>(f.current=!0,t?.refreshOnMount&&o.runtime&&w(),()=>{f.current=!1}),[]),i(()=>{if(!t?.refreshInterval||t.refreshInterval<=0)return;const e=setInterval(()=>{w()},t.refreshInterval);return()=>clearInterval(e)},[t?.refreshInterval,w]),i(()=>{if(!t?.refreshOnFocus)return;const e=()=>{w()};return window.addEventListener("focus",e),()=>window.removeEventListener("focus",e)},[t?.refreshOnFocus,w]),{data:p,loading:l,error:c,refetch:w,lastUpdated:d}}function A(e,t){const o=h(),[l,s]=r(!1),[c,u]=r(),[d,m]=r(null),[f,p]=r(0),w=n(!0);i(()=>(w.current=!0,()=>{w.current=!1}),[]);const y=a(()=>{u(void 0),m(null),p(0)},[]),v=a(async(r,n)=>{const a=o.hostApi;if(!a)throw new Error("HostAPI not available");try{const n=await a.executeAction(e,r);if(n.success)return n.data;throw new Error(n.errorMessage||"Action failed")}catch(e){if(n>0)return await new Promise(e=>setTimeout(e,t?.retryDelay||1e3)),v(r,n-1);throw e}},[o.hostApi,e,t?.retryDelay]);return{execute:a(async e=>{if(w.current){s(!0),m(null),p(e=>e+1);try{const r=await v(e,t?.retryCount||0);return w.current&&(u(r),t?.onSuccess?.(r)),r}catch(e){const r=e;throw w.current&&(m(r),t?.onError?.(r)),e}finally{w.current&&s(!1)}}},[v,t]),loading:l,result:c,error:d,reset:y,executionCount:f}}function I(e,t,o){const l=h(),[s,c]=r(),[u,d]=r(!1),[m,f]=r(null),[p,w]=r(!1),y=n(!0),v=n(t);i(()=>(y.current=!0,()=>{y.current=!1}),[]);const g=a(async r=>{const n=l.hostApi;if(n&&y.current){d(!0),f(null);try{const t=await n.requestData(e,r||v.current);if(!y.current)return;c(t),w(!0),o?.onSuccess?.(t)}catch(e){if(y.current){const r=e;f(r),o?.onError?.(r)}}finally{y.current&&d(!1)}}},[l.hostApi,e,o]);return i(()=>{const e=v.current;v.current=t,!1!==o?.refreshOnParamsChange&&l.hostApi&&p&&JSON.stringify(e)!==JSON.stringify(t)&&g()},[t,l.hostApi,o?.refreshOnParamsChange,p,g]),i(()=>{!1!==o?.immediate&&l.hostApi&&g()},[l.hostApi]),i(()=>{if(!o?.refreshInterval||o.refreshInterval<=0)return;const e=setInterval(()=>{g()},o.refreshInterval);return()=>clearInterval(e)},[o?.refreshInterval,g]),{data:s,loading:u,error:m,refetch:g,isFetched:p}}function N(e,r){const n=h();i(()=>{const t=n.runtime?.on(e,e=>{r(e.data)});return()=>{t?.()}},[n.runtime,e,r])}function j(){const e=h().state.page;return t(()=>({pageId:e?.pageId,pageVersionId:e?.pageVersionId,schemaVersion:e?.pageJson?.schemaVersion,title:e?.title,config:e?.config,isLoaded:null!==e}),[e])}function R(e){const r=h().state.components.get(e);return t(()=>({isLoaded:"loaded"===r?.status,isLoading:"loading"===r?.status,hasError:"failed"===r?.status,loadTime:r?.loadTime,info:r}),[r])}function C(e){const t=h(),[a,o]=r(!1),[l,s]=r(!1),c=n(t.state.phase),u=n(!0);return i(()=>(u.current=!0,()=>{u.current=!1}),[]),i(()=>{const r=t.state.phase;r!==c.current&&(e?.onPhaseChange?.(r),c.current=r),a||"idle"===r||(o(!0),Promise.resolve(e?.onMounted?.()).catch(r=>{e?.onError?.(r)})),l||"ready"!==r||(s(!0),Promise.resolve(e?.onReady?.()).catch(r=>{e?.onError?.(r)})),"error"===r&&t.state.error&&e?.onError?.(t.state.error)},[t.state.phase,t.state.error,a,l,e]),{phase:t.state.phase,hasMounted:a,hasReady:l}}function P(e,n,t){const[a,o]=r(!1),{once:l=!0}=t||{};return i(()=>{!e||l&&a||(o(!0),n())},[e,n,l,a]),{executed:a}}function x(e,r=300){const{execute:t,loading:o,result:l,error:s}=A(e),c=n(null),u=n(!0);return i(()=>(u.current=!0,()=>{u.current=!1,c.current&&clearTimeout(c.current)}),[]),{execute:a(e=>{c.current&&clearTimeout(c.current),c.current=setTimeout(()=>{u.current&&t(e).catch(()=>{})},r)},[t,r]),loading:o,result:l,error:s,cancel:a(()=>{c.current&&(clearTimeout(c.current),c.current=null)},[])}}function S(e,t=[],o){const[l,s]=r(),[c,u]=r(!1),[d,m]=r(null),f=n(!0);i(()=>(f.current=!0,()=>{f.current=!1}),[]);const h=a(async()=>{if(f.current){u(!0),m(null);try{const r=await e();return f.current&&(s(r),o?.onSuccess?.(r)),r}catch(e){const r=e;return void(f.current&&(m(r),o?.onError?.(r)))}finally{f.current&&u(!1)}}},[e,...t]);return i(()=>{!1!==o?.immediate&&h()},[]),{data:l,loading:c,error:d,execute:h}}function k(e){const r=n();return i(()=>{r.current=e},[e]),r.current}export{y as DJVProvider,p as DJVRenderer,m as RuntimeContext,f as RuntimeProvider,A as useAction,S as useAsync,R as useComponentState,v as useDJVRuntime,I as useData,x as useDebouncedAction,g as useHostApi,C as useLifecycle,j as usePageInfo,k as usePrevious,E as useQuery,d as useReactRuntime,h as useRuntimeContext,N as useRuntimeEvent,T as useRuntimeState,b as useRuntimeStateWritable,P as useWhen};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djvlc/runtime-host-react",
3
- "version": "1.1.6",
3
+ "version": "1.1.7",
4
4
  "description": "DJV 低代码平台 React 宿主适配器",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",
@@ -26,7 +26,7 @@
26
26
  "clean": "rimraf dist"
27
27
  },
28
28
  "dependencies": {
29
- "@djvlc/runtime-core": "1.1.0"
29
+ "@djvlc/runtime-core": "1.1.1"
30
30
  },
31
31
  "devDependencies": {
32
32
  "@types/node": "^20.10.0",