@grlt-hub/app-compose
Advanced tools
Comparing version 1.0.0 to 1.1.0
@@ -70,15 +70,25 @@ import * as effector from 'effector'; | ||
type Statuses<T extends AnyContainer[]> = { | ||
[K in T[number]['id']]: ContainerStatus; | ||
type TransitiveDependency = Pick<AnyContainer, 'id'> & { | ||
path: string; | ||
}; | ||
type UpResult<T extends AnyContainer[]> = { | ||
hasErrors: boolean; | ||
statuses: Statuses<T>; | ||
}; | ||
declare const compose: { | ||
up: <T extends AnyContainer[]>(containers: T, config?: { | ||
debug?: boolean; | ||
}) => Promise<UpResult<T>>; | ||
}) => Promise<{ | ||
hasErrors: boolean; | ||
statuses: { [K in T[number]["id"]]: ContainerStatus; }; | ||
}>; | ||
graph: (containers: AnyContainer[]) => { | ||
[x: string]: { | ||
strict: AnyContainer["id"][]; | ||
optional: AnyContainer["id"][]; | ||
transitive: { | ||
strict: TransitiveDependency[]; | ||
optional: TransitiveDependency[]; | ||
}; | ||
}; | ||
}; | ||
}; | ||
export { compose, createContainer }; |
@@ -1,5 +0,5 @@ | ||
import{clearNode as g,combine as f,createEffect as T,launch as v,sample as D}from"effector";import{createStore as O}from"effector";import"effector";var s={idle:"idle",pending:"pending",done:"done",fail:"fail",off:"off"};var m={CONTAINER_ID_EMPTY_STRING:"Container ID cannot be an empty string.",depsIntersection:(e,a)=>` | ||
Dependency conflict detected in container '${a}': | ||
import{createStore as h}from"effector";import"effector";var s={idle:"idle",pending:"pending",done:"done",fail:"fail",off:"off"};var S={CONTAINER_ID_EMPTY_STRING:"Container ID cannot be an empty string.",depsIntersection:(e,o)=>` | ||
Dependency conflict detected in container '${o}': | ||
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(m.CONTAINER_ID_EMPTY_STRING)},x=e=>{if(!e.dependsOn||!e.optionalDependsOn)return;let a=new Set(e.dependsOn.map(d=>d.id)),y=new Set(e.optionalDependsOn.map(d=>d.id)),c=a.intersection(y);if(c.size>0)throw new Error(m.depsIntersection(Array.from(c),e.id))},C=e=>(R(e),x(e),e);var P=e=>{let a=C(e),y=O(s.idle);return{...a,$status:y}};var b=(e,a)=>{if(a.has(e))throw new Error(`Duplicate container ID found: ${e}`);a.add(e)},r={off:e=>e===s.off,fail:e=>e===s.fail,pending:e=>e===s.pending,done:e=>e===s.done,idle:e=>e===s.idle},w=async(e,a)=>{let y=new Set;for(let n of e)b(n.id,y);let c=e.reduce((n,o)=>(n[o.id]=o.$status,n),{}),d=f(c,n=>{let o=Object.values(n),i=o.every(p=>/^(done|fail|off)$/.test(p)),l=o.some(r.fail);return{done:i,hasErrors:l,statuses:n}});a!=null&&a.debug&&d.watch(n=>{console.debug(`[${new Date().toISOString()}] app-compose:`,JSON.stringify(n.statuses,null,2))});let A=[d],u={};return await Promise.all(e.map(async n=>{var S,I;let o=f(((S=n.dependsOn)!=null?S:[]).map(t=>t.$status),t=>t.some(r.off)?s.off:t.some(r.fail)?s.fail:t.some(r.pending)?s.pending:t.every(r.done)||t.length===0?s.done:s.idle),i=f(((I=n.optionalDependsOn)!=null?I:[]).map(t=>t.$status),t=>t.some(r.pending)?s.pending:t.some(r.idle)?s.idle:s.done),l=f([o,i],t=>t.every(r.done)),p=T(async()=>n.enable?await n.enable(u,u):!0),E=T(async()=>{u[n.id]=(await n.start(u,u)).api});D({clock:p.doneData,fn:t=>t?s.pending:s.off,target:n.$status}),D({clock:p.failData,fn:()=>s.fail,target:n.$status}),D({clock:n.$status,filter:r.pending,target:E}),D({clock:E.finally,fn:t=>t.status,target:n.$status}),o.watch(t=>{(r.off(t)||r.fail(t))&&v(n.$status,t)}),l.watch(t=>{t&&p()}),A=[...A,o,i,l,p,E]})),new Promise((n,o)=>{d.watch(i=>{if(i.done===!0){u={},A.forEach(p=>g(p,{deep:!0}));let l={hasErrors:i.hasErrors,statuses:i.statuses};i.hasErrors&&o(l),n(l)}})})},N={up:w};export{N as compose,P as createContainer}; | ||
Each dependency should be listed only once, as either required or optional.`},R=e=>{if(e.id==="")throw new Error(S.CONTAINER_ID_EMPTY_STRING)},x=e=>{if(!e.dependsOn||!e.optionalDependsOn)return;let o=new Set(e.dependsOn.map(i=>i.id)),r=new Set(e.optionalDependsOn.map(i=>i.id)),c=o.intersection(r);if(c.size>0)throw new Error(S.depsIntersection(Array.from(c),e.id))},T=e=>(R(e),x(e),e);var P=e=>{let o=T(e),r=h(s.idle);return{...o,$status:r}};var I=e=>{var y,p;let o=new Set,r=[],c=[],i=[];for((e.dependsOn||[]).forEach(t=>i.push([t,[e.id,t.id],"strict"])),(e.optionalDependsOn||[]).forEach(t=>i.push([t,[e.id,t.id],"optional"]));i.length>0;){let[t,a,l]=i.pop();o.has(t.id)||(o.add(t.id),!((y=e.dependsOn)!=null&&y.includes(t))&&!((p=e.optionalDependsOn)!=null&&p.includes(t))&&(l==="strict"?r.push({id:t.id,path:a.join(" -> ")}):c.push({id:t.id,path:a.join(" -> ")})),(t.dependsOn||[]).forEach(d=>{i.push([d,[...a,d.id],l])}),(t.optionalDependsOn||[]).forEach(d=>{i.push([d,[...a,d.id],"optional"])}))}return{strict:r,optional:c}};var O=e=>e.reduce((o,r)=>{let c=(r.dependsOn||[]).map(p=>p.id),i=(r.optionalDependsOn||[]).map(p=>p.id),y=I(r);return o[r.id]={strict:c,optional:i,transitive:{strict:y.strict,optional:y.optional}},o},{});import{clearNode as b,combine as D,createEffect as v,launch as w,sample as A}from"effector";var N=(e,o)=>{if(o.has(e))throw new Error(`Duplicate container ID found: ${e}`);o.add(e)},u={off:e=>e===s.off,fail:e=>e===s.fail,pending:e=>e===s.pending,done:e=>e===s.done,idle:e=>e===s.idle},g=async(e,o)=>{let r=new Set;for(let t of e)N(t.id,r);let c=e.reduce((t,a)=>(t[a.id]=a.$status,t),{}),i=D(c,t=>{let a=Object.values(t),l=a.every(f=>/^(done|fail|off)$/.test(f)),d=a.some(u.fail);return{done:l,hasErrors:d,statuses:t}});o!=null&&o.debug&&i.watch(t=>{console.debug(`[${new Date().toISOString()}] app-compose:`,JSON.stringify(t.statuses,null,2))});let y=[i],p={};return await Promise.all(e.map(async t=>{var m,C;let a=D(((m=t.dependsOn)!=null?m:[]).map(n=>n.$status),n=>n.some(u.off)?s.off:n.some(u.fail)?s.fail:n.some(u.pending)?s.pending:n.every(u.done)||n.length===0?s.done:s.idle),l=D(((C=t.optionalDependsOn)!=null?C:[]).map(n=>n.$status),n=>n.some(u.pending)?s.pending:n.some(u.idle)?s.idle:s.done),d=D([a,l],n=>n.every(u.done)),f=v(async()=>t.enable?await t.enable(p,p):!0),E=v(async()=>{p[t.id]=(await t.start(p,p)).api});A({clock:f.doneData,fn:n=>n?s.pending:s.off,target:t.$status}),A({clock:f.failData,fn:()=>s.fail,target:t.$status}),A({clock:t.$status,filter:u.pending,target:E}),A({clock:E.finally,fn:n=>n.status,target:t.$status}),a.watch(n=>{(u.off(n)||u.fail(n))&&w(t.$status,n)}),d.watch(n=>{n&&f()}),y=[...y,a,l,d,f,E]})),new Promise((t,a)=>{i.watch(l=>{if(l.done===!0){p={},y.forEach(f=>b(f,{deep:!0}));let d={hasErrors:l.hasErrors,statuses:l.statuses};l.hasErrors&&a(d),t(d)}})})};var $={up:g,graph:O};export{$ as compose,P as createContainer}; |
{ | ||
"name": "@grlt-hub/app-compose", | ||
"version": "1.0.0", | ||
"version": "1.1.0", | ||
"type": "module", | ||
@@ -5,0 +5,0 @@ "private": false, |
@@ -7,8 +7,10 @@ # App Compose | ||
Modern applications require a modular structure to ensure **flexibility** and adapt to **business needs**. Building a truly modular system inevitably brings the challenge of bringing all components together. In this process, it’s critical to manage the **connection flow**: loading one module first, then another, while controlling their dependencies. Complexity increases when it becomes necessary to disable parts of the system **without leaving traces** in the code (such as `if/else` statements) and **without disrupting** the operation of other components. | ||
Modern applications thrive on modular architecture, adapting seamlessly to evolving business needs. To achieve **true modularity**, though, you need more than just independent components—you need an **efficient** way to bring them together. This means **controlling** how modules load, in what order, and with which dependencies. It gets even trickier when you want to turn off parts of the system **without any traces** in the code, like `if/else` statements, and without affecting the **stability** of other components. | ||
`app-compose` provides the ability not only to flexibly **disable modules on demand** but also to completely exclude their code from loading when they are turned off. Moreover, all dependencies of a disabled module are also automatically excluded, which helps **save resources** and maintain high **application performance**. | ||
## How does this library solve this problem? | ||
`app-compose` is designed to make this seamless. It lets you dynamically enable or disable modules as needed, not only preventing their code from loading when they’re off but also excluding all related dependencies. This means no excess resource use and optimized **performance**, even as the app grows. | ||
With `app-compose`, you can **scale** your applications effortlessly—whether you’re building a lightweight tool or a complex, feature-rich system. By managing dependency flow and on-demand module loading, `app-compose` provides the control you need to build applications that are both **flexible and robust**. | ||
### Example | ||
@@ -74,5 +76,7 @@ | ||
- Designed with a focus on **quality** and **performance**. | ||
- Weighs less than **1.2 kB**, making it lightweight. | ||
- Weighs less than **1.5 kB**, making it lightweight. | ||
- Covered by **100% tests**, including **type tests**. | ||
- Ensures high performance, suitable for **scalable applications**. | ||
- Includes **debugging tools** to facilitate the development process. | ||
- Offers the ability to **visualize the system** composed of containers effectively (including transitive dependencies and their paths). | ||
- Follows **semantic versioning** (semver), guaranteeing **stability** and **predictability** of changes with each release. | ||
@@ -83,1 +87,6 @@ | ||
For additional information, guides and api reference visit [documentation site](https://grlt-hub.github.io/app-compose/). | ||
## Community | ||
- [Discord](https://discord.gg/Q4DFKnxp) | ||
- [Telegram](https://t.me/grlt_hub_app_compose) |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
13240
102
90