@grlt-hub/app-compose
Advanced tools
Comparing version 1.2.0 to 1.3.0
@@ -76,13 +76,25 @@ import * as effector from 'effector'; | ||
up: <T extends AnyContainer[], C extends { | ||
apis?: true; | ||
apis?: boolean; | ||
debug?: boolean; | ||
} | undefined>(containers: T, config?: C) => Promise<undefined extends C ? { | ||
autoResolveDeps?: { | ||
strict: true; | ||
optional?: boolean; | ||
}; | ||
} | undefined>(__containers: T, __config?: C) => Promise<undefined extends C ? { | ||
hasErrors: boolean; | ||
statuses: { [K in T[number]["id"]]: ContainerStatus; }; | ||
} : { | ||
} : NonNullable<C>["apis"] extends true ? { | ||
apis: { [K_1 in T[number] as K_1["id"]]?: Awaited<ReturnType<K_1["start"]>>["api"] | undefined; }; | ||
hasErrors: boolean; | ||
statuses: { [K in T[number]["id"]]: ContainerStatus; }; | ||
} : { | ||
hasErrors: boolean; | ||
statuses: { [K in T[number]["id"]]: ContainerStatus; }; | ||
}>; | ||
graph: (containers: AnyContainer[]) => { | ||
graph: (__containers: AnyContainer[], __config?: { | ||
autoResolveDeps?: { | ||
strict: true; | ||
optional?: boolean; | ||
}; | ||
} | undefined) => { | ||
[x: string]: { | ||
@@ -89,0 +101,0 @@ strict: AnyContainer["id"][]; |
@@ -1,5 +0,5 @@ | ||
import{createStore as g}from"effector";import"effector";var i={idle:"idle",pending:"pending",done:"done",fail:"fail",off:"off"};var T={CONTAINER_ID_EMPTY_STRING:"Container ID cannot be an empty string.",depsIntersection:(e,s)=>` | ||
Dependency conflict detected in container '${s}': | ||
import{createStore as w}from"effector";import"effector";var r={idle:"idle",pending:"pending",done:"done",fail:"fail",off:"off"};var R={CONTAINER_ID_EMPTY_STRING:"Container ID cannot be an empty string.",depsIntersection:(e,i)=>` | ||
Dependency conflict detected in container '${i}': | ||
The following dependencies are listed as both required and optional: [${e.join(", ")}]. | ||
Each dependency should be listed only once, as either required or optional.`},R=e=>{if(e.id==="")throw new Error(T.CONTAINER_ID_EMPTY_STRING)},h=e=>{if(!e.dependsOn||!e.optionalDependsOn)return;let s=new Set(e.dependsOn.map(a=>a.id)),p=new Set(e.optionalDependsOn.map(a=>a.id)),y=s.intersection(p);if(y.size>0)throw new Error(T.depsIntersection(Array.from(y),e.id))},S=e=>(R(e),h(e),e);var P=e=>{let s=S(e),p=g(i.idle);return{...s,$status:p}};var I=e=>{var c,r;let s=new Set,p=[],y=[],a=[];for((e.dependsOn||[]).forEach(t=>a.push([t,[e.id,t.id],"strict"])),(e.optionalDependsOn||[]).forEach(t=>a.push([t,[e.id,t.id],"optional"]));a.length>0;){let[t,o,l]=a.pop();s.has(t.id)||(s.add(t.id),!((c=e.dependsOn)!=null&&c.includes(t))&&!((r=e.optionalDependsOn)!=null&&r.includes(t))&&(l==="strict"?p.push({id:t.id,path:o.join(" -> ")}):y.push({id:t.id,path:o.join(" -> ")})),(t.dependsOn||[]).forEach(d=>{a.push([d,[...o,d.id],l])}),(t.optionalDependsOn||[]).forEach(d=>{a.push([d,[...o,d.id],"optional"])}))}return{strict:p,optional:y}};var O=e=>e.reduce((s,p)=>{let y=(p.dependsOn||[]).map(r=>r.id),a=(p.optionalDependsOn||[]).map(r=>r.id),c=I(p);return s[p.id]={strict:y,optional:a,transitive:{strict:c.strict,optional:c.optional}},s},{});import{clearNode as b,combine as A,createEffect as v,launch as w,sample as E}from"effector";var N=(e,s)=>{if(s.has(e))throw new Error(`Duplicate container ID found: ${e}`);s.add(e)},u={off:e=>e===i.off,fail:e=>e===i.fail,pending:e=>e===i.pending,done:e=>e===i.done,idle:e=>e===i.idle},x=async(e,s)=>{let p=new Set;for(let t of e)N(t.id,p);let y=e.reduce((t,o)=>(t[o.id]=o.$status,t),{}),a=A(y,t=>{let o=Object.values(t),l=o.every(f=>/^(done|fail|off)$/.test(f)),d=o.some(u.fail);return{done:l,hasErrors:d,statuses:t}});s!=null&&s.debug&&a.watch(t=>{console.debug(`[${new Date().toISOString()}] app-compose:`,JSON.stringify(t.statuses,null,2))});let c=[a],r={};return await Promise.allSettled(e.map(t=>{var C,m;let o=A(((C=t.dependsOn)!=null?C:[]).map(n=>n.$status),n=>n.some(u.off)?i.off:n.some(u.fail)?i.fail:n.some(u.pending)?i.pending:n.every(u.done)||n.length===0?i.done:i.idle),l=A(((m=t.optionalDependsOn)!=null?m:[]).map(n=>n.$status),n=>n.some(u.pending)?i.pending:n.some(u.idle)?i.idle:i.done),d=A([o,l],n=>n.every(u.done)),f=v(async()=>t.enable?await t.enable(r,r):!0),D=v(async()=>{r[t.id]=(await t.start(r,r)).api});E({clock:f.doneData,fn:n=>n?i.pending:i.off,target:t.$status}),E({clock:f.failData,fn:()=>i.fail,target:t.$status}),E({clock:t.$status,filter:u.pending,target:D}),E({clock:D.finally,fn:n=>n.status,target:t.$status}),o.watch(n=>{(u.off(n)||u.fail(n))&&w(t.$status,n)}),d.watch(n=>{n&&f()}),c=[...c,o,l,d,f,D]})),new Promise((t,o)=>{a.watch(l=>{if(l.done===!0){c.forEach(D=>b(D,{deep:!0}));let d=(s==null?void 0:s.apis)===!0;d||(r={});let f={hasErrors:l.hasErrors,statuses:l.statuses,...d?{apis:r}:{}};l.hasErrors&&o(f),t(f)}})})};var $={up:x,graph:O};export{$ as compose,P as createContainer}; | ||
Each dependency should be listed only once, as either required or optional.`},P=e=>{if(e.id==="")throw new Error(R.CONTAINER_ID_EMPTY_STRING)},N=e=>{if(!e.dependsOn||!e.optionalDependsOn)return;let i=new Set(e.dependsOn.map(o=>o.id)),s=new Set(e.optionalDependsOn.map(o=>o.id)),p=i.intersection(s);if(p.size>0)throw new Error(R.depsIntersection(Array.from(p),e.id))},g=e=>(P(e),N(e),e);var $=e=>{let i=g(e),s=w(r.idle);return{...i,$status:s}};var m=(e,i=!1)=>{let s=new Set,p=o=>{var y,d;s.has(o)||(s.add(o),(y=o.dependsOn)==null||y.forEach(p),i&&((d=o.optionalDependsOn)==null||d.forEach(p)))};return e.forEach(p),Array.from(s)};var O=e=>{var y,d;let i=new Set,s=[],p=[],o=[];for((e.dependsOn||[]).forEach(a=>o.push([a,[e.id,a.id],"strict"])),(e.optionalDependsOn||[]).forEach(a=>o.push([a,[e.id,a.id],"optional"]));o.length>0;){let[a,l,A]=o.pop();i.has(a.id)||(i.add(a.id),!((y=e.dependsOn)!=null&&y.includes(a))&&!((d=e.optionalDependsOn)!=null&&d.includes(a))&&(A==="strict"?s.push({id:a.id,path:l.join(" -> ")}):p.push({id:a.id,path:l.join(" -> ")})),(a.dependsOn||[]).forEach(t=>{o.push([t,[...l,t.id],A])}),(a.optionalDependsOn||[]).forEach(t=>{o.push([t,[...l,t.id],"optional"])}))}return{strict:s,optional:p}};var _=e=>Object.assign({autoResolveDeps:{strict:!1,optional:!1}},e!=null?e:{}),x=(e,i)=>{var o;let s=_(i);return((o=s.autoResolveDeps)!=null&&o.strict?m(e,s.autoResolveDeps.optional):e).reduce((y,d)=>{let a=(d.dependsOn||[]).map(t=>t.id),l=(d.optionalDependsOn||[]).map(t=>t.id),A=O(d);return y[d.id]={strict:a,optional:l,transitive:{strict:A.strict,optional:A.optional}},y},{})};import{clearNode as j,combine as S,createEffect as h,launch as k,sample as T}from"effector";var F=(e,i)=>{if(i.has(e))throw new Error(`Duplicate container ID found: ${e}`);i.add(e)},c={off:e=>e===r.off,fail:e=>e===r.fail,pending:e=>e===r.pending,done:e=>e===r.done,idle:e=>e===r.idle},K=e=>Object.assign({apis:!1,debug:!1,autoResolveDeps:{strict:!1,optional:!1}},e!=null?e:{}),b=async(e,i)=>{var A;let s=K(i),p=(A=s.autoResolveDeps)!=null&&A.strict?m(e,s.autoResolveDeps.optional):e,o=new Set;for(let t of p)F(t.id,o);let y=p.reduce((t,u)=>(t[u.id]=u.$status,t),{}),d=S(y,t=>{let u=Object.values(t),f=u.every(D=>/^(done|fail|off)$/.test(D)),C=u.some(c.fail);return{done:f,hasErrors:C,statuses:t}});s!=null&&s.debug&&d.watch(t=>{console.debug(`[${new Date().toISOString()}] app-compose:`,JSON.stringify(t.statuses,null,2))});let a=[d],l={};return await Promise.allSettled(p.map(t=>{var v,I;let u=S(((v=t.dependsOn)!=null?v:[]).map(n=>n.$status),n=>n.some(c.off)?r.off:n.some(c.fail)?r.fail:n.some(c.pending)?r.pending:n.every(c.done)||n.length===0?r.done:r.idle),f=S(((I=t.optionalDependsOn)!=null?I:[]).map(n=>n.$status),n=>n.some(c.pending)?r.pending:n.some(c.idle)?r.idle:r.done),C=S([u,f],n=>n.every(c.done)),D=h(async()=>t.enable?await t.enable(l,l):!0),E=h(async()=>{l[t.id]=(await t.start(l,l)).api});T({clock:D.doneData,fn:n=>n?r.pending:r.off,target:t.$status}),T({clock:D.failData,fn:()=>r.fail,target:t.$status}),T({clock:t.$status,filter:c.pending,target:E}),T({clock:E.finally,fn:n=>n.status,target:t.$status}),u.watch(n=>{(c.off(n)||c.fail(n))&&k(t.$status,n)}),C.watch(n=>{n&&D()}),a=[...a,u,f,C,D,E]})),new Promise((t,u)=>{d.watch(f=>{if(f.done===!0){a.forEach(E=>j(E,{deep:!0}));let C=(s==null?void 0:s.apis)===!0;C||(l={});let D={hasErrors:f.hasErrors,statuses:f.statuses,...C?{apis:l}:{}};f.hasErrors&&u(D),t(D)}})})};var V={up:b,graph:x};export{V as compose,$ as createContainer}; |
{ | ||
"name": "@grlt-hub/app-compose", | ||
"version": "1.2.0", | ||
"version": "1.3.0", | ||
"type": "module", | ||
@@ -9,12 +9,25 @@ "private": false, | ||
"types": "dist/index.d.ts", | ||
"files": [ | ||
"/README.md", | ||
"/package.json", | ||
"/dist" | ||
], | ||
"files": ["/README.md", "/package.json", "/dist"], | ||
"author": "Viktor Pasynok", | ||
"license": "MIT", | ||
"devDependencies": { | ||
"@size-limit/preset-small-lib": "11.1.6", | ||
"@vitest/coverage-v8": "2.1.4", | ||
"clean-publish": "5.1.0", | ||
"knip": "5.36.3", | ||
"prettier": "3.3.3", | ||
"prettier-plugin-organize-imports": "4.1.0", | ||
"size-limit": "11.1.6", | ||
"tslib": "2.8.1", | ||
"tsup": "8.3.5", | ||
"typescript": "5.6.3", | ||
"vitest": "2.1.4" | ||
}, | ||
"peerDependencies": { | ||
"effector": "23" | ||
}, | ||
"volta": { | ||
"node": "22.9.0", | ||
"npm": "10.9.0" | ||
}, | ||
"publishConfig": { | ||
@@ -21,0 +34,0 @@ "access": "public" |
@@ -75,3 +75,3 @@ # App Compose | ||
- Designed with a focus on **quality** and **performance**. | ||
- Weighs less than **1.5 kB**, making it lightweight. | ||
- Weighs less than **1.6 kB** (runtime), making it lightweight. | ||
- Covered by **100% tests**, including **type tests**. | ||
@@ -78,0 +78,0 @@ - Ensures high performance, suitable for **scalable applications**. |
14882
121
11