@grlt-hub/app-compose
Version:
Compose modules into apps
1 lines • 6.89 kB
JavaScript
import"./chunk-O7MQIALB.js";import{LIBRARY_NAME as e,isNil as t}from"./chunk-GQZ3QUEZ.js";import{createStore as n}from"effector";import"effector";var o="idle",i="pending",r="done",a="fail",s="off",d="Container ID cannot be an empty string.",c="Container Domain cannot be an empty string.",f=(e,t)=>`Dependency conflict detected in container "${t}":\nThe following dependencies are listed as both required and optional: [${e.join(", ")}].\n\nEach dependency should be listed only once, as either required or optional.`,l=e=>((e=>{if(t(e.id)||0===e.id.length)throw new Error(d)})(e),(e=>{if(t(e.domain)||0===e.domain.length)throw new Error(c)})(e),(e=>{if(t(e.dependencies)||t(e.optionalDependencies))return;const n=new Set(e.dependencies.map(e=>e.id)),o=new Set(e.optionalDependencies.map(e=>e.id)),i=n.intersection(o);if(i.size)throw new Error(f(Array.from(i),e.id))})(e),e),u=e=>({...l(e),$status:n(o)}),p=({container:e,containerIdsToBoot:t,visitedContainerIds:n})=>{const o={included:[],skipped:[]},i=e.optionalDependencies;if(!i)return o;for(const e of i)t.has(e.id)||n.has(e.id)?o.included.push(e):o.skipped.push(e.id);return o},g=({stageContainers:e,visitedContainerIds:t})=>{const{strictDependencies:n}=(e=>{var t;const n=new Set,o=new Set,i=[...e];for(;i.length>0;){const e=i.pop();o.has(e)||(o.add(e),null==(t=e.dependencies)||t.forEach(e=>{i.push(e),n.add(e)}))}return{strictDependencies:n}})(e),o=new Set,i=new Set;for(const r of[...e,...n])t.has(r.id)||(i.add(r),o.add(r.id));return{containerIdsToBoot:o,containersToBoot:i}},h=t=>`${e} Duplicate stage id detected: "${t}".\n\nEach stage id must be unique. Please ensure that the stage "${t}" appears only once in the configuration.`,m=t=>`${e} Duplicate container ID found: ${t}`,w=(t,n)=>`${e} Container with ID "${t}" is already included in a previous stage (up to stage "${n}").\n\nThis indicates an issue in the stage definitions provided to the compose function.\n\nSuggested actions:\n - Remove the container from the "${n}" stage in the compose configuration.\n - Use the graph fn to verify container dependencies and resolve potential conflicts.`,v=({visitedContainerIds:e,container:t,stageId:n})=>{if(e.has(t.id))throw new Error(w(t.id,n))},$=e=>{const t=new Set;for(const[n]of e){if(t.has(n))throw new Error(h(n));t.add(n)}},y=({container:e,visitedContainerIds:t})=>{if(t.has(e.id))throw new Error(m(e.id));t.add(e.id)},C=({visitedContainerIds:e,stageTuples:t})=>($(t),t.reduce((t,[n,o])=>{o.forEach(t=>v({visitedContainerIds:e,stageId:n,container:t}));const{containersToBoot:i,skippedContainers:r}=(({stageContainers:e,visitedContainerIds:t})=>{const{containerIdsToBoot:n,containersToBoot:o}=g({stageContainers:e,visitedContainerIds:t}),i={};for(const e of o){const{included:o,skipped:r}=p({container:e,containerIdsToBoot:n,visitedContainerIds:t});e.optionalDependencies=o,r.length&&(i[e.id]=r)}return{containersToBoot:Array.from(o),skippedContainers:i}})({visitedContainerIds:e,stageContainers:o});return i.forEach(t=>y({container:t,visitedContainerIds:e})),t.push({id:n,containersToBoot:i,skippedContainers:r}),t},[]));import{clearNode as I}from"effector";import{clearNode as k,combine as E,createDomain as S,launch as b,sample as D}from"effector";var T=t=>{console.error(`${e} Container "${t.container.id}" failed with error: ${t.error.message} on stage "${t.stageId}"`),t.error.stack&&console.error(`Stack trace:\n${t.error.stack}`)},q={off:e=>e===s,fail:e=>e===a,pending:e=>e===i,done:e=>e===r,idle:e=>e===o,failedOrOff:e=>e===a||e===s},O=(e,t)=>[...e,...t].reduce((e,{id:t,$status:n})=>(e[t]=q.done(n.getState()),e),{}),B=e=>{const t=(e=>Object.assign({debug:!1,onContainerFail:T},null!=e?e:{}))(e);return async(e,n)=>{const d=S(`up.${e.id}`),c=d.createEffect(t.onContainerFail);let f=[d];const l=e.containersToBoot.reduce((e,t)=>(e[t.id]=t.$status,e),{}),u=E(l,e=>({done:Object.values(e).every(e=>/^(done|fail|off)$/.test(e)),statuses:e}));return t.debug&&u.watch(t=>{console.debug(`%c>> ${e.id}`,"color: #E2A03F; font-weight: bold;"),Object.entries(t.statuses).forEach(([e,t])=>console.debug(`• ${e} = ${t}`))}),await Promise.allSettled(e.containersToBoot.map(t=>{var l,p;const g=null!=(l=t.dependencies)?l:[],h=null!=(p=t.optionalDependencies)?p:[],m=E(g.map(e=>e.$status),e=>e.some(q.off)?s:e.some(q.fail)?a:e.some(q.pending)?i:e.every(q.done)||0===e.length?r:o),w=E(h.map(e=>e.$status),e=>e.some(q.pending)||e.some(q.idle)?o:r),v=E([m,w],e=>e.every(q.done)),$=d.createEffect(async()=>!t.enable||await t.enable(n,O(g,h))),y=d.createEffect(async()=>{n[t.id]=(await t.start(n,O(g,h))).api});D({clock:$.doneData,fn:e=>e?i:s,target:t.$status}),D({clock:$.failData,fn:()=>a,target:t.$status}),D({clock:t.$status,filter:q.pending,target:y}),D({clock:y.finally,fn:e=>e.status,target:t.$status}),D({clock:[y.fail,$.fail],fn:n=>({container:{id:t.id,domain:t.domain},error:n.error,stageId:e.id}),target:c}),m.watch(e=>{(q.fail(e)||q.off(e))&&b(t.$status,e)}),v.watch(e=>{e&&$()}),f.push(m,w,v,u)})),new Promise(e=>{u.watch(t=>{if(!t.done)return;f.forEach(e=>k(e,{deep:!0})),f=[];const n={allDone:Object.values(t.statuses).every(e=>q.done(e)),containerStatuses:t.statuses};e(n)})})}},j=({id:t,stageId:n,log:o})=>{throw new Error(`${e} Application startup failed.\nRequired container(s) "${t}" did not up in stage "${n}".\n\nStartup Log:\n${JSON.stringify(o,null,2).replace(/[\{\},"]/g,"").replace(/\n\s*\n/g,"\n\n")}\nRecommendations:\n- Verify if the container(s) "${t}" are truly required.\n- If not, consider removing them from the required list in "up.required".\n- Ensure all dependencies for the container(s) are correct and their logic works as expected.`)},A=e=>{const t=e.map(e=>e.$status.getState());return t.every(q.fail)?a:t.every(q.off)?s:i},F={ok:!0},N=e=>{var n;if(t(e.required))return F;if("all"===e.required){const o=null==(n=Object.entries(e.containerStatuses).find(([e,t])=>q.failedOrOff(t)))?void 0:n[0];return t(o)?F:{ok:!1,id:[o]}}let o;for(let t=0;t<e.required.length;t++){const n=e.required[t],i="id"in n,r=i?n.$status.getState():A(n);if(q.failedOrOff(r)){o=i?n.id:n.map(e=>e.id);break}}return t(o)?F:{ok:!1,id:"string"==typeof o?[o]:o}},x=async e=>{const t=C({stageTuples:e.stages,visitedContainerIds:new Set});return{up:n=>(async(e,t)=>{const n=B(t);let o={};const i={};for(const t of e.stages){const r=await n(t,o);i[t.id]=r;const a=N({required:e.required,containerStatuses:r.containerStatuses});a.ok||j({id:a.id,stageId:t.id,log:i})}return e.stages.forEach(e=>e.containersToBoot.forEach(e=>I(e.$status,{deep:!0}))),o={},{allDone:Object.values(i).every(e=>e.allDone),stages:i}})({stages:t,required:e.required},n),diff:async()=>{(await import("./diff-NTGNATWT.js")).diff({expected:e.stages,received:t})},graph:async e=>{var n;return(await import("./graph-66CFWMXC.js")).graph({stages:t},{view:null!=(n=null==e?void 0:e.view)?n:"containers"})}}};export{x as compose,u as createContainer};