Merge branch 'rewrite' of github.com:twitter/bower into rewrite

This commit is contained in:
Andre Cruz
2013-04-16 00:08:42 +01:00
2 changed files with 643 additions and 75 deletions

193
README.md
View File

@@ -1,45 +1,50 @@
# Bower rewrite
This repository is just an experiment around the new bower rewrite.
It will remain private and only trustworthy people will have access to it.
If the general consensus is to advance with it, the code will move to a new branch on the official repository.
## Why?
Bower codebase is becoming unmanageable, especially at its core.
Main issues are:
- ___Monolithic Package.js that handles all package types (GitHub, url, local, etc).___
- ___Package.js has too many nesting level of callbacks, causing confusion and making it hard to read___
- Some commands, such as install and update, have incorrect behaviour (#200, #256)
- __No separation of concerns. The overall codebase has grown in a patch fashion, which has lead to a bloated and tight coupled solution.__
- __Monolithic Package.js that handles all package types (both local and remote `Git`, URL, local files, etc).__
- __Package.js has a big nesting level of callbacks, causing confusion and making the code hard to read.__
- Some commands, such as install and update, have incorrect behaviour ([#200](https://github.com/twitter/bower/issues/200), [#256](https://github.com/twitter/bower/issues/256))
- This is directly related with the current implementation of bower core: Package.js and Manager.js
- Programmatic usage needs improvement
- Unable to spawn multiple commands in parallel in different folders
- Some commands simply do not fire the `end` event
- Others fire the `error` event many times
- Some commands should fire more meaningful events (e.g.: install should fire each installed package)
## Solution
The rewrite will give a chance to make bower more manageable, solving the issues mentioned above while also improving the overall codebase. Readable code is crucial to increase the number of contributors and to the success of bower.
## Main goals
Solutions to the main issues:
- Ease the process of gathering more contributors.
- Clear architecture and separation of concerns.
- Installation/update speedup.
- Named endpoints on the CLI install.
- Offline installation of packages, thanks to the cache.
- Ability to easily add package types (`SVN`, etc).
- Support for commit hashes and branches in targets for `Git` endpoints.
- Improved output after installation/update.
- Integrate with update-notifier and yeomen insight.
- Polymorphism can be used to make different kind of packages share a common API. Different kind of package resolvers will be implemented separately but will share common functionality. This will be further explained bellow.
- Promises could solve the nesting problem found in the codebase.
- **TODO: COMPLETE HERE**
## Implementation details
### Term dictionary
- **Canonical package:** A folder containing all the files that belong to a package. May include a `bower.json` file inside. (typically what gets installed)
- **Source:** URL, git endpoint, etc.
- **Target:** `semver` range, commit hash, branch (indicates a version).
- **Endpoint:** source#target
- **Named endpoint:** name@endpoint#target
- **UoW:** Unit of Work
- **Components folder:** The folder in which components are installed (`bower_components` by default).
### Overall strategy
For the sake of simplicity, keep the following terms in mind:
- **UoW:** Unit of Work
- **Canonical endpoint:** The form in which a dependency endpoint is declared in the `bower.json` file.
- **Dep tuple (dependency tuple):** A data structure composed of an endpoint string (url, git repo, etc) and a *semver* compatible version.
- **Dep tuple range**: Same as *dep tuple*, but version can contain a range. Used mainly to specify a set of compatible versions.
![Really nicely drawn architecture diagram](http://f.cl.ly/items/1E0R2w2P1z3e1V2w1X23/bower_architecture.jpg "Really nicely drawn architecture diagram")
![Really nicely drawn architecture diagram](http://f.cl.ly/items/44271M0R1O012H2m4234/resolve_diagram.png "Don't over think it! We already did! :P")
Bower is composed of the following components:
@@ -47,40 +52,74 @@ Bower is composed of the following components:
- `.bowerrc`: Allows for customisations of Bower behaviour at the project/user level.
- `bower.json`: Main purpose is to declare the component dependencies and other component related information.
- `Manager`: Main coordinator, responsible for:
- Deciding which version of the dependencies should be resolved while keeping every dependant compatible, and queueing those dependencies in the `UoW`.
- Tracking which dependencies have been resolved, and which ones failed to resolve.
- Caching resolved dependencies into `ResolveCache`.
- Requesting `Uow` to fail-fast, in case it realises there is no resolution for the current dependency tree.
- `ResolveCache`: Keeps a cache of previously resolved *dep tuples*. Lookup can be done using a *dep tuple range*.
- Checking which packages are already installed in the current `bower folder`.
- Deciding which version of the dependencies should be fetched from the `PackageRepository`, while keeping every dependant compatible (note that the `Manager` is `server` aware).
- Tracking which dependencies have been fetched, which ones failed to fetch, and which ones are being fetched.
- Requesting the `PackageRepository` to fail-fast, in case it realises there is no resolution for the current dependency tree.
- `PackageRepository`: Abstraction to the underlying complexity of heterogeneous source types. Responsible for:
- Storing new entries in `ResolveCache`.
- Queueing resolvers into the `UoW`, if no suitable entry is found in the `ResolveCache`.
- `ResolveCache`: Keeps a cache of previously resolved endpoints. Lookup can be done using an endpoint.
- `UnitOfWork`: Work coordinator, responsible for:
- Keeping track of which *dep tuples* are being resolved.
- Limiting amount of parallel resolutions
- **QUESTION:** I see that *dep tuples* with same endpoint and different versions are not supposed to be processed in parallel. Not sure why, I don't see any problem with it. I even see a potential optimisation, in which the `Manager` realises that something that is being resolved will never be compatible, and aborts that specific resolution, and then queues another version (not a must for an initial version, but something to keep in mind).
- `ResolverFactory`: Parses a *dep tuple* and returns a `Resolver` capable of resolving the endpoint type.
- Keeping track of which resolvers are being resolved.
- Limiting amount of parallel resolutions.
- `ResolverFactory`: Parses an endpoint and returns a `Resolver` capable of resolving the source type.
- `Resolver`: Base resolver, which can be extended by concrete resolvers, like `UrlResolver`, `GitRemoteResolver`, etc.
Here's an overview of the resolution process (pseudo-algorithm):
You can find additional details about each of these components below, in [Architecture components details](#architecture-components-details).
1. `Manager` is requested to install a set of *dep tuple ranges* (may have come from `bower.json`, through the CLI, or even asked to install programatically).
2. **START OF RESOLUTION CYCLE**
3. `Manager` checks if `UoW` is currently resolving any of the *dep tuple ranges* and, if it is, no need to re-enqueue.
4. `Manager` queries `ResolveCache` for any cached resolution for the unresolved *dep tuple ranges*.
5. If there is no cache miss, use cached results and **RESOLUTION CYCLE IS DONE**.
6. Else, queue *dep tuple ranges* that are missing in cache into `UoW`.
7. While `UoW` has queued dependencies and parallel limit has not been reached, request `ResolverFactory` to fabricate a `Resolver` suited for each of the queued dependencies, and start their resolution (if limit has been reached, wait before starting resolution).
8. Every time a `Resolver` is done, the `UoW` notifies the `Manager`.
9. `Manager` caches the result in `ResolveCache`.
10. `Manager` asks the resolved `Resolver` if it has any dependency. Go to **START OF RESOLUTION CYCLE**, and continue process with the unresolved dependencies of the resolved `Resolver`.
### Project / Manager -> EventEmitter
#### Resolve process
Here's an overview of the dependency resolve process:
1. **INSTALL/UPDATE** - A set of named endpoints and/or endpoints is requested to be installed/updated, and these are passed to the `Manager`.
2. **ANALIZE COMPONENTS FOLDER** - `Manager` starts by reading the *components folder* and understanding which packages are already installed.
3. **ENQUEUE ENDPOINTS** - For each endpoint that should be fetched, the `Manager` enqueues the *named endpoints*/endpoints in the `PackageRepository`. Some considerations:
- If a package should be fetched or not depends on the following conditions:
- What operation is being done (install/update).
- If package is already installed.
- If `Manager` has already enqueued that *named endpoint*/endpoint in the current runtime (regardless of the fetch being currently in progress, already complete, or failed).
- Additional flags (force, etc).
4. **FABRICATE RESOLVERS** - For each of the endpoints, the `PackageRepository` requests the `ResolverFactory` for suitable resolvers, capable of handling the source type. Some considerations:
- The factory method takes the source string as its main argument, and is also provided with target, and package name if possible (all this information is extracted from the *named endpoint*/endpoint).
- This method is asynchronous, in order to allow for I/O operations to happen, without blocking the whole process (e.g., querying registry, etc).
- There is a runtime internal cache of sources that have already been analysed, and what type of `Resolver` resulted from that analysis. This speeds up the decision process, particularly for aliases (registered packages), and published packages, which would required HTTP requests.
5. **LOOKUP CACHE** - `PackageRepository` looks up the `ResolveCache` using the endpoint, for a cached *canonical package* that complies to the endpoint target. Some considerations:
- The lookup is performed using an endpoint that is fetched from the `Resolver`. This allows the resolver to guarantee that the endpoint has been normalised (twitter/bootstrap -> git://github.com/twitter/bootstrap.git, etc).
- The `ResolveCache` is `semver` aware. What this means, is that if you try to lookup `~1.2.1`, and the cache has a entries for versions `1.2.3` and `1.2.4`, it will give a hit with `1.2.4`.
6. **CACHE HIT VALIDATION** - At this stage, and only for the cache hits, the `PackageRepository` will question the `Resolver` if there is any version higher than the one fetched from cache that also complies with the endpoint target. Some considerations:
- This step is ignored in case a flag like `offline` is passed.
- How the `Resolver` checks this, depends on the `Resolver` type. (e.g. `GitRemoteResolver` would fetch the git refs with `git ls-remote --tags --heads`, and check if there is a higher version that complies with the target).
- This check should be as quick as possible. If the process of checking a new version is too slow, it's preferable to just assume there is a new version.
- If there is no way to check if there is a higher version, assume that there is.
- If the `Resolver` indicates that the cached version is outdated, then it is treated as a cache miss.
7. **RESOLVE CACHE MISSES** - Any cache miss needs to be resolved, so the `PackageRepository` requests each of the remaining resolvers to resolve, and waits.
8. **CACHE RESOLVED PACKAGES** - As the resolvers complete the resolution, the `PackageRepository` stores the canonic packages in the `ResolveCache`, along with the source, version, and any additional information that the `Resolver` provides. This allows resolvers to store additional details about the fetched package to be used for future *cache hit validations* (e.g. store HTTP expiration headers in the case of the `UrlPackage`).
9. **RETURN PACKAGE TO MANAGER** - The `PackageRepository` returns the canonical package to the `Manager`.
10. **EVALUATE RESOLVED PACKAGE DEPENDENCIES** - The `Manager` checks if the returned canonical packages have a `bower.json` file describing additional dependencies and, if so, continue in point #3. If there are no more unresolved dependencies, finish up the installation procedure.
### Architecture components details
#### Manager -> EventEmitter
TODO
### Resolve Cache
#### PackageRepository
TODO
### Resolve Factory
#### ResolverFactory
Simple function that takes a *dep tuple range* with options and creates an instance of a `Resolver` that obeys the base `Resolver` interface.
@@ -88,16 +127,19 @@ Simple function that takes a *dep tuple range* with options and creates an insta
function createResolver(depTuple, options) -> Promise
```
This function could perform transformations/normalizations to the tuple endpoint.
This function could perform transformations/normalisations to the tuple endpoint.
For instance, if `endpoint` is a shorthand it would expand it.
The function is actually async to allow query the bower registry to know the real endpoint.
### Resolver -> EventEmitter
#### ResolveCache
#### Resolver -> EventEmitter
The `Resolver.js` class extends EventEmitter.
Think of it as an abstract class that implements the resolver interface as well as serving as a base for other resolver types.
#### Events
##### Events
- `name_change`: fired when the name of the package has changed
- `action`: fired to inform the current action being performed by the resolver
@@ -107,7 +149,7 @@ Think of it as an abstract class that implements the resolver interface as well
------------
#### Constructor
##### Constructor
Resolver(depTuple, options)
@@ -120,19 +162,19 @@ Options:
Public functions
#### Resolver#getName() -> String
##### Resolver#getName() -> String
Returns the package name.
#### Resolver#getEndpoint() -> String
##### Resolver#getEndpoint() -> String
Returns the package endpoint.
#### Resolver#getRange() -> String
##### Resolver#getRange() -> String
Returns the semver range it should resolve to.
#### Resolver#getTempDir() -> String
##### Resolver#getTempDir() -> String
Returns the temporary directory that the package is using to resolve itself.
#### Resolver#resolve()
##### Resolver#resolve()
Resolves the package.
The resolve process obeys a very explicit flow:
@@ -142,19 +184,19 @@ The resolve process obeys a very explicit flow:
- When done, calls #_parseJson and waits
- When done, marks the package as resolved and emits the `end` event.
#### Resolver#getResolveError() -> Error
##### Resolver#getResolveError() -> Error
Get the error occurred during the resolve process.
Returns null if no error occurred.
#### Resolver#getJson() -> Object
##### Resolver#getJson() -> Object
Get the package JSON.
Throws an error if the package is not yet resolved.
#### Resolver#getDependencies() -> Array
##### Resolver#getDependencies() -> Array
Get an array of packages that are direct dependencies of the package.
Throws an error if the package is not yet resolved.
#### Resolver#install(directory) -> Promise
##### Resolver#install(directory) -> Promise
Installs the package into the specified directory.
The base implementation simply renames the temporary directory to the install directory.
If the install directory already exists, it will be deleted unless it is some kind of repository.
@@ -165,26 +207,26 @@ Throws an error if the package is not yet resolved.
Protected functions
#### Resolver#_createTempDir() -> Promise
##### Resolver#_createTempDir() -> Promise
Creates a temporary dir.
### Resolver#_readJson() -> Promise
##### Resolver#_readJson() -> Promise
Reads `bower.json`, possibly by using a dedicated `read-json` package that will be available in the Bower organization. It will ensure everything is valid.
### Resolver#_parseJson(json) -> Promise
##### Resolver#_parseJson(json) -> Promise
Parses the json:
- Checks if the packages name is different from the json one. If so and if the name was "guessed", the name of the package will be updated and a `name_change` event will be emited.
- Checks if the packages name is different from the json one. If so and if the name was "guessed", the name of the package will be updated and a `name_change` event will be emitted.
- Deletes files that are specified in the `ignore` property of the json from the temporary directory.
--------
Abstract functions that must be implemented by concrete resolvers.
#### Resolver#_resolveSelf() -> Promise
##### Resolver#_resolveSelf() -> Promise
Resolves self. This method should be implemented by the concrete resolvers. For instance, the UrlPackage would download the contents of a URL into the temporary directory.
### Types of Resolvers
#### Types of Resolvers
The following resolvers will extend from `Resolver.js` and will obey its interface.
@@ -192,18 +234,19 @@ The following resolvers will extend from `Resolver.js` and will obey its interfa
- `UrlResolver` extends `Resolver` (dependencies pointing to downloadable resources)
- `GitFsResolver` extends `Resolver` (git dependencies available in the local file system)
- `GitRemoteResolver` extends `Resolver` or `GitFsResolver` (remote git dependencies)
- `PublishedResolver` extends `Resolver` (? makes sense if bower supports a publish model, just like npm).
- `PublishedResolver` extends `Resolver` (? makes sense if bower supports a publish model, just like `npm`).
These type of resolvers will be known and created (instantiated) by the `ResolverFactory`.
This architecture will make it very easy for the community to create others package types, for instance, a `MercurialLocalPackage`, `MercurialRemotePackage`, `SvnResolver`, etc.
### Unit of work -> EventEmitter
------------
#### Events
#### Unit of work -> EventEmitter
##### Events
- `enqueue`: fired when a package is enqueued
- `dequeue`: fired when a package is dequeued
@@ -215,7 +258,7 @@ With these events, it will be possible to track the current status of each packa
------------
#### Constructor
##### Constructor
UnitOfWork(options)
@@ -224,25 +267,25 @@ Options:
- `maxConcurrent`: maximum number of concurrent resolvers running (defaults to 5)
- `failFast`: true to fail-fast if an error occurred while resolving a package (defaults to true)
#### UnitOfWork#enqueue(depTuple) -> Promise
##### UnitOfWork#enqueue(depTuple) -> Promise
**WON'T TOUCH FROM HERE DOWN. SINCE A LOT HAS BEEN CHANGED, I WONDER IF THE PROMISES ARE REALLY NECESSARY FOR WHAT WE'RE TRYING TO ACCOMPLISH HERE. LET'S DISCUSS THIS TOMORROW**
Enqueues a resolver to be ran.
The promise is fulfilled when the package is accepted to be resolved or is rejected if the unit of work is doomed to fail.
When fullfilled, a `done` function is passed that should be called when the resolve process of the package is finished:
When fulfilled, a `done` function is passed that should be called when the resolve process of the package is finished:
Throws an error if the package is already queued or being resolved.
- If the package failed resolving, it should be called with an instance of `Error`. In that case, the package will be marked as failed and all the remaining enqueued packages will have the `enqueue` promise rejected, making the whole process to fail-fast.
- If the packages succeed resolving, it should be called with no arguments. In that case, the package will be marked as resolved
#### UnitOfWork#dequeue(package) -> Itself
##### UnitOfWork#dequeue(package) -> Itself
Removes a previously enqueued package.
#### UnitOfWork#getResolved(name) -> Itself
##### UnitOfWork#getResolved(name) -> Itself
Returns an array of resolved packages whose names are `name`.
When called without a name, returns an object with all the resolved packages.
#### UnitOfWork#getFailed(name) -> Itself
Returns an array of packages that failed to resulve whose names are `name`.
##### UnitOfWork#getFailed(name) -> Itself
Returns an array of packages that failed to resolve whose names are `name`.
When called without a name, returns an object with all the failed packages.

525
resolve_diagram.graphml Normal file
View File

@@ -0,0 +1,525 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<graphml xmlns="http://graphml.graphdrawing.org/xmlns" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:y="http://www.yworks.com/xml/graphml" xmlns:yed="http://www.yworks.com/xml/yed/3" xsi:schemaLocation="http://graphml.graphdrawing.org/xmlns http://www.yworks.com/xml/schema/graphml/1.1/ygraphml.xsd">
<!--Created by yFiles for Java 2.10-->
<key for="graphml" id="d0" yfiles.type="resources"/>
<key for="port" id="d1" yfiles.type="portgraphics"/>
<key for="port" id="d2" yfiles.type="portgeometry"/>
<key for="port" id="d3" yfiles.type="portuserdata"/>
<key attr.name="url" attr.type="string" for="node" id="d4"/>
<key attr.name="description" attr.type="string" for="node" id="d5"/>
<key for="node" id="d6" yfiles.type="nodegraphics"/>
<key attr.name="Description" attr.type="string" for="graph" id="d7"/>
<key attr.name="url" attr.type="string" for="edge" id="d8"/>
<key attr.name="description" attr.type="string" for="edge" id="d9"/>
<key for="edge" id="d10" yfiles.type="edgegraphics"/>
<graph edgedefault="directed" id="G">
<data key="d7"/>
<node id="n0">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="108.0" x="499.9999999999998" y="26.00000000000003"/>
<y:Fill color="#CAE257" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="54.115234375" x="26.9423828125" y="17.43359375">Manager<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n1">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="108.0" x="746.9999999999998" y="-59.0"/>
<y:Fill color="#FF6600" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="22.158203125" x="42.9208984375" y="17.43359375">CLI<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n2">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="487.4999999999998" y="179.50000000000003"/>
<y:Fill color="#BC80E8" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="113.037109375" x="9.9814453125" y="17.43359375">PackageRepository<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n3">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="193.25000000000017" y="179.50000000000003"/>
<y:Fill color="#C0C0C0" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="95.283203125" x="18.8583984375" y="17.43359375">ResolverFactory<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n4">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="769.4999999999998" y="179.50000000000003"/>
<y:Fill color="#FFDF69" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="83.3359375" x="24.83203125" y="17.43359375">ResolveCache<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n5">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="71.0" x="518.4999999999998" y="333.0"/>
<y:Fill color="#FF9900" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="29.951171875" x="20.5244140625" y="17.43359375">UoW<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n6">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="108.0" x="499.9999999999998" y="-148.42089843749997"/>
<y:Fill color="#B3D4F5" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="68.1484375" x="19.92578125" y="17.43359375">bower.json<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n7">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="-39.49999999999952" y="25.50000000000037"/>
<y:Fill color="#F9C6C6" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="113.6875" x="9.65625" y="17.43359375">GitRemoteResolver<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n8">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="-39.49999999999952" y="102.5000000000002"/>
<y:Fill color="#F9C6C6" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="82.216796875" x="25.3916015625" y="17.43359375">GitFsResolver<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n9">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="-39.49999999999952" y="179.50000000000003"/>
<y:Fill color="#F9C6C6" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="109.41015625" x="11.794921875" y="17.43359375">PublishedResolver<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n10">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="-39.49999999999952" y="256.0000000000002"/>
<y:Fill color="#F9C6C6" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="69.73046875" x="31.634765625" y="17.43359375">UrlResolver<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<node id="n11">
<data key="d5"/>
<data key="d6">
<y:GenericNode configuration="ShinyPlateNode">
<y:Geometry height="53.0" width="133.0" x="-39.49999999999952" y="332.5000000000004"/>
<y:Fill color="#F9C6C6" transparent="false"/>
<y:BorderStyle hasColor="false" type="line" width="1.0"/>
<y:NodeLabel alignment="center" autoSizePolicy="content" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasBackgroundColor="false" hasLineColor="false" height="18.1328125" modelName="custom" textColor="#000000" visible="true" width="65.587890625" x="33.7060546875" y="17.43359375">FsResolver<y:LabelModel>
<y:SmartNodeLabelModel distance="4.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartNodeLabelModelParameter labelRatioX="0.0" labelRatioY="0.0" nodeRatioX="0.0" nodeRatioY="0.0" offsetX="0.0" offsetY="0.0" upX="0.0" upY="-1.0"/>
</y:ModelParameter>
</y:NodeLabel>
</y:GenericNode>
</data>
</node>
<edge id="e0" source="n6" target="n1">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="53.96875" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="719.2556762695312" y="-109.09696197509766"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:Arc height="35.141727447509766" ratio="0.6607534289360046" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e1" source="n1" target="n0">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="686.692626953125" y="36.712684631347656"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="46.3984375" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="171.28515625" x="-149.25381663372184" y="8.81762933731077">Named endpoints/endpoints
+
additional settings<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.5" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="28.250164031982422" ratio="0.43259406089782715" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e2" source="n0" target="n2">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="513.0144653320312" y="129.25"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="46.3984375" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="99.326171875" x="-67.3515625" y="27.0555419921875">Enqueue
named endpoint
/ endpoint<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-40.98554611206055" ratio="-1.0680272579193115" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e3" source="n2" target="n0">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="602.2950439453125" y="129.25"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="60.90625" x="-9.6099853515625" y="-66.3875732421875">Canonical
Package<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-48.29507064819336" ratio="-1.2585034370422363" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e4" source="n2" target="n3">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="-66.526611328125" sy="0.0" tx="66.526611328125" ty="0.0">
<y:Point x="406.875" y="184.02462768554688"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="101.904296875" x="-131.550537109375" y="-38.108184814453125">Source
+ additional info<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="76.49234040703737" distanceToCenter="true" position="center" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-21.975372314453125" ratio="-0.5453054904937744" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e5" source="n3" target="n2">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="66.526611328125" sy="0.0" tx="-66.526611328125" ty="0.0">
<y:Point x="406.875" y="235.323974609375"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="53.037109375" x="54.079833984375" y="20.257568359375">Resolver<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="61.69980864509831" distanceToCenter="true" position="center" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-29.323974609375" ratio="-0.7276566028594971" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e6" source="n2" target="n4">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="50.50000000000557" sy="14.499999999998693" tx="-66.526611328125" ty="0.0">
<y:Point x="682.1296997070312" y="157.9893798828125"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="56.013671875" x="33.62109375" y="-55.900421142578125">Lookup
endpoint<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="33.32058818736142" distanceToCenter="false" position="center" ratio="0.0" segment="-1"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="55.473655700683594" ratio="1.3398674726486206" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e7" source="n4" target="n2">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="-66.526611328125" sy="0.0" tx="66.526611328125" ty="0.0">
<y:Point x="695.0" y="205.02296447753906"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="18.1328125" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="53.974609375" x="-101.460693359375" y="-10.043441772460938">Hit/miss<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="6.283185307179586" distance="60.16303464791759" distanceToCenter="true" position="center" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-0.9770392179489136" ratio="-0.026238612830638885" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e8" source="n2" target="n5">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="520.375" y="282.75"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="53.76953125" x="-41.79681396484375" y="34.1219482421875">Enqueue
Resolver<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-33.625" ratio="-0.8762214779853821" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e9" source="n5" target="n2">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="590.625" y="282.75"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="55.6796875" x="-11.59735107421875" y="-66.3875732421875">Resolved
Resolver<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="0.0" segment="-1"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-36.625" ratio="-0.9543973803520203" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e10" source="n2" target="n4">
<data key="d9"/>
<data key="d10">
<y:ArcEdge>
<y:Path sx="66.52301487651081" sy="10.136170353159002" tx="-66.526611328125" ty="0.0">
<y:Point x="697.0289916992188" y="240.91098022460938"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:EdgeLabel alignment="center" backgroundColor="#FFFFFF" configuration="AutoFlippingLabel" distance="2.0" fontFamily="Dialog" fontSize="12" fontStyle="plain" hasLineColor="false" height="32.265625" modelName="custom" preferredPlacement="anywhere" ratio="0.5" textColor="#000000" visible="true" width="92.46484375" x="30.2735595703125" y="8.641998291015625">Store canonical
package<y:LabelModel>
<y:SmartEdgeLabelModel autoRotationEnabled="false" defaultAngle="0.0" defaultDistance="10.0"/>
</y:LabelModel>
<y:ModelParameter>
<y:SmartEdgeLabelModelParameter angle="0.0" distance="30.0" distanceToCenter="true" position="center" ratio="1.0" segment="0"/>
</y:ModelParameter>
<y:PreferredPlacementDescriptor angle="0.0" angleOffsetOnRightSide="0" angleReference="absolute" angleRotationOnRightSide="co" distance="-1.0" frozen="true" placement="anywhere" side="anywhere" sideReference="relative_to_edge_flow"/>
</y:EdgeLabel>
<y:Arc height="-29.911914825439453" ratio="-0.8014184236526489" type="fixedRatio"/>
</y:ArcEdge>
</data>
</edge>
<edge id="e11" source="n3" target="n7">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="151.74999999999977" y="206.00000000000003"/>
<y:Point x="151.74999999999977" y="52.00000000000037"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e12" source="n3" target="n9">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0"/>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e13" source="n3" target="n10">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="151.74999999999977" y="206.00000000000003"/>
<y:Point x="151.74999999999977" y="282.5000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e14" source="n3" target="n11">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="151.74999999999977" y="206.00000000000003"/>
<y:Point x="151.74999999999977" y="359.0000000000004"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
<edge id="e15" source="n3" target="n8">
<data key="d9"/>
<data key="d10">
<y:PolyLineEdge>
<y:Path sx="0.0" sy="0.0" tx="0.0" ty="0.0">
<y:Point x="151.74999999999977" y="206.00000000000003"/>
<y:Point x="151.74999999999977" y="129.0000000000002"/>
</y:Path>
<y:LineStyle color="#000000" type="line" width="1.0"/>
<y:Arrows source="none" target="standard"/>
<y:BendStyle smoothed="false"/>
</y:PolyLineEdge>
</data>
</edge>
</graph>
<data key="d0">
<y:Resources/>
</data>
</graphml>