mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-11 16:05:44 +01:00
Merge with default
This commit is contained in:
@@ -46,8 +46,11 @@ import java.io.Serializable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
import static java.util.Collections.unmodifiableCollection;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
/**
|
||||
* The FileObject represents a file or a directory in a repository.
|
||||
@@ -90,7 +93,9 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
&& Objects.equal(description, other.description)
|
||||
&& Objects.equal(length, other.length)
|
||||
&& Objects.equal(subRepository, other.subRepository)
|
||||
&& Objects.equal(lastModified, other.lastModified);
|
||||
&& Objects.equal(commitDate, other.commitDate)
|
||||
&& Objects.equal(partialResult, other.partialResult)
|
||||
&& Objects.equal(computationAborted, other.computationAborted);
|
||||
//J+
|
||||
}
|
||||
|
||||
@@ -100,8 +105,16 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
@Override
|
||||
public int hashCode()
|
||||
{
|
||||
return Objects.hashCode(name, path, directory, description, length,
|
||||
subRepository, lastModified);
|
||||
return Objects.hashCode(
|
||||
name,
|
||||
path,
|
||||
directory,
|
||||
description,
|
||||
length,
|
||||
subRepository,
|
||||
commitDate,
|
||||
partialResult,
|
||||
computationAborted);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -118,7 +131,9 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
.add("description", description)
|
||||
.add("length", length)
|
||||
.add("subRepository", subRepository)
|
||||
.add("lastModified", lastModified)
|
||||
.add("commitDate", commitDate)
|
||||
.add("partialResult", partialResult)
|
||||
.add("computationAborted", computationAborted)
|
||||
.toString();
|
||||
//J+
|
||||
}
|
||||
@@ -130,35 +145,44 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
* if the repository provider is not able to get the last commit for the path.
|
||||
*
|
||||
*
|
||||
* @return last commit message
|
||||
* @return Last commit message or <code>null</code>, when this value has not been computed
|
||||
* (see {@link #isPartialResult()}).
|
||||
*/
|
||||
public String getDescription()
|
||||
public Optional<String> getDescription()
|
||||
{
|
||||
return description;
|
||||
return ofNullable(description);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the last commit date for this. The method will return null,
|
||||
* if the repository provider is not able to get the last commit for the path.
|
||||
* if the repository provider is not able to get the last commit for the path
|
||||
* or it has not been computed.
|
||||
*
|
||||
*
|
||||
* @return last commit date
|
||||
*/
|
||||
@Override
|
||||
public Long getLastModified()
|
||||
{
|
||||
return lastModified;
|
||||
public Long getLastModified() {
|
||||
return this.isPartialResult()? null: this.commitDate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the file.
|
||||
*
|
||||
*
|
||||
* @return length of file
|
||||
* Returns the last commit date for this. The method will return {@link OptionalLong#empty()},
|
||||
* if the repository provider is not able to get the last commit for the path or if this value has not been computed
|
||||
* (see {@link #isPartialResult()} and {@link #isComputationAborted()}).
|
||||
*/
|
||||
public long getLength()
|
||||
public OptionalLong getCommitDate()
|
||||
{
|
||||
return length;
|
||||
return commitDate == null? OptionalLong.empty(): OptionalLong.of(commitDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the length of the file or {@link OptionalLong#empty()}, when this value has not been computed
|
||||
* (see {@link #isPartialResult()} and {@link #isComputationAborted()}).
|
||||
*/
|
||||
public OptionalLong getLength()
|
||||
{
|
||||
return length == null? OptionalLong.empty(): OptionalLong.of(length);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -200,7 +224,7 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Return sub repository informations or null if the file is not
|
||||
* Return sub repository information or null if the file is not
|
||||
* sub repository.
|
||||
*
|
||||
* @since 1.10
|
||||
@@ -222,6 +246,42 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
return directory;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the children of this file.
|
||||
*
|
||||
* @return The children of this file if it is a directory.
|
||||
*/
|
||||
public Collection<FileObject> getChildren() {
|
||||
return children == null? null: unmodifiableCollection(children);
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is <code>true</code>, some values for this object have not been computed, yet. These values (like
|
||||
* {@link #getLength()}, {@link #getDescription()} or {@link #getCommitDate()})
|
||||
* will return {@link Optional#empty()} (or {@link OptionalLong#empty()} respectively), unless they are computed.
|
||||
* There may be an asynchronous task running, that will set these values in the future.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @return <code>true</code>, whenever some values of this object have not been computed, yet.
|
||||
*/
|
||||
public boolean isPartialResult() {
|
||||
return partialResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* If this is <code>true</code>, some values for this object have not been computed and will not be computed. These
|
||||
* values (like {@link #getLength()}, {@link #getDescription()} or {@link #getCommitDate()})
|
||||
* will return {@link Optional#empty()} (or {@link OptionalLong#empty()} respectively), unless they are computed.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @return <code>true</code>, whenever some values of this object finally are not computed.
|
||||
*/
|
||||
public boolean isComputationAborted() {
|
||||
return computationAborted;
|
||||
}
|
||||
|
||||
//~--- set methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -247,14 +307,14 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the last modified date of the file.
|
||||
* Sets the commit date of the file.
|
||||
*
|
||||
*
|
||||
* @param lastModified last modified date
|
||||
* @param commitDate commit date
|
||||
*/
|
||||
public void setLastModified(Long lastModified)
|
||||
public void setCommitDate(Long commitDate)
|
||||
{
|
||||
this.lastModified = lastModified;
|
||||
this.commitDate = commitDate;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -263,7 +323,7 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
*
|
||||
* @param length file length
|
||||
*/
|
||||
public void setLength(long length)
|
||||
public void setLength(Long length)
|
||||
{
|
||||
this.length = length;
|
||||
}
|
||||
@@ -302,22 +362,47 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
this.subRepository = subRepository;
|
||||
}
|
||||
|
||||
public Collection<FileObject> getChildren() {
|
||||
return unmodifiableCollection(children);
|
||||
/**
|
||||
* Set marker, that some values for this object are not computed, yet.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param partialResult Set this to <code>true</code>, whenever some values of this object are not computed, yet.
|
||||
*/
|
||||
public void setPartialResult(boolean partialResult) {
|
||||
this.partialResult = partialResult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set marker, that computation of some values for this object has been aborted.
|
||||
*
|
||||
* @since 2.0.0
|
||||
*
|
||||
* @param computationAborted Set this to <code>true</code>, whenever some values of this object are not computed and
|
||||
* will not be computed in the future.
|
||||
*/
|
||||
public void setComputationAborted(boolean computationAborted) {
|
||||
this.computationAborted = computationAborted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the children for this file.
|
||||
*
|
||||
* @param children The new childre.
|
||||
*/
|
||||
public void setChildren(List<FileObject> children) {
|
||||
this.children = new ArrayList<>(children);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a child to the list of children .
|
||||
*
|
||||
* @param child The additional child.
|
||||
*/
|
||||
public void addChild(FileObject child) {
|
||||
this.children.add(child);
|
||||
}
|
||||
|
||||
public boolean hasChildren() {
|
||||
return !children.isEmpty();
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** file description */
|
||||
@@ -326,11 +411,11 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
/** directory indicator */
|
||||
private boolean directory;
|
||||
|
||||
/** last modified date */
|
||||
private Long lastModified;
|
||||
/** commit date */
|
||||
private Long commitDate;
|
||||
|
||||
/** file length */
|
||||
private long length;
|
||||
private Long length;
|
||||
|
||||
/** filename */
|
||||
private String name;
|
||||
@@ -338,9 +423,16 @@ public class FileObject implements LastModifiedAware, Serializable
|
||||
/** file path */
|
||||
private String path;
|
||||
|
||||
/** Marker for partial result. */
|
||||
private boolean partialResult = false;
|
||||
|
||||
/** Marker for aborted computation. */
|
||||
private boolean computationAborted = false;
|
||||
|
||||
/** sub repository informations */
|
||||
@XmlElement(name = "subrepository")
|
||||
private SubRepository subRepository;
|
||||
|
||||
/** Children of this file (aka directory). */
|
||||
private Collection<FileObject> children = new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -300,6 +300,13 @@ public final class BrowseCommandBuilder
|
||||
return this;
|
||||
}
|
||||
|
||||
private void updateCache(BrowserResult updatedResult) {
|
||||
if (!disableCache) {
|
||||
CacheKey key = new CacheKey(repository, request);
|
||||
cache.put(key, updatedResult);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- inner classes --------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -416,5 +423,5 @@ public final class BrowseCommandBuilder
|
||||
private final Repository repository;
|
||||
|
||||
/** request for the command */
|
||||
private final BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
private final BrowseCommandRequest request = new BrowseCommandRequest(this::updateCache);
|
||||
}
|
||||
|
||||
@@ -37,6 +37,10 @@ package sonia.scm.repository.spi;
|
||||
|
||||
import com.google.common.base.MoreObjects;
|
||||
import com.google.common.base.Objects;
|
||||
import sonia.scm.repository.BrowserResult;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
@@ -48,6 +52,14 @@ public final class BrowseCommandRequest extends FileBaseCommandRequest
|
||||
/** Field description */
|
||||
private static final long serialVersionUID = 7956624623516803183L;
|
||||
|
||||
public BrowseCommandRequest() {
|
||||
this(null);
|
||||
}
|
||||
|
||||
public BrowseCommandRequest(Consumer<BrowserResult> updater) {
|
||||
this.updater = updater;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
/**
|
||||
@@ -220,6 +232,12 @@ public final class BrowseCommandRequest extends FileBaseCommandRequest
|
||||
return recursive;
|
||||
}
|
||||
|
||||
public void updateCache(BrowserResult update) {
|
||||
if (updater != null) {
|
||||
updater.accept(update);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** disable last commit */
|
||||
@@ -230,4 +248,8 @@ public final class BrowseCommandRequest extends FileBaseCommandRequest
|
||||
|
||||
/** browse file objects recursive */
|
||||
private boolean recursive = false;
|
||||
|
||||
// WARNING / TODO: This field creates a reverse channel from the implementation to the API. This will break
|
||||
// whenever the API runs in a different process than the SPI (for example to run explicit hosts for git repositories).
|
||||
private final transient Consumer<BrowserResult> updater;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import java.util.function.Consumer;
|
||||
|
||||
/**
|
||||
* Tasks submitted to this executor will be run synchronously up to a given time, after which they will be queued and
|
||||
* processed asynchronously. After a maximum amount of time consumed by these tasks, they will be skipped. Note that
|
||||
* this only works for short-living tasks.
|
||||
* <p>
|
||||
* Get instances of this using a {@link SyncAsyncExecutorProvider}.
|
||||
*/
|
||||
public interface SyncAsyncExecutor {
|
||||
|
||||
/**
|
||||
* Execute the given task (either synchronously or asynchronously). If this task is skipped due to
|
||||
* timeouts, nothing will be done.
|
||||
*
|
||||
* @param task The {@link Runnable} to be executed.
|
||||
* @return Either {@link ExecutionType#SYNCHRONOUS} when the given {@link Runnable} has been executed immediately or
|
||||
* {@link ExecutionType#ASYNCHRONOUS}, when the task was queued to be executed asynchronously in the future.
|
||||
*/
|
||||
default ExecutionType execute(Runnable task) {
|
||||
return execute(
|
||||
ignored -> task.run(),
|
||||
() -> {}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given <code>task</code> (either synchronously or asynchronously). If this task is
|
||||
* skipped due to timeouts, the <code>abortionFallback</code> will be called.
|
||||
*
|
||||
* @param task The {@link Runnable} to be executed.
|
||||
* @param abortionFallback This will only be run, when this and all remaining tasks are aborted. This task should
|
||||
* only consume a negligible amount of time.
|
||||
* @return Either {@link ExecutionType#SYNCHRONOUS} when the given {@link Runnable} has been executed immediately or
|
||||
* {@link ExecutionType#ASYNCHRONOUS}, when the task was queued to be executed asynchronously in the future.
|
||||
*/
|
||||
default ExecutionType execute(Runnable task, Runnable abortionFallback) {
|
||||
return execute(ignored -> task.run(), abortionFallback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given <code>task</code> (either synchronously or asynchronously). If this task is skipped due to
|
||||
* timeouts, nothing will be done.
|
||||
*
|
||||
* @param task The {@link Consumer} to be executed. The parameter given to this is either
|
||||
* {@link ExecutionType#SYNCHRONOUS} when the given {@link Consumer} is executed immediately
|
||||
* or {@link ExecutionType#ASYNCHRONOUS}, when the task had been queued and now is executed
|
||||
* asynchronously.
|
||||
* @return Either {@link ExecutionType#SYNCHRONOUS} when the given {@link Runnable} has been executed immediately or
|
||||
* {@link ExecutionType#ASYNCHRONOUS}, when the task was queued to be executed asynchronously in the future.
|
||||
*/
|
||||
default ExecutionType execute(Consumer<ExecutionType> task) {
|
||||
return execute(task, () -> {});
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the given <code>task</code> (either synchronously or asynchronously). If this task is
|
||||
* skipped due to timeouts, the <code>abortionFallback</code> will be called.
|
||||
*
|
||||
* @param task The {@link Consumer} to be executed. The parameter given to this is either
|
||||
* {@link ExecutionType#SYNCHRONOUS} when the given {@link Consumer} is executed immediately
|
||||
* or {@link ExecutionType#ASYNCHRONOUS}, when the task had been queued and now is executed
|
||||
* asynchronously.
|
||||
* @param abortionFallback This will only be run, when this and all remaining tasks are aborted. This task should
|
||||
* only consume a negligible amount of time.
|
||||
* @return Either {@link ExecutionType#SYNCHRONOUS} when the given {@link Runnable} has been executed immediately or
|
||||
* {@link ExecutionType#ASYNCHRONOUS}, when the task was queued to be executed asynchronously in the future.
|
||||
*/
|
||||
ExecutionType execute(Consumer<ExecutionType> task, Runnable abortionFallback);
|
||||
|
||||
/**
|
||||
* When all submitted tasks have been executed synchronously, this will return <code>true</code>. If at least one task
|
||||
* has been enqueued to be executed asynchronously, this returns <code>false</code> (even when none of the enqueued
|
||||
* tasks have been run, yet).
|
||||
*/
|
||||
boolean hasExecutedAllSynchronously();
|
||||
|
||||
enum ExecutionType {
|
||||
SYNCHRONOUS, ASYNCHRONOUS
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
/**
|
||||
* Use this provider to get {@link SyncAsyncExecutor} instances to execute a number of normally short-lived tasks, that
|
||||
* should be run asynchronously (or even be skipped) whenever they take too long in summary.
|
||||
* <p>
|
||||
* The goal of this is a "best effort" approach: The submitted tasks are run immediately when they are submitted, unless
|
||||
* a given timespan (<code>switchToAsyncInSeconds</code>) has passed. From this moment on the tasks are put into a queue to be
|
||||
* processed asynchronously. If even then they take too long and their accumulated asynchronous runtime exceeds another
|
||||
* limit (<code>maxAsyncAbortSeconds</code>), the tasks are skipped.
|
||||
* <p>
|
||||
* Note that whenever a task has been started either synchronously or asynchronously it will neither be terminated nor
|
||||
* switched from foreground to background execution, so this will only work well for short-living tasks. A long running
|
||||
* task can still block this for longer than the configured amount of seconds.
|
||||
*/
|
||||
public interface SyncAsyncExecutorProvider {
|
||||
|
||||
int DEFAULT_SWITCH_TO_ASYNC_IN_SECONDS = 2;
|
||||
|
||||
/**
|
||||
* Creates an {@link SyncAsyncExecutor} that will run tasks synchronously for
|
||||
* {@link #DEFAULT_SWITCH_TO_ASYNC_IN_SECONDS} seconds. The limit of asynchronous runtime is implementation dependant.
|
||||
*
|
||||
* @return The executor.
|
||||
*/
|
||||
default SyncAsyncExecutor createExecutorWithDefaultTimeout() {
|
||||
return createExecutorWithSecondsToTimeout(DEFAULT_SWITCH_TO_ASYNC_IN_SECONDS);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates an {@link SyncAsyncExecutor} that will run tasks synchronously for
|
||||
* <code>switchToAsyncInSeconds</code> seconds. The limit of asynchronous runtime is implementation dependant.
|
||||
*
|
||||
* @param switchToAsyncInSeconds The amount of seconds submitted tasks will be run synchronously. After this time,
|
||||
* further tasks will be run asynchronously. To run all tasks asynchronously no matter
|
||||
* what, set this to <code>0</code>.
|
||||
* @return The executor.
|
||||
*/
|
||||
SyncAsyncExecutor createExecutorWithSecondsToTimeout(int switchToAsyncInSeconds);
|
||||
|
||||
/**
|
||||
* Creates an {@link SyncAsyncExecutor} that will run tasks synchronously for
|
||||
* <code>switchToAsyncInSeconds</code> seconds and will abort tasks after they ran
|
||||
* <code>maxAsyncAbortSeconds</code> asynchronously.
|
||||
*
|
||||
* @param switchToAsyncInSeconds The amount of seconds submitted tasks will be run synchronously. After this time,
|
||||
* further tasks will be run asynchronously. To run all tasks asynchronously no matter
|
||||
* what, set this to <code>0</code>.
|
||||
* @param maxAsyncAbortSeconds The amount of seconds, tasks that were started asynchronously may run in summary
|
||||
* before remaining tasks will not be executed at all anymore. To abort all tasks that
|
||||
* are submitted after <code>switchToAsyncInSeconds</code> immediately, set this to
|
||||
* <code>0</code>.
|
||||
* @return The executor.
|
||||
*/
|
||||
SyncAsyncExecutor createExecutorWithSecondsToTimeout(int switchToAsyncInSeconds, int maxAsyncAbortSeconds);
|
||||
}
|
||||
@@ -745,6 +745,10 @@ public final class GitUtil
|
||||
public static Optional<LfsPointer> getLfsPointer(org.eclipse.jgit.lib.Repository repo, String path, RevCommit commit, TreeWalk treeWalk) throws IOException {
|
||||
Attributes attributes = LfsFactory.getAttributesForPath(repo, path, commit);
|
||||
|
||||
return getLfsPointer(repo, treeWalk, attributes);
|
||||
}
|
||||
|
||||
public static Optional<LfsPointer> getLfsPointer(org.eclipse.jgit.lib.Repository repo, TreeWalk treeWalk, Attributes attributes) throws IOException {
|
||||
Attribute filter = attributes.get("filter");
|
||||
if (filter != null && "lfs".equals(filter.getValue())) {
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
|
||||
@@ -35,9 +35,11 @@ package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.base.Stopwatch;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.eclipse.jgit.attributes.Attributes;
|
||||
import org.eclipse.jgit.lfs.LfsPointer;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
@@ -49,6 +51,7 @@ import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import org.eclipse.jgit.treewalk.filter.TreeFilter;
|
||||
import org.eclipse.jgit.util.LfsFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.NotFoundException;
|
||||
@@ -56,6 +59,7 @@ import sonia.scm.repository.BrowserResult;
|
||||
import sonia.scm.repository.FileObject;
|
||||
import sonia.scm.repository.GitSubModuleParser;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.InternalRepositoryException;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.SubRepository;
|
||||
import sonia.scm.store.Blob;
|
||||
@@ -69,10 +73,13 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType.ASYNCHRONOUS;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
@@ -90,71 +97,56 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
/**
|
||||
* the logger for GitBrowseCommand
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(GitBrowseCommand.class);
|
||||
private static final Logger logger = LoggerFactory.getLogger(GitBrowseCommand.class);
|
||||
|
||||
/** sub repository cache */
|
||||
private final Map<ObjectId, Map<String, SubRepository>> subrepositoryCache = Maps.newHashMap();
|
||||
|
||||
private final Object asyncMonitor = new Object();
|
||||
|
||||
private final LfsBlobStoreFactory lfsBlobStoreFactory;
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
private final SyncAsyncExecutor executor;
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
* @param context
|
||||
* @param repository
|
||||
* @param lfsBlobStoreFactory
|
||||
*/
|
||||
public GitBrowseCommand(GitContext context, Repository repository, LfsBlobStoreFactory lfsBlobStoreFactory)
|
||||
{
|
||||
private BrowserResult browserResult;
|
||||
|
||||
public GitBrowseCommand(GitContext context, Repository repository, LfsBlobStoreFactory lfsBlobStoreFactory, SyncAsyncExecutor executor) {
|
||||
super(context, repository);
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@Override
|
||||
@SuppressWarnings("unchecked")
|
||||
public BrowserResult getBrowserResult(BrowseCommandRequest request)
|
||||
throws IOException {
|
||||
logger.debug("try to create browse result for {}", request);
|
||||
|
||||
BrowserResult result;
|
||||
|
||||
org.eclipse.jgit.lib.Repository repo = open();
|
||||
ObjectId revId;
|
||||
ObjectId revId = computeRevIdToBrowse(request, repo);
|
||||
|
||||
if (Util.isEmpty(request.getRevision()))
|
||||
{
|
||||
revId = getDefaultBranch(repo);
|
||||
if (revId != null) {
|
||||
browserResult = new BrowserResult(revId.getName(), request.getRevision(), getEntry(repo, request, revId));
|
||||
return browserResult;
|
||||
} else {
|
||||
logger.warn("could not find head of repository {}, empty?", repository.getNamespaceAndName());
|
||||
return new BrowserResult(Constants.HEAD, request.getRevision(), createEmptyRoot());
|
||||
}
|
||||
else
|
||||
{
|
||||
revId = GitUtil.getRevisionId(repo, request.getRevision());
|
||||
}
|
||||
|
||||
if (revId != null)
|
||||
{
|
||||
result = new BrowserResult(revId.getName(), request.getRevision(), getEntry(repo, request, revId));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (Util.isNotEmpty(request.getRevision()))
|
||||
{
|
||||
private ObjectId computeRevIdToBrowse(BrowseCommandRequest request, org.eclipse.jgit.lib.Repository repo) throws IOException {
|
||||
if (Util.isEmpty(request.getRevision())) {
|
||||
return getDefaultBranch(repo);
|
||||
} else {
|
||||
ObjectId revId = GitUtil.getRevisionId(repo, request.getRevision());
|
||||
if (revId == null) {
|
||||
logger.error("could not find revision {}", request.getRevision());
|
||||
throw notFound(entity("Revision", request.getRevision()).in(this.repository));
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("could not find head of repository, empty?");
|
||||
return revId;
|
||||
}
|
||||
}
|
||||
|
||||
result = new BrowserResult(Constants.HEAD, request.getRevision(), createEmtpyRoot());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
//~--- methods --------------------------------------------------------------
|
||||
|
||||
private FileObject createEmtpyRoot() {
|
||||
private FileObject createEmptyRoot() {
|
||||
FileObject fileObject = new FileObject();
|
||||
fileObject.setName("");
|
||||
fileObject.setPath("");
|
||||
@@ -162,18 +154,6 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
return fileObject;
|
||||
}
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
* @param repo
|
||||
* @param request
|
||||
* @param revId
|
||||
* @param treeWalk
|
||||
*
|
||||
* @return
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
private FileObject createFileObject(org.eclipse.jgit.lib.Repository repo,
|
||||
BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk)
|
||||
throws IOException {
|
||||
@@ -207,128 +187,63 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
// don't show message and date for directories to improve performance
|
||||
if (!file.isDirectory() &&!request.isDisableLastCommit())
|
||||
{
|
||||
logger.trace("fetch last commit for {} at {}", path, revId.getName());
|
||||
RevCommit commit = getLatestCommit(repo, revId, path);
|
||||
|
||||
Optional<LfsPointer> lfsPointer = commit == null? empty(): GitUtil.getLfsPointer(repo, path, commit, treeWalk);
|
||||
file.setPartialResult(true);
|
||||
RevCommit commit;
|
||||
try (RevWalk walk = new RevWalk(repo)) {
|
||||
commit = walk.parseCommit(revId);
|
||||
}
|
||||
Optional<LfsPointer> lfsPointer = getLfsPointer(repo, path, commit, treeWalk);
|
||||
|
||||
if (lfsPointer.isPresent()) {
|
||||
BlobStore lfsBlobStore = lfsBlobStoreFactory.getLfsBlobStore(repository);
|
||||
String oid = lfsPointer.get().getOid().getName();
|
||||
Blob blob = lfsBlobStore.get(oid);
|
||||
if (blob == null) {
|
||||
logger.error("lfs blob for lob id {} not found in lfs store of repository {}", oid, repository.getNamespaceAndName());
|
||||
file.setLength(-1);
|
||||
} else {
|
||||
file.setLength(blob.getSize());
|
||||
}
|
||||
setFileLengthFromLfsBlob(lfsPointer.get(), file);
|
||||
} else {
|
||||
file.setLength(loader.getSize());
|
||||
}
|
||||
|
||||
if (commit != null)
|
||||
{
|
||||
file.setLastModified(GitUtil.getCommitTime(commit));
|
||||
file.setDescription(commit.getShortMessage());
|
||||
}
|
||||
else if (logger.isWarnEnabled())
|
||||
{
|
||||
logger.warn("could not find latest commit for {} on {}", path,
|
||||
revId);
|
||||
}
|
||||
executor.execute(
|
||||
new CompleteFileInformation(path, revId, repo, file, request),
|
||||
new AbortFileInformation(request)
|
||||
);
|
||||
}
|
||||
}
|
||||
return file;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Method description
|
||||
*
|
||||
*
|
||||
*
|
||||
* @param repo
|
||||
* @param revId
|
||||
* @param path
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
private RevCommit getLatestCommit(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revId, String path)
|
||||
{
|
||||
RevCommit result = null;
|
||||
RevWalk walk = null;
|
||||
|
||||
try
|
||||
{
|
||||
walk = new RevWalk(repo);
|
||||
walk.setTreeFilter(AndTreeFilter.create(PathFilter.create(path),
|
||||
TreeFilter.ANY_DIFF));
|
||||
|
||||
RevCommit commit = walk.parseCommit(revId);
|
||||
|
||||
walk.markStart(commit);
|
||||
result = Util.getFirst(walk);
|
||||
}
|
||||
catch (IOException ex)
|
||||
{
|
||||
logger.error("could not parse commit for file", ex);
|
||||
}
|
||||
finally
|
||||
{
|
||||
GitUtil.release(walk);
|
||||
}
|
||||
|
||||
return result;
|
||||
private void updateCache(BrowseCommandRequest request) {
|
||||
request.updateCache(browserResult);
|
||||
logger.info("updated browser result for repository {}", repository.getNamespaceAndName());
|
||||
}
|
||||
|
||||
private FileObject getEntry(org.eclipse.jgit.lib.Repository repo, BrowseCommandRequest request, ObjectId revId) throws IOException {
|
||||
RevWalk revWalk = null;
|
||||
TreeWalk treeWalk = null;
|
||||
|
||||
FileObject result;
|
||||
|
||||
try {
|
||||
try (RevWalk revWalk = new RevWalk(repo); TreeWalk treeWalk = new TreeWalk(repo)) {
|
||||
logger.debug("load repository browser for revision {}", revId.name());
|
||||
|
||||
treeWalk = new TreeWalk(repo);
|
||||
if (!isRootRequest(request)) {
|
||||
treeWalk.setFilter(PathFilter.create(request.getPath()));
|
||||
}
|
||||
revWalk = new RevWalk(repo);
|
||||
|
||||
RevTree tree = revWalk.parseTree(revId);
|
||||
|
||||
if (tree != null)
|
||||
{
|
||||
if (tree != null) {
|
||||
treeWalk.addTree(tree);
|
||||
}
|
||||
else
|
||||
{
|
||||
} else {
|
||||
throw new IllegalStateException("could not find tree for " + revId.name());
|
||||
}
|
||||
|
||||
if (isRootRequest(request)) {
|
||||
result = createEmtpyRoot();
|
||||
FileObject result = createEmptyRoot();
|
||||
findChildren(result, repo, request, revId, treeWalk);
|
||||
return result;
|
||||
} else {
|
||||
result = findFirstMatch(repo, request, revId, treeWalk);
|
||||
FileObject result = findFirstMatch(repo, request, revId, treeWalk);
|
||||
if ( result.isDirectory() ) {
|
||||
treeWalk.enterSubtree();
|
||||
findChildren(result, repo, request, revId, treeWalk);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
finally
|
||||
{
|
||||
GitUtil.release(revWalk);
|
||||
GitUtil.release(treeWalk);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isRootRequest(BrowseCommandRequest request) {
|
||||
return Strings.isNullOrEmpty(request.getPath()) || "/".equals(request.getPath());
|
||||
@@ -384,56 +299,144 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
throw notFound(entity("File", request.getPath()).in("Revision", revId.getName()).in(this.repository));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String,
|
||||
SubRepository> getSubRepositories(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revision)
|
||||
private Map<String, SubRepository> getSubRepositories(org.eclipse.jgit.lib.Repository repo, ObjectId revision)
|
||||
throws IOException {
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
logger.debug("read submodules of {} at {}", repository.getName(),
|
||||
revision);
|
||||
}
|
||||
|
||||
Map<String, SubRepository> subRepositories;
|
||||
try ( ByteArrayOutputStream baos = new ByteArrayOutputStream() )
|
||||
{
|
||||
logger.debug("read submodules of {} at {}", repository.getName(), revision);
|
||||
|
||||
try ( ByteArrayOutputStream baos = new ByteArrayOutputStream() ) {
|
||||
new GitCatCommand(context, repository, lfsBlobStoreFactory).getContent(repo, revision,
|
||||
PATH_MODULES, baos);
|
||||
subRepositories = GitSubModuleParser.parse(baos.toString());
|
||||
return GitSubModuleParser.parse(baos.toString());
|
||||
} catch (NotFoundException ex) {
|
||||
logger.trace("could not find .gitmodules: {}", ex.getMessage());
|
||||
return Collections.emptyMap();
|
||||
}
|
||||
catch (NotFoundException ex)
|
||||
{
|
||||
logger.trace("could not find .gitmodules", ex);
|
||||
subRepositories = Collections.EMPTY_MAP;
|
||||
}
|
||||
|
||||
return subRepositories;
|
||||
}
|
||||
|
||||
private SubRepository getSubRepository(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revId, String path)
|
||||
private SubRepository getSubRepository(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path)
|
||||
throws IOException {
|
||||
Map<String, SubRepository> subRepositories = subrepositoryCache.get(revId);
|
||||
|
||||
if (subRepositories == null)
|
||||
{
|
||||
if (subRepositories == null) {
|
||||
subRepositories = getSubRepositories(repo, revId);
|
||||
subrepositoryCache.put(revId, subRepositories);
|
||||
}
|
||||
|
||||
SubRepository sub = null;
|
||||
|
||||
if (subRepositories != null)
|
||||
{
|
||||
sub = subRepositories.get(path);
|
||||
if (subRepositories != null) {
|
||||
return subRepositories.get(path);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
return sub;
|
||||
private Optional<LfsPointer> getLfsPointer(org.eclipse.jgit.lib.Repository repo, String path, RevCommit commit, TreeWalk treeWalk) {
|
||||
try {
|
||||
Attributes attributes = LfsFactory.getAttributesForPath(repo, path, commit);
|
||||
|
||||
return GitUtil.getLfsPointer(repo, treeWalk, attributes);
|
||||
} catch (IOException e) {
|
||||
throw new InternalRepositoryException(repository, "could not read lfs pointer", e);
|
||||
}
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** sub repository cache */
|
||||
private final Map<ObjectId, Map<String, SubRepository>> subrepositoryCache = Maps.newHashMap();
|
||||
private void setFileLengthFromLfsBlob(LfsPointer lfsPointer, FileObject file) {
|
||||
BlobStore lfsBlobStore = lfsBlobStoreFactory.getLfsBlobStore(repository);
|
||||
String oid = lfsPointer.getOid().getName();
|
||||
Blob blob = lfsBlobStore.get(oid);
|
||||
if (blob == null) {
|
||||
logger.error("lfs blob for lob id {} not found in lfs store of repository {}", oid, repository.getNamespaceAndName());
|
||||
file.setLength(null);
|
||||
} else {
|
||||
file.setLength(blob.getSize());
|
||||
}
|
||||
}
|
||||
|
||||
private class CompleteFileInformation implements Consumer<SyncAsyncExecutor.ExecutionType> {
|
||||
private final String path;
|
||||
private final ObjectId revId;
|
||||
private final org.eclipse.jgit.lib.Repository repo;
|
||||
private final FileObject file;
|
||||
private final BrowseCommandRequest request;
|
||||
|
||||
public CompleteFileInformation(String path, ObjectId revId, org.eclipse.jgit.lib.Repository repo, FileObject file, BrowseCommandRequest request) {
|
||||
this.path = path;
|
||||
this.revId = revId;
|
||||
this.repo = repo;
|
||||
this.file = file;
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void accept(SyncAsyncExecutor.ExecutionType executionType) {
|
||||
logger.trace("fetch last commit for {} at {}", path, revId.getName());
|
||||
|
||||
Stopwatch sw = Stopwatch.createStarted();
|
||||
|
||||
Optional<RevCommit> commit = getLatestCommit(repo, revId, path);
|
||||
|
||||
synchronized (asyncMonitor) {
|
||||
file.setPartialResult(false);
|
||||
if (commit.isPresent()) {
|
||||
applyValuesFromCommit(executionType, commit.get());
|
||||
} else {
|
||||
logger.warn("could not find latest commit for {} on {}", path, revId);
|
||||
}
|
||||
}
|
||||
|
||||
logger.trace("finished loading of last commit {} of {} in {}", revId.getName(), path, sw.stop());
|
||||
}
|
||||
|
||||
private Optional<RevCommit> getLatestCommit(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revId, String path) {
|
||||
try (RevWalk walk = new RevWalk(repo)) {
|
||||
walk.setTreeFilter(AndTreeFilter.create(TreeFilter.ANY_DIFF, PathFilter.create(path)));
|
||||
|
||||
RevCommit commit = walk.parseCommit(revId);
|
||||
|
||||
walk.markStart(commit);
|
||||
return of(Util.getFirst(walk));
|
||||
} catch (IOException ex) {
|
||||
logger.error("could not parse commit for file", ex);
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
|
||||
private void applyValuesFromCommit(SyncAsyncExecutor.ExecutionType executionType, RevCommit commit) {
|
||||
file.setCommitDate(GitUtil.getCommitTime(commit));
|
||||
file.setDescription(commit.getShortMessage());
|
||||
if (executionType == ASYNCHRONOUS && browserResult != null) {
|
||||
updateCache(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class AbortFileInformation implements Runnable {
|
||||
private final BrowseCommandRequest request;
|
||||
|
||||
public AbortFileInformation(BrowseCommandRequest request) {
|
||||
this.request = request;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
synchronized (asyncMonitor) {
|
||||
if (markPartialAsAborted(browserResult.getFile())) {
|
||||
updateCache(request);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean markPartialAsAborted(FileObject file) {
|
||||
boolean changed = false;
|
||||
if (file.isPartialResult()) {
|
||||
file.setPartialResult(false);
|
||||
file.setComputationAborted(true);
|
||||
changed = true;
|
||||
}
|
||||
for (FileObject child : file.getChildren()) {
|
||||
changed |= markPartialAsAborted(child);
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,12 +80,13 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory, HookContextFactory hookContextFactory, ScmEventBus eventBus) {
|
||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory, HookContextFactory hookContextFactory, ScmEventBus eventBus, SyncAsyncExecutorProvider executorProvider) {
|
||||
this.handler = handler;
|
||||
this.repository = repository;
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
this.hookContextFactory = hookContextFactory;
|
||||
this.eventBus = eventBus;
|
||||
this.executorProvider = executorProvider;
|
||||
this.context = new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider);
|
||||
}
|
||||
|
||||
@@ -150,7 +151,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
@Override
|
||||
public BrowseCommand getBrowseCommand()
|
||||
{
|
||||
return new GitBrowseCommand(context, repository, lfsBlobStoreFactory);
|
||||
return new GitBrowseCommand(context, repository, lfsBlobStoreFactory, executorProvider.createExecutorWithDefaultTimeout());
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -301,4 +302,6 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
private final HookContextFactory hookContextFactory;
|
||||
|
||||
private final ScmEventBus eventBus;
|
||||
|
||||
private final SyncAsyncExecutorProvider executorProvider;
|
||||
}
|
||||
|
||||
@@ -55,14 +55,16 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||
private final LfsBlobStoreFactory lfsBlobStoreFactory;
|
||||
private final HookContextFactory hookContextFactory;
|
||||
private final ScmEventBus eventBus;
|
||||
private final SyncAsyncExecutorProvider executorProvider;
|
||||
|
||||
@Inject
|
||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory, HookContextFactory hookContextFactory, ScmEventBus eventBus) {
|
||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory, HookContextFactory hookContextFactory, ScmEventBus eventBus, SyncAsyncExecutorProvider executorProvider) {
|
||||
this.handler = handler;
|
||||
this.storeProvider = storeProvider;
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
this.hookContextFactory = hookContextFactory;
|
||||
this.eventBus = eventBus;
|
||||
this.executorProvider = executorProvider;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -70,7 +72,7 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||
GitRepositoryServiceProvider provider = null;
|
||||
|
||||
if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
||||
provider = new GitRepositoryServiceProvider(handler, repository, storeProvider, lfsBlobStoreFactory, hookContextFactory, eventBus);
|
||||
provider = new GitRepositoryServiceProvider(handler, repository, storeProvider, lfsBlobStoreFactory, hookContextFactory, eventBus, executorProvider);
|
||||
}
|
||||
|
||||
return provider;
|
||||
|
||||
@@ -35,15 +35,21 @@ import org.junit.Test;
|
||||
import sonia.scm.repository.BrowserResult;
|
||||
import sonia.scm.repository.FileObject;
|
||||
import sonia.scm.repository.GitRepositoryConfig;
|
||||
import sonia.scm.repository.spi.SyncAsyncExecutors.AsyncExecutorStepper;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Collection;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertNotNull;
|
||||
import static org.junit.Assert.assertNull;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutors.stepperAsynchronousExecutor;
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutors.synchronousExecutor;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GitBrowseCommand}.
|
||||
@@ -102,15 +108,55 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase {
|
||||
assertFalse(a.isDirectory());
|
||||
assertEquals("a.txt", a.getName());
|
||||
assertEquals("a.txt", a.getPath());
|
||||
assertEquals("added new line for blame", a.getDescription());
|
||||
assertTrue(a.getLength() > 0);
|
||||
checkDate(a.getLastModified());
|
||||
assertEquals("added new line for blame", a.getDescription().get());
|
||||
assertTrue(a.getLength().getAsLong() > 0);
|
||||
checkDate(a.getCommitDate().getAsLong());
|
||||
|
||||
assertTrue(c.isDirectory());
|
||||
assertEquals("c", c.getName());
|
||||
assertEquals("c", c.getPath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testAsynchronousBrowse() throws IOException {
|
||||
try (AsyncExecutorStepper executor = stepperAsynchronousExecutor()) {
|
||||
GitBrowseCommand command = new GitBrowseCommand(createContext(), repository, null, executor);
|
||||
List<BrowserResult> updatedResults = new LinkedList<>();
|
||||
BrowseCommandRequest request = new BrowseCommandRequest(updatedResults::add);
|
||||
FileObject root = command.getBrowserResult(request).getFile();
|
||||
assertNotNull(root);
|
||||
|
||||
Collection<FileObject> foList = root.getChildren();
|
||||
|
||||
FileObject a = findFile(foList, "a.txt");
|
||||
FileObject b = findFile(foList, "b.txt");
|
||||
|
||||
assertTrue(a.isPartialResult());
|
||||
assertFalse("expected empty name before commit could have been read", a.getDescription().isPresent());
|
||||
assertFalse("expected empty date before commit could have been read", a.getCommitDate().isPresent());
|
||||
assertTrue(b.isPartialResult());
|
||||
assertFalse("expected empty name before commit could have been read", b.getDescription().isPresent());
|
||||
assertFalse("expected empty date before commit could have been read", b.getCommitDate().isPresent());
|
||||
|
||||
executor.next();
|
||||
|
||||
assertEquals(1, updatedResults.size());
|
||||
assertFalse(a.isPartialResult());
|
||||
assertNotNull("expected correct name after commit could have been read", a.getDescription());
|
||||
assertTrue("expected correct date after commit could have been read", a.getCommitDate().isPresent());
|
||||
assertTrue(b.isPartialResult());
|
||||
assertFalse("expected empty name before commit could have been read", b.getDescription().isPresent());
|
||||
assertFalse("expected empty date before commit could have been read", b.getCommitDate().isPresent());
|
||||
|
||||
executor.next();
|
||||
|
||||
assertEquals(2, updatedResults.size());
|
||||
assertFalse(b.isPartialResult());
|
||||
assertNotNull("expected correct name after commit could have been read", b.getDescription());
|
||||
assertTrue("expected correct date after commit could have been read", b.getCommitDate().isPresent());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBrowseSubDirectory() throws IOException {
|
||||
BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
@@ -129,20 +175,20 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase {
|
||||
assertFalse(d.isDirectory());
|
||||
assertEquals("d.txt", d.getName());
|
||||
assertEquals("c/d.txt", d.getPath());
|
||||
assertEquals("added file d and e in folder c", d.getDescription());
|
||||
assertTrue(d.getLength() > 0);
|
||||
checkDate(d.getLastModified());
|
||||
assertEquals("added file d and e in folder c", d.getDescription().get());
|
||||
assertTrue(d.getLength().getAsLong() > 0);
|
||||
checkDate(d.getCommitDate().getAsLong());
|
||||
|
||||
assertFalse(e.isDirectory());
|
||||
assertEquals("e.txt", e.getName());
|
||||
assertEquals("c/e.txt", e.getPath());
|
||||
assertEquals("added file d and e in folder c", e.getDescription());
|
||||
assertTrue(e.getLength() > 0);
|
||||
checkDate(e.getLastModified());
|
||||
assertEquals("added file d and e in folder c", e.getDescription().get());
|
||||
assertTrue(e.getLength().getAsLong() > 0);
|
||||
checkDate(e.getCommitDate().getAsLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecusive() throws IOException {
|
||||
public void testRecursive() throws IOException {
|
||||
BrowseCommandRequest request = new BrowseCommandRequest();
|
||||
|
||||
request.setRecursive(true);
|
||||
@@ -171,6 +217,6 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase {
|
||||
}
|
||||
|
||||
private GitBrowseCommand createCommand() {
|
||||
return new GitBrowseCommand(createContext(), repository, null);
|
||||
return new GitBrowseCommand(createContext(), repository, null, synchronousExecutor());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -231,13 +231,13 @@ public class HgFileviewCommand extends AbstractCommand
|
||||
file.setName(getNameFromPath(path));
|
||||
file.setPath(path);
|
||||
file.setDirectory(false);
|
||||
file.setLength(stream.decimalIntUpTo(' '));
|
||||
file.setLength((long) stream.decimalIntUpTo(' '));
|
||||
|
||||
DateTime timestamp = stream.dateTimeUpTo(' ');
|
||||
String description = stream.textUpTo('\0');
|
||||
|
||||
if (!disableLastCommit) {
|
||||
file.setLastModified(timestamp.getDate().getTime());
|
||||
file.setCommitDate(timestamp.getDate().getTime());
|
||||
file.setDescription(description);
|
||||
}
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
FileObject file = new HgBrowseCommand(cmdContext, repository).getBrowserResult(request).getFile();
|
||||
assertEquals("a.txt", file.getName());
|
||||
assertFalse(file.isDirectory());
|
||||
assertTrue(file.getChildren().isEmpty());
|
||||
assertTrue(file.getChildren() == null || file.getChildren().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,9 +73,9 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
assertFalse(a.isDirectory());
|
||||
assertEquals("a.txt", a.getName());
|
||||
assertEquals("a.txt", a.getPath());
|
||||
assertEquals("added new line for blame", a.getDescription());
|
||||
assertTrue(a.getLength() > 0);
|
||||
checkDate(a.getLastModified());
|
||||
assertEquals("added new line for blame", a.getDescription().get());
|
||||
assertTrue(a.getLength().getAsLong() > 0);
|
||||
checkDate(a.getCommitDate().getAsLong());
|
||||
assertTrue(c.isDirectory());
|
||||
assertEquals("c", c.getName());
|
||||
assertEquals("c", c.getPath());
|
||||
@@ -132,16 +132,16 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
assertFalse(d.isDirectory());
|
||||
assertEquals("d.txt", d.getName());
|
||||
assertEquals("c/d.txt", d.getPath());
|
||||
assertEquals("added file d and e in folder c", d.getDescription());
|
||||
assertTrue(d.getLength() > 0);
|
||||
checkDate(d.getLastModified());
|
||||
assertEquals("added file d and e in folder c", d.getDescription().get());
|
||||
assertTrue(d.getLength().getAsLong() > 0);
|
||||
checkDate(d.getCommitDate().getAsLong());
|
||||
assertNotNull(e);
|
||||
assertFalse(e.isDirectory());
|
||||
assertEquals("e.txt", e.getName());
|
||||
assertEquals("c/e.txt", e.getPath());
|
||||
assertEquals("added file d and e in folder c", e.getDescription());
|
||||
assertTrue(e.getLength() > 0);
|
||||
checkDate(e.getLastModified());
|
||||
assertEquals("added file d and e in folder c", e.getDescription().get());
|
||||
assertTrue(e.getLength().getAsLong() > 0);
|
||||
checkDate(e.getCommitDate().getAsLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -154,8 +154,8 @@ public class HgBrowseCommandTest extends AbstractHgCommandTestBase {
|
||||
|
||||
FileObject a = getFileObject(foList, "a.txt");
|
||||
|
||||
assertNull(a.getDescription());
|
||||
assertNull(a.getLastModified());
|
||||
assertFalse(a.getDescription().isPresent());
|
||||
assertFalse(a.getCommitDate().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -173,7 +173,7 @@ public class SvnBrowseCommand extends AbstractSvnCommand
|
||||
{
|
||||
if (entry.getDate() != null)
|
||||
{
|
||||
fileObject.setLastModified(entry.getDate().getTime());
|
||||
fileObject.setCommitDate(entry.getDate().getTime());
|
||||
}
|
||||
|
||||
fileObject.setDescription(entry.getCommitMessage());
|
||||
|
||||
@@ -60,7 +60,7 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase
|
||||
FileObject file = createCommand().getBrowserResult(request).getFile();
|
||||
assertEquals("a.txt", file.getName());
|
||||
assertFalse(file.isDirectory());
|
||||
assertTrue(file.getChildren().isEmpty());
|
||||
assertTrue(file.getChildren() == null || file.getChildren().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -73,9 +73,9 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase
|
||||
assertFalse(a.isDirectory());
|
||||
assertEquals("a.txt", a.getName());
|
||||
assertEquals("a.txt", a.getPath());
|
||||
assertEquals("added line for blame test", a.getDescription());
|
||||
assertTrue(a.getLength() > 0);
|
||||
checkDate(a.getLastModified());
|
||||
assertEquals("added line for blame test", a.getDescription().get());
|
||||
assertTrue(a.getLength().getAsLong() > 0);
|
||||
checkDate(a.getCommitDate().getAsLong());
|
||||
assertTrue(c.isDirectory());
|
||||
assertEquals("c", c.getName());
|
||||
assertEquals("c", c.getPath());
|
||||
@@ -122,16 +122,16 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase
|
||||
assertFalse(d.isDirectory());
|
||||
assertEquals("d.txt", d.getName());
|
||||
assertEquals("c/d.txt", d.getPath());
|
||||
assertEquals("added d and e in folder c", d.getDescription());
|
||||
assertTrue(d.getLength() > 0);
|
||||
checkDate(d.getLastModified());
|
||||
assertEquals("added d and e in folder c", d.getDescription().get());
|
||||
assertTrue(d.getLength().getAsLong() > 0);
|
||||
checkDate(d.getCommitDate().getAsLong());
|
||||
assertNotNull(e);
|
||||
assertFalse(e.isDirectory());
|
||||
assertEquals("e.txt", e.getName());
|
||||
assertEquals("c/e.txt", e.getPath());
|
||||
assertEquals("added d and e in folder c", e.getDescription());
|
||||
assertTrue(e.getLength() > 0);
|
||||
checkDate(e.getLastModified());
|
||||
assertEquals("added d and e in folder c", e.getDescription().get());
|
||||
assertTrue(e.getLength().getAsLong() > 0);
|
||||
checkDate(e.getCommitDate().getAsLong());
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -144,8 +144,8 @@ public class SvnBrowseCommandTest extends AbstractSvnCommandTestBase
|
||||
|
||||
FileObject a = getFileObject(foList, "a.txt");
|
||||
|
||||
assertNull(a.getDescription());
|
||||
assertNull(a.getLastModified());
|
||||
assertFalse(a.getDescription().isPresent());
|
||||
assertFalse(a.getCommitDate().isPresent());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -0,0 +1,107 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.Semaphore;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType.ASYNCHRONOUS;
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType.SYNCHRONOUS;
|
||||
|
||||
public final class SyncAsyncExecutors {
|
||||
|
||||
public static SyncAsyncExecutor synchronousExecutor() {
|
||||
return new SyncAsyncExecutor() {
|
||||
@Override
|
||||
public ExecutionType execute(Consumer<ExecutionType> runnable, Runnable abortionFallback) {
|
||||
runnable.accept(SYNCHRONOUS);
|
||||
return SYNCHRONOUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExecutedAllSynchronously() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static SyncAsyncExecutor asynchronousExecutor() {
|
||||
|
||||
Executor executor = Executors.newSingleThreadExecutor();
|
||||
|
||||
return new SyncAsyncExecutor() {
|
||||
@Override
|
||||
public ExecutionType execute(Consumer<ExecutionType> runnable, Runnable abortionFallback) {
|
||||
executor.execute(() -> runnable.accept(ASYNCHRONOUS));
|
||||
return ASYNCHRONOUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExecutedAllSynchronously() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public static AsyncExecutorStepper stepperAsynchronousExecutor() {
|
||||
return new AsyncExecutorStepper() {
|
||||
|
||||
Executor executor = Executors.newSingleThreadExecutor();
|
||||
Semaphore enterSemaphore = new Semaphore(0);
|
||||
Semaphore exitSemaphore = new Semaphore(0);
|
||||
boolean timedOut = false;
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
enterSemaphore.release(Integer.MAX_VALUE/2);
|
||||
exitSemaphore.release(Integer.MAX_VALUE/2);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ExecutionType execute(Consumer<ExecutionType> runnable, Runnable abortionFallback) {
|
||||
executor.execute(() -> {
|
||||
try {
|
||||
enterSemaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
if (timedOut) {
|
||||
abortionFallback.run();
|
||||
} else {
|
||||
runnable.accept(ASYNCHRONOUS);
|
||||
exitSemaphore.release();
|
||||
}
|
||||
});
|
||||
return ASYNCHRONOUS;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void next() {
|
||||
enterSemaphore.release();
|
||||
try {
|
||||
exitSemaphore.acquire();
|
||||
} catch (InterruptedException e) {
|
||||
Thread.currentThread().interrupt();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void timeout() {
|
||||
timedOut = true;
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean hasExecutedAllSynchronously() {
|
||||
return true;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public interface AsyncExecutorStepper extends SyncAsyncExecutor, Closeable {
|
||||
void next();
|
||||
|
||||
void timeout();
|
||||
}
|
||||
}
|
||||
@@ -8,8 +8,8 @@
|
||||
"private": false,
|
||||
"prettier": "@scm-manager/prettier-config",
|
||||
"dependencies": {
|
||||
"@typescript-eslint/eslint-plugin": "^2.4.0",
|
||||
"@typescript-eslint/parser": "^2.4.0",
|
||||
"@typescript-eslint/eslint-plugin": "^2.12.0",
|
||||
"@typescript-eslint/parser": "^2.12.0",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"eslint": "^6.5.1",
|
||||
"eslint-config-prettier": "^6.4.0",
|
||||
|
||||
@@ -37,8 +37,6 @@
|
||||
"enzyme-context": "^1.1.2",
|
||||
"enzyme-context-react-router-4": "^2.0.0",
|
||||
"fetch-mock": "^7.5.1",
|
||||
"flow-bin": "^0.109.0",
|
||||
"flow-typed": "^2.5.1",
|
||||
"raf": "^3.4.0",
|
||||
"react-test-renderer": "^16.10.2",
|
||||
"storybook-addon-i18next": "^1.2.1",
|
||||
|
||||
@@ -2559,6 +2559,8 @@ exports[`Storyshots Table|Table TextColumn 1`] = `
|
||||
</table>
|
||||
`;
|
||||
|
||||
exports[`Storyshots Toast Click to close 1`] = `null`;
|
||||
|
||||
exports[`Storyshots Toast Danger 1`] = `null`;
|
||||
|
||||
exports[`Storyshots Toast Info 1`] = `null`;
|
||||
|
||||
@@ -241,7 +241,7 @@ class ApiClient {
|
||||
es.addEventListener(type, listeners[type]);
|
||||
}
|
||||
|
||||
return es.close;
|
||||
return () => es.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import React, { FC, useContext } from "react";
|
||||
import React, { FC, useContext, MouseEvent } from "react";
|
||||
import { ToastThemeContext, Themeable } from "./themes";
|
||||
import styled from "styled-components";
|
||||
|
||||
type Props = {
|
||||
icon?: string;
|
||||
onClick?: () => void;
|
||||
};
|
||||
|
||||
const ThemedButton = styled.div.attrs(props => ({
|
||||
const ThemedButton = styled.button.attrs(props => ({
|
||||
className: "button"
|
||||
}))<Themeable>`
|
||||
color: ${props => props.theme.primary};
|
||||
@@ -25,10 +26,18 @@ const ToastButtonIcon = styled.i`
|
||||
margin-right: 0.25rem;
|
||||
`;
|
||||
|
||||
const ToastButton: FC<Props> = ({ icon, children }) => {
|
||||
const ToastButton: FC<Props> = ({ icon, onClick, children }) => {
|
||||
const theme = useContext(ToastThemeContext);
|
||||
|
||||
const handleClick = (e: MouseEvent<HTMLButtonElement>) => {
|
||||
if (onClick) {
|
||||
e.preventDefault();
|
||||
onClick();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ThemedButton theme={theme}>
|
||||
<ThemedButton theme={theme} onClick={handleClick}>
|
||||
{icon && <ToastButtonIcon className={`fas fa-fw fa-${icon}`} />} {children}
|
||||
</ThemedButton>
|
||||
);
|
||||
|
||||
@@ -26,7 +26,31 @@ const Animator = () => {
|
||||
);
|
||||
};
|
||||
|
||||
const Closeable = () => {
|
||||
const [show, setShow] = useState(true);
|
||||
|
||||
const hide = () => {
|
||||
setShow(false);
|
||||
};
|
||||
|
||||
if (!show) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Toast type="success" title="Awesome feature">
|
||||
<p>Close the message with a click</p>
|
||||
<ToastButtons>
|
||||
<ToastButton icon="times" onClick={hide}>
|
||||
Click to close
|
||||
</ToastButton>
|
||||
</ToastButtons>
|
||||
</Toast>
|
||||
);
|
||||
};
|
||||
|
||||
toastStories.add("Open/Close", () => <Animator />);
|
||||
toastStories.add("Click to close", () => <Closeable />);
|
||||
|
||||
types.forEach(type => {
|
||||
toastStories.add(type.charAt(0).toUpperCase() + type.slice(1), () => (
|
||||
|
||||
@@ -13,9 +13,11 @@ export type File = {
|
||||
directory: boolean;
|
||||
description?: string;
|
||||
revision: string;
|
||||
length: number;
|
||||
lastModified?: string;
|
||||
length?: number;
|
||||
commitDate?: string;
|
||||
subRepository?: SubRepository; // TODO
|
||||
partialResult: boolean;
|
||||
computationAborted: boolean;
|
||||
_links: Links;
|
||||
_embedded: {
|
||||
children: File[] | null | undefined;
|
||||
|
||||
@@ -42,8 +42,6 @@
|
||||
"@types/styled-components": "^4.1.19",
|
||||
"@types/systemjs": "^0.20.6",
|
||||
"fetch-mock": "^7.5.1",
|
||||
"flow-bin": "^0.109.0",
|
||||
"flow-typed": "^2.6.1",
|
||||
"react-test-renderer": "^16.10.2",
|
||||
"redux-mock-store": "^1.5.3"
|
||||
},
|
||||
|
||||
@@ -101,7 +101,9 @@
|
||||
"length": "Größe",
|
||||
"lastModified": "Zuletzt bearbeitet",
|
||||
"description": "Beschreibung",
|
||||
"branch": "Branch"
|
||||
"branch": "Branch",
|
||||
"notYetComputed": "Noch nicht berechnet; Der Wert wird in Kürze aktualisiert",
|
||||
"computationAborted": "Die Berechnung dauert zu lange und wurde abgebrochen"
|
||||
},
|
||||
"content": {
|
||||
"historyButton": "History",
|
||||
|
||||
@@ -101,7 +101,9 @@
|
||||
"length": "Length",
|
||||
"lastModified": "Last modified",
|
||||
"description": "Description",
|
||||
"branch": "Branch"
|
||||
"branch": "Branch",
|
||||
"notYetComputed": "Not yet computed, will be updated in a short while",
|
||||
"computationAborted": "The computation took too long and was aborted"
|
||||
},
|
||||
"content": {
|
||||
"historyButton": "History",
|
||||
|
||||
@@ -7,7 +7,7 @@ import styled from "styled-components";
|
||||
import { binder } from "@scm-manager/ui-extensions";
|
||||
import { Repository, File } from "@scm-manager/ui-types";
|
||||
import { ErrorNotification, Loading, Notification } from "@scm-manager/ui-components";
|
||||
import { getFetchSourcesFailure, isFetchSourcesPending, getSources } from "../modules/sources";
|
||||
import { getFetchSourcesFailure, isFetchSourcesPending, getSources, fetchSources } from "../modules/sources";
|
||||
import FileTreeLeaf from "./FileTreeLeaf";
|
||||
|
||||
type Props = WithTranslation & {
|
||||
@@ -19,10 +19,16 @@ type Props = WithTranslation & {
|
||||
path: string;
|
||||
baseUrl: string;
|
||||
|
||||
updateSources: () => void;
|
||||
|
||||
// context props
|
||||
match: any;
|
||||
};
|
||||
|
||||
type State = {
|
||||
stoppableUpdateHandler?: number;
|
||||
};
|
||||
|
||||
const FixedWidthTh = styled.th`
|
||||
width: 16px;
|
||||
`;
|
||||
@@ -39,7 +45,28 @@ export function findParent(path: string) {
|
||||
return "";
|
||||
}
|
||||
|
||||
class FileTree extends React.Component<Props> {
|
||||
class FileTree extends React.Component<Props, State> {
|
||||
constructor(props: Props) {
|
||||
super(props);
|
||||
this.state = {};
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps: Readonly<Props>, prevState: Readonly<State>): void {
|
||||
if (prevState.stoppableUpdateHandler === this.state.stoppableUpdateHandler) {
|
||||
const { tree, updateSources } = this.props;
|
||||
if (tree?._embedded?.children && tree._embedded.children.find(c => c.partialResult)) {
|
||||
const stoppableUpdateHandler = setTimeout(updateSources, 3000);
|
||||
this.setState({ stoppableUpdateHandler: stoppableUpdateHandler });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount(): void {
|
||||
if (this.state.stoppableUpdateHandler) {
|
||||
clearTimeout(this.state.stoppableUpdateHandler);
|
||||
}
|
||||
}
|
||||
|
||||
render() {
|
||||
const { error, loading, tree } = this.props;
|
||||
|
||||
@@ -106,7 +133,7 @@ class FileTree extends React.Component<Props> {
|
||||
<FixedWidthTh />
|
||||
<th>{t("sources.file-tree.name")}</th>
|
||||
<th className="is-hidden-mobile">{t("sources.file-tree.length")}</th>
|
||||
<th className="is-hidden-mobile">{t("sources.file-tree.lastModified")}</th>
|
||||
<th className="is-hidden-mobile">{t("sources.file-tree.commitDate")}</th>
|
||||
<th className="is-hidden-touch">{t("sources.file-tree.description")}</th>
|
||||
{binder.hasExtension("repos.sources.tree.row.right") && <th className="is-hidden-mobile" />}
|
||||
</tr>
|
||||
@@ -123,6 +150,14 @@ class FileTree extends React.Component<Props> {
|
||||
}
|
||||
}
|
||||
|
||||
const mapDispatchToProps = (dispatch: any, ownProps: Props) => {
|
||||
const { repository, revision, path } = ownProps;
|
||||
|
||||
const updateSources = () => dispatch(fetchSources(repository, revision, path, false));
|
||||
|
||||
return { updateSources };
|
||||
};
|
||||
|
||||
const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
const { repository, revision, path } = ownProps;
|
||||
|
||||
@@ -141,5 +176,8 @@ const mapStateToProps = (state: any, ownProps: Props) => {
|
||||
|
||||
export default compose(
|
||||
withRouter,
|
||||
connect(mapStateToProps)
|
||||
connect(
|
||||
mapStateToProps,
|
||||
mapDispatchToProps
|
||||
)
|
||||
)(withTranslation("repos")(FileTree));
|
||||
|
||||
@@ -4,10 +4,12 @@ import classNames from "classnames";
|
||||
import styled from "styled-components";
|
||||
import { binder, ExtensionPoint } from "@scm-manager/ui-extensions";
|
||||
import { File } from "@scm-manager/ui-types";
|
||||
import { DateFromNow, FileSize } from "@scm-manager/ui-components";
|
||||
import { DateFromNow, FileSize, Tooltip } from "@scm-manager/ui-components";
|
||||
import FileIcon from "./FileIcon";
|
||||
import { Icon } from "@scm-manager/ui-components/src";
|
||||
import { WithTranslation, withTranslation } from "react-i18next";
|
||||
|
||||
type Props = {
|
||||
type Props = WithTranslation & {
|
||||
file: File;
|
||||
baseUrl: string;
|
||||
};
|
||||
@@ -35,7 +37,7 @@ export function createLink(base: string, file: File) {
|
||||
return link;
|
||||
}
|
||||
|
||||
export default class FileTreeLeaf extends React.Component<Props> {
|
||||
class FileTreeLeaf extends React.Component<Props> {
|
||||
createLink = (file: File) => {
|
||||
return createLink(this.props.baseUrl, file);
|
||||
};
|
||||
@@ -62,20 +64,42 @@ export default class FileTreeLeaf extends React.Component<Props> {
|
||||
return <Link to={this.createLink(file)}>{file.name}</Link>;
|
||||
};
|
||||
|
||||
contentIfPresent = (file: File, attribute: string, content: (file: File) => any) => {
|
||||
const { t } = this.props;
|
||||
if (file.hasOwnProperty(attribute)) {
|
||||
return content(file);
|
||||
} else if (file.computationAborted) {
|
||||
return (
|
||||
<Tooltip location="top" message={t("sources.file-tree.computationAborted")}>
|
||||
<Icon name={"question-circle"} />
|
||||
</Tooltip>
|
||||
);
|
||||
} else if (file.partialResult) {
|
||||
return (
|
||||
<Tooltip location="top" message={t("sources.file-tree.notYetComputed")}>
|
||||
<Icon name={"hourglass"} />
|
||||
</Tooltip>
|
||||
);
|
||||
} else {
|
||||
return content(file);
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const { file } = this.props;
|
||||
|
||||
const fileSize = file.directory ? "" : <FileSize bytes={file.length} />;
|
||||
const renderFileSize = (file: File) => <FileSize bytes={file.length} />;
|
||||
const renderCommitDate = (file: File) => <DateFromNow date={file.commitDate} />;
|
||||
|
||||
return (
|
||||
<tr>
|
||||
<td>{this.createFileIcon(file)}</td>
|
||||
<MinWidthTd className="is-word-break">{this.createFileName(file)}</MinWidthTd>
|
||||
<NoWrapTd className="is-hidden-mobile">{fileSize}</NoWrapTd>
|
||||
<td className="is-hidden-mobile">
|
||||
<DateFromNow date={file.lastModified} />
|
||||
</td>
|
||||
<MinWidthTd className={classNames("is-word-break", "is-hidden-touch")}>{file.description}</MinWidthTd>
|
||||
<NoWrapTd className="is-hidden-mobile">{file.directory ? "" : this.contentIfPresent(file, "length", renderFileSize)}</NoWrapTd>
|
||||
<td className="is-hidden-mobile">{this.contentIfPresent(file, "commitDate", renderCommitDate)}</td>
|
||||
<MinWidthTd className={classNames("is-word-break", "is-hidden-touch")}>
|
||||
{this.contentIfPresent(file, "description", file => file.description)}
|
||||
</MinWidthTd>
|
||||
{binder.hasExtension("repos.sources.tree.row.right") && (
|
||||
<td className="is-hidden-mobile">
|
||||
{!file.directory && (
|
||||
@@ -93,3 +117,5 @@ export default class FileTreeLeaf extends React.Component<Props> {
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default withTranslation("repos")(FileTreeLeaf);
|
||||
|
||||
@@ -115,7 +115,7 @@ class Content extends React.Component<Props, State> {
|
||||
showMoreInformation() {
|
||||
const collapsed = this.state.collapsed;
|
||||
const { file, revision, t, repository } = this.props;
|
||||
const date = <DateFromNow date={file.lastModified} />;
|
||||
const date = <DateFromNow date={file.commitDate} />;
|
||||
const description = file.description ? (
|
||||
<p>
|
||||
{file.description.split("\n").map((item, key) => {
|
||||
|
||||
@@ -49,10 +49,8 @@ const collection = {
|
||||
name: "src",
|
||||
path: "src",
|
||||
directory: true,
|
||||
description: "",
|
||||
length: 176,
|
||||
revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4",
|
||||
lastModified: "",
|
||||
subRepository: undefined,
|
||||
_links: {
|
||||
self: {
|
||||
@@ -71,7 +69,7 @@ const collection = {
|
||||
description: "bump version",
|
||||
length: 780,
|
||||
revision: "76aae4bb4ceacf0e88938eb5b6832738b7d537b4",
|
||||
lastModified: "2017-07-31T11:17:19Z",
|
||||
commitDate: "2017-07-31T11:17:19Z",
|
||||
subRepository: undefined,
|
||||
_links: {
|
||||
self: {
|
||||
@@ -127,7 +125,7 @@ describe("sources fetch", () => {
|
||||
{
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
itemId: "scm/core/_/",
|
||||
payload: collection
|
||||
payload: { updatePending: false, sources: collection }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -148,7 +146,7 @@ describe("sources fetch", () => {
|
||||
{
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
itemId: "scm/core/abc/src",
|
||||
payload: collection
|
||||
payload: { updatePending: false, sources: collection }
|
||||
}
|
||||
];
|
||||
|
||||
@@ -182,14 +180,14 @@ describe("reducer tests", () => {
|
||||
|
||||
it("should store the collection, without revision and path", () => {
|
||||
const expectedState = {
|
||||
"scm/core/_/": collection
|
||||
"scm/core/_/": { updatePending: false, sources: collection }
|
||||
};
|
||||
expect(reducer({}, fetchSourcesSuccess(repository, "", "", collection))).toEqual(expectedState);
|
||||
});
|
||||
|
||||
it("should store the collection, with revision and path", () => {
|
||||
const expectedState = {
|
||||
"scm/core/abc/src/main": collection
|
||||
"scm/core/abc/src/main": { updatePending: false, sources: collection }
|
||||
};
|
||||
expect(reducer({}, fetchSourcesSuccess(repository, "abc", "src/main", collection))).toEqual(expectedState);
|
||||
});
|
||||
@@ -200,7 +198,7 @@ describe("selector tests", () => {
|
||||
const state = {
|
||||
sources: {
|
||||
"scm/core/abc/src/main/package.json": {
|
||||
noDirectory
|
||||
sources: {noDirectory}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -223,7 +221,9 @@ describe("selector tests", () => {
|
||||
it("should return the source collection without revision and path", () => {
|
||||
const state = {
|
||||
sources: {
|
||||
"scm/core/_/": collection
|
||||
"scm/core/_/": {
|
||||
sources: collection
|
||||
}
|
||||
}
|
||||
};
|
||||
expect(getSources(state, repository, "", "")).toBe(collection);
|
||||
@@ -232,7 +232,9 @@ describe("selector tests", () => {
|
||||
it("should return the source collection with revision and path", () => {
|
||||
const state = {
|
||||
sources: {
|
||||
"scm/core/abc/src/main": collection
|
||||
"scm/core/abc/src/main": {
|
||||
sources: collection
|
||||
}
|
||||
}
|
||||
};
|
||||
expect(getSources(state, repository, "abc", "src/main")).toBe(collection);
|
||||
|
||||
@@ -9,13 +9,25 @@ export const FETCH_SOURCES_PENDING = `${FETCH_SOURCES}_${types.PENDING_SUFFIX}`;
|
||||
export const FETCH_SOURCES_SUCCESS = `${FETCH_SOURCES}_${types.SUCCESS_SUFFIX}`;
|
||||
export const FETCH_SOURCES_FAILURE = `${FETCH_SOURCES}_${types.FAILURE_SUFFIX}`;
|
||||
|
||||
export function fetchSources(repository: Repository, revision: string, path: string) {
|
||||
return function(dispatch: any) {
|
||||
export function fetchSources(repository: Repository, revision: string, path: string, initialLoad = true) {
|
||||
return function(dispatch: any, getState: () => any) {
|
||||
const state = getState();
|
||||
if (
|
||||
isFetchSourcesPending(state, repository, revision, path) ||
|
||||
isUpdateSourcePending(state, repository, revision, path)
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (initialLoad) {
|
||||
dispatch(fetchSourcesPending(repository, revision, path));
|
||||
} else {
|
||||
dispatch(updateSourcesPending(repository, revision, path, getSources(state, repository, revision, path)));
|
||||
}
|
||||
return apiClient
|
||||
.get(createUrl(repository, revision, path))
|
||||
.then(response => response.json())
|
||||
.then(sources => {
|
||||
.then((sources: File) => {
|
||||
dispatch(fetchSourcesSuccess(repository, revision, path, sources));
|
||||
})
|
||||
.catch(err => {
|
||||
@@ -42,10 +54,23 @@ export function fetchSourcesPending(repository: Repository, revision: string, pa
|
||||
};
|
||||
}
|
||||
|
||||
export function updateSourcesPending(
|
||||
repository: Repository,
|
||||
revision: string,
|
||||
path: string,
|
||||
currentSources: any
|
||||
): Action {
|
||||
return {
|
||||
type: "UPDATE_PENDING",
|
||||
payload: { updatePending: true, sources: currentSources },
|
||||
itemId: createItemId(repository, revision, path)
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchSourcesSuccess(repository: Repository, revision: string, path: string, sources: File) {
|
||||
return {
|
||||
type: FETCH_SOURCES_SUCCESS,
|
||||
payload: sources,
|
||||
payload: { updatePending: false, sources },
|
||||
itemId: createItemId(repository, revision, path)
|
||||
};
|
||||
}
|
||||
@@ -72,7 +97,7 @@ export default function reducer(
|
||||
type: "UNKNOWN"
|
||||
}
|
||||
): any {
|
||||
if (action.itemId && action.type === FETCH_SOURCES_SUCCESS) {
|
||||
if (action.itemId && (action.type === FETCH_SOURCES_SUCCESS || action.type === "UPDATE_PENDING")) {
|
||||
return {
|
||||
...state,
|
||||
[action.itemId]: action.payload
|
||||
@@ -99,13 +124,17 @@ export function getSources(
|
||||
path: string
|
||||
): File | null | undefined {
|
||||
if (state.sources) {
|
||||
return state.sources[createItemId(repository, revision, path)];
|
||||
return state.sources[createItemId(repository, revision, path)]?.sources;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
export function isFetchSourcesPending(state: any, repository: Repository, revision: string, path: string): boolean {
|
||||
return isPending(state, FETCH_SOURCES, createItemId(repository, revision, path));
|
||||
return state && isPending(state, FETCH_SOURCES, createItemId(repository, revision, path));
|
||||
}
|
||||
|
||||
function isUpdateSourcePending(state: any, repository: Repository, revision: string, path: string): boolean {
|
||||
return state?.sources && state.sources[createItemId(repository, revision, path)]?.updatePending;
|
||||
}
|
||||
|
||||
export function getFetchSourcesFailure(
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import de.otto.edison.hal.Embedded;
|
||||
import de.otto.edison.hal.Links;
|
||||
import org.mapstruct.Context;
|
||||
@@ -16,18 +15,13 @@ import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
import java.time.Instant;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
@Mapper
|
||||
public abstract class BrowserResultToFileObjectDtoMapper extends BaseFileObjectDtoMapper {
|
||||
|
||||
@Inject
|
||||
private FileObjectToFileObjectDtoMapper childrenMapper;
|
||||
|
||||
@VisibleForTesting
|
||||
void setChildrenMapper(FileObjectToFileObjectDtoMapper childrenMapper) {
|
||||
this.childrenMapper = childrenMapper;
|
||||
}
|
||||
|
||||
FileObjectDto map(BrowserResult browserResult, @Context NamespaceAndName namespaceAndName) {
|
||||
FileObjectDto fileObjectDto = fileObjectToDto(browserResult.getFile(), namespaceAndName, browserResult);
|
||||
fileObjectDto.setRevision(browserResult.getRevision());
|
||||
@@ -36,12 +30,8 @@ public abstract class BrowserResultToFileObjectDtoMapper extends BaseFileObjectD
|
||||
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
@Mapping(target = "children", qualifiedBy = Children.class)
|
||||
protected abstract FileObjectDto fileObjectToDto(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult);
|
||||
|
||||
@Children
|
||||
protected FileObjectDto childrenToDto(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult) {
|
||||
return childrenMapper.map(fileObject, namespaceAndName, browserResult);
|
||||
}
|
||||
protected abstract FileObjectDto fileObjectToDto(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult);
|
||||
|
||||
@Override
|
||||
void applyEnrichers(Links.Builder links, Embedded.Builder embeddedBuilder, NamespaceAndName namespaceAndName, BrowserResult browserResult, FileObject fileObject) {
|
||||
@@ -52,6 +42,14 @@ public abstract class BrowserResultToFileObjectDtoMapper extends BaseFileObjectD
|
||||
applyEnrichers(appender, fileObject, namespaceAndName, browserResult, browserResult.getRevision());
|
||||
}
|
||||
|
||||
Optional<Instant> mapOptionalInstant(OptionalLong optionalLong) {
|
||||
if (optionalLong.isPresent()) {
|
||||
return Optional.of(Instant.ofEpochMilli(optionalLong.getAsLong()));
|
||||
} else {
|
||||
return Optional.empty();
|
||||
}
|
||||
}
|
||||
|
||||
@Qualifier
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.CLASS)
|
||||
|
||||
@@ -10,6 +10,8 @@ import lombok.Setter;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.OptionalLong;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@@ -19,14 +21,16 @@ public class FileObjectDto extends HalRepresentation {
|
||||
private String path;
|
||||
private boolean directory;
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private String description;
|
||||
private long length;
|
||||
private Optional<String> description;
|
||||
private OptionalLong length;
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private Instant lastModified;
|
||||
private Optional<Instant> commitDate;
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private SubRepositoryDto subRepository;
|
||||
@JsonInclude(JsonInclude.Include.NON_EMPTY)
|
||||
private String revision;
|
||||
private boolean partialResult;
|
||||
private boolean computationAborted;
|
||||
|
||||
public FileObjectDto(Links links, Embedded embedded) {
|
||||
super(links, embedded);
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.Embedded;
|
||||
import de.otto.edison.hal.Links;
|
||||
import org.mapstruct.Context;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import sonia.scm.repository.BrowserResult;
|
||||
import sonia.scm.repository.FileObject;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
|
||||
@Mapper
|
||||
public abstract class FileObjectToFileObjectDtoMapper extends BaseFileObjectDtoMapper {
|
||||
|
||||
@Mapping(target = "attributes", ignore = true) // We do not map HAL attributes
|
||||
protected abstract FileObjectDto map(FileObject fileObject, @Context NamespaceAndName namespaceAndName, @Context BrowserResult browserResult);
|
||||
|
||||
@Override
|
||||
void applyEnrichers(Links.Builder links, Embedded.Builder embeddedBuilder, NamespaceAndName namespaceAndName, BrowserResult browserResult, FileObject fileObject) {
|
||||
applyEnrichers(new EdisonHalAppender(links, embeddedBuilder), fileObject, namespaceAndName, browserResult, browserResult.getRevision());
|
||||
}
|
||||
}
|
||||
@@ -37,7 +37,6 @@ public class MapperModule extends AbstractModule {
|
||||
|
||||
bind(TagToTagDtoMapper.class).to(Mappers.getMapper(TagToTagDtoMapper.class).getClass());
|
||||
|
||||
bind(FileObjectToFileObjectDtoMapper.class).to(Mappers.getMapper(FileObjectToFileObjectDtoMapper.class).getClass());
|
||||
bind(BrowserResultToFileObjectDtoMapper.class).to(Mappers.getMapper(BrowserResultToFileObjectDtoMapper.class).getClass());
|
||||
bind(ModificationsToDtoMapper.class).to(Mappers.getMapper(ModificationsToDtoMapper.class).getClass());
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ import sonia.scm.debug.DebugModule;
|
||||
import sonia.scm.filter.WebElementModule;
|
||||
import sonia.scm.plugin.ExtensionProcessor;
|
||||
import sonia.scm.plugin.PluginLoader;
|
||||
import sonia.scm.repository.ExecutorModule;
|
||||
|
||||
import javax.servlet.ServletContext;
|
||||
import java.util.ArrayList;
|
||||
@@ -51,6 +52,7 @@ public class ApplicationModuleProvider implements ModuleProvider {
|
||||
moduleList.add(new DebugModule());
|
||||
}
|
||||
moduleList.add(new MapperModule());
|
||||
moduleList.add(new ExecutorModule());
|
||||
|
||||
return moduleList;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,49 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import sonia.scm.repository.spi.SyncAsyncExecutor;
|
||||
|
||||
import java.time.Instant;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicLong;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType.ASYNCHRONOUS;
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType.SYNCHRONOUS;
|
||||
|
||||
public class DefaultSyncAsyncExecutor implements SyncAsyncExecutor {
|
||||
|
||||
private final Executor executor;
|
||||
private final Instant switchToAsyncTime;
|
||||
private final long maxAsyncAbortMilliseconds;
|
||||
private AtomicLong accumulatedAsyncRuntime = new AtomicLong(0L);
|
||||
private boolean executedAllSynchronously = true;
|
||||
|
||||
DefaultSyncAsyncExecutor(Executor executor, Instant switchToAsyncTime, int maxAsyncAbortSeconds) {
|
||||
this.executor = executor;
|
||||
this.switchToAsyncTime = switchToAsyncTime;
|
||||
this.maxAsyncAbortMilliseconds = maxAsyncAbortSeconds * 1000L;
|
||||
}
|
||||
|
||||
public ExecutionType execute(Consumer<ExecutionType> task, Runnable abortionFallback) {
|
||||
if (switchToAsyncTime.isBefore(Instant.now())) {
|
||||
executor.execute(() -> {
|
||||
if (accumulatedAsyncRuntime.get() < maxAsyncAbortMilliseconds) {
|
||||
long chunkStartTime = System.currentTimeMillis();
|
||||
task.accept(ASYNCHRONOUS);
|
||||
accumulatedAsyncRuntime.addAndGet(System.currentTimeMillis() - chunkStartTime);
|
||||
} else {
|
||||
abortionFallback.run();
|
||||
}
|
||||
});
|
||||
executedAllSynchronously = false;
|
||||
return ASYNCHRONOUS;
|
||||
} else {
|
||||
task.accept(SYNCHRONOUS);
|
||||
return SYNCHRONOUS;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean hasExecutedAllSynchronously() {
|
||||
return executedAllSynchronously;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import sonia.scm.repository.spi.SyncAsyncExecutor;
|
||||
import sonia.scm.repository.spi.SyncAsyncExecutorProvider;
|
||||
|
||||
import javax.inject.Inject;
|
||||
import javax.inject.Singleton;
|
||||
import java.io.Closeable;
|
||||
import java.time.Instant;
|
||||
import java.time.temporal.ChronoUnit;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
@Singleton
|
||||
public class DefaultSyncAsyncExecutorProvider implements SyncAsyncExecutorProvider, Closeable {
|
||||
|
||||
public static final int DEFAULT_MAX_ASYNC_ABORT_SECONDS = 60;
|
||||
public static final String MAX_ASYNC_ABORT_SECONDS_PROPERTY = "scm.maxAsyncAbortSeconds";
|
||||
|
||||
public static final int DEFAULT_NUMBER_OF_THREADS = 4;
|
||||
public static final String NUMBER_OF_THREADS_PROPERTY = "scm.asyncThreads";
|
||||
|
||||
private final ExecutorService executor;
|
||||
private final int defaultMaxAsyncAbortSeconds;
|
||||
|
||||
@Inject
|
||||
public DefaultSyncAsyncExecutorProvider() {
|
||||
this(Executors.newFixedThreadPool(getProperty(NUMBER_OF_THREADS_PROPERTY, DEFAULT_NUMBER_OF_THREADS)));
|
||||
}
|
||||
|
||||
public DefaultSyncAsyncExecutorProvider(ExecutorService executor) {
|
||||
this.executor = executor;
|
||||
this.defaultMaxAsyncAbortSeconds = getProperty(MAX_ASYNC_ABORT_SECONDS_PROPERTY, DEFAULT_MAX_ASYNC_ABORT_SECONDS);
|
||||
}
|
||||
|
||||
public SyncAsyncExecutor createExecutorWithSecondsToTimeout(int switchToAsyncInSeconds) {
|
||||
return createExecutorWithSecondsToTimeout(switchToAsyncInSeconds, defaultMaxAsyncAbortSeconds);
|
||||
}
|
||||
|
||||
public SyncAsyncExecutor createExecutorWithSecondsToTimeout(int switchToAsyncInSeconds, int maxAsyncAbortSeconds) {
|
||||
return new DefaultSyncAsyncExecutor(
|
||||
executor,
|
||||
Instant.now().plus(switchToAsyncInSeconds, ChronoUnit.SECONDS),
|
||||
maxAsyncAbortSeconds);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
executor.shutdownNow();
|
||||
}
|
||||
|
||||
private static int getProperty(String key, int defaultValue) {
|
||||
return Integer.parseInt(System.getProperty(key, Integer.toString(defaultValue)));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import com.google.inject.AbstractModule;
|
||||
import sonia.scm.lifecycle.modules.CloseableModule;
|
||||
import sonia.scm.repository.spi.SyncAsyncExecutorProvider;
|
||||
|
||||
public class ExecutorModule extends AbstractModule {
|
||||
@Override
|
||||
protected void configure() {
|
||||
bind(SyncAsyncExecutorProvider.class).to(DefaultSyncAsyncExecutorProvider.class);
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,6 @@ import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import org.mockito.InjectMocks;
|
||||
import sonia.scm.repository.BrowserResult;
|
||||
import sonia.scm.repository.FileObject;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
@@ -24,9 +23,6 @@ public class BrowserResultToFileObjectDtoMapperTest {
|
||||
private final URI baseUri = URI.create("http://example.com/base/");
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@InjectMocks
|
||||
private FileObjectToFileObjectDtoMapperImpl fileObjectToFileObjectDtoMapper;
|
||||
|
||||
private BrowserResultToFileObjectDtoMapper mapper;
|
||||
|
||||
private final Subject subject = mock(Subject.class);
|
||||
@@ -34,28 +30,28 @@ public class BrowserResultToFileObjectDtoMapperTest {
|
||||
|
||||
private FileObject fileObject1 = new FileObject();
|
||||
private FileObject fileObject2 = new FileObject();
|
||||
private FileObject partialFileObject = new FileObject();
|
||||
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
initMocks(this);
|
||||
mapper = Mappers.getMapper(BrowserResultToFileObjectDtoMapper.class);
|
||||
mapper.setChildrenMapper(fileObjectToFileObjectDtoMapper);
|
||||
mapper.setResourceLinks(resourceLinks);
|
||||
|
||||
subjectThreadState.bind();
|
||||
ThreadContext.bind(subject);
|
||||
|
||||
fileObject1.setName("FO 1");
|
||||
fileObject1.setLength(100);
|
||||
fileObject1.setLastModified(0L);
|
||||
fileObject1.setLength(100L);
|
||||
fileObject1.setCommitDate(0L);
|
||||
fileObject1.setPath("/path/object/1");
|
||||
fileObject1.setDescription("description of file object 1");
|
||||
fileObject1.setDirectory(false);
|
||||
|
||||
fileObject2.setName("FO 2");
|
||||
fileObject2.setLength(100);
|
||||
fileObject2.setLastModified(101L);
|
||||
fileObject2.setLength(100L);
|
||||
fileObject2.setCommitDate(101L);
|
||||
fileObject2.setPath("/path/object/2");
|
||||
fileObject2.setDescription("description of file object 2");
|
||||
fileObject2.setDirectory(true);
|
||||
|
||||
@@ -1,121 +0,0 @@
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.apache.shiro.subject.Subject;
|
||||
import org.apache.shiro.subject.support.SubjectThreadState;
|
||||
import org.apache.shiro.util.ThreadContext;
|
||||
import org.apache.shiro.util.ThreadState;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.repository.BrowserResult;
|
||||
import sonia.scm.repository.FileObject;
|
||||
import sonia.scm.repository.NamespaceAndName;
|
||||
import sonia.scm.repository.SubRepository;
|
||||
|
||||
import java.net.URI;
|
||||
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.mock;
|
||||
|
||||
@RunWith(MockitoJUnitRunner.Silent.class)
|
||||
public class FileObjectToFileObjectDtoMapperTest {
|
||||
|
||||
private final URI baseUri = URI.create("http://example.com/base/");
|
||||
@SuppressWarnings("unused") // Is injected
|
||||
private final ResourceLinks resourceLinks = ResourceLinksMock.createMock(baseUri);
|
||||
|
||||
@InjectMocks
|
||||
private FileObjectToFileObjectDtoMapperImpl mapper;
|
||||
|
||||
private final Subject subject = mock(Subject.class);
|
||||
private final ThreadState subjectThreadState = new SubjectThreadState(subject);
|
||||
|
||||
private URI expectedBaseUri;
|
||||
|
||||
@Before
|
||||
public void init() {
|
||||
expectedBaseUri = baseUri.resolve(RepositoryRootResource.REPOSITORIES_PATH_V2 + "/");
|
||||
subjectThreadState.bind();
|
||||
ThreadContext.bind(subject);
|
||||
}
|
||||
|
||||
@After
|
||||
public void unbind() {
|
||||
ThreadContext.unbindSubject();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldMapAttributesCorrectly() {
|
||||
FileObject fileObject = createFileObject();
|
||||
FileObjectDto dto = mapper.map(fileObject, new NamespaceAndName("namespace", "name"), new BrowserResult("revision", fileObject));
|
||||
|
||||
assertEqualAttributes(fileObject, dto);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveCorrectSelfLinkForDirectory() {
|
||||
FileObject fileObject = createDirectoryObject();
|
||||
FileObjectDto dto = mapper.map(fileObject, new NamespaceAndName("namespace", "name"), new BrowserResult("revision", fileObject));
|
||||
|
||||
assertThat(dto.getLinks().getLinkBy("self").get().getHref()).isEqualTo(expectedBaseUri.resolve("namespace/name/sources/revision/foo/bar").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldHaveCorrectContentLink() {
|
||||
FileObject fileObject = createFileObject();
|
||||
fileObject.setDirectory(false);
|
||||
FileObjectDto dto = mapper.map(fileObject, new NamespaceAndName("namespace", "name"), new BrowserResult("revision", fileObject));
|
||||
|
||||
assertThat(dto.getLinks().getLinkBy("self").get().getHref()).isEqualTo(expectedBaseUri.resolve("namespace/name/content/revision/foo/bar").toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void shouldAppendLinks() {
|
||||
HalEnricherRegistry registry = new HalEnricherRegistry();
|
||||
registry.register(FileObject.class, (ctx, appender) -> {
|
||||
NamespaceAndName repository = ctx.oneRequireByType(NamespaceAndName.class);
|
||||
FileObject fo = ctx.oneRequireByType(FileObject.class);
|
||||
String rev = ctx.oneRequireByType(String.class);
|
||||
|
||||
appender.appendLink("hog", "http://" + repository.logString() + "/" + fo.getName() + "/" + rev);
|
||||
});
|
||||
mapper.setRegistry(registry);
|
||||
|
||||
FileObject fileObject = createFileObject();
|
||||
FileObjectDto dto = mapper.map(fileObject, new NamespaceAndName("hitchhiker", "hog"), new BrowserResult("42", fileObject));
|
||||
|
||||
assertThat(dto.getLinks().getLinkBy("hog").get().getHref()).isEqualTo("http://hitchhiker/hog/foo/42");
|
||||
}
|
||||
|
||||
private FileObject createDirectoryObject() {
|
||||
FileObject fileObject = createFileObject();
|
||||
fileObject.setDirectory(true);
|
||||
return fileObject;
|
||||
}
|
||||
|
||||
private FileObject createFileObject() {
|
||||
FileObject fileObject = new FileObject();
|
||||
fileObject.setName("foo");
|
||||
fileObject.setDescription("bar");
|
||||
fileObject.setPath("foo/bar");
|
||||
fileObject.setDirectory(false);
|
||||
fileObject.setLength(100);
|
||||
fileObject.setLastModified(123L);
|
||||
|
||||
fileObject.setSubRepository(new SubRepository("repo.url"));
|
||||
return fileObject;
|
||||
}
|
||||
|
||||
private void assertEqualAttributes(FileObject fileObject, FileObjectDto dto) {
|
||||
assertThat(dto.getName()).isEqualTo(fileObject.getName());
|
||||
assertThat(dto.getDescription()).isEqualTo(fileObject.getDescription());
|
||||
assertThat(dto.getPath()).isEqualTo(fileObject.getPath());
|
||||
assertThat(dto.isDirectory()).isEqualTo(fileObject.isDirectory());
|
||||
assertThat(dto.getLength()).isEqualTo(fileObject.getLength());
|
||||
assertThat(dto.getLastModified().toEpochMilli()).isEqualTo((long) fileObject.getLastModified());
|
||||
assertThat(dto.getSubRepository().getBrowserUrl()).isEqualTo(fileObject.getSubRepository().getBrowserUrl());
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,6 @@ import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
import org.mapstruct.factory.Mappers;
|
||||
import org.mockito.InjectMocks;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.MockitoJUnitRunner;
|
||||
import sonia.scm.NotFoundException;
|
||||
@@ -41,16 +40,12 @@ public class SourceRootResourceTest extends RepositoryTestBase {
|
||||
@Mock
|
||||
private BrowseCommandBuilder browseCommandBuilder;
|
||||
|
||||
@InjectMocks
|
||||
private FileObjectToFileObjectDtoMapperImpl fileObjectToFileObjectDtoMapper;
|
||||
|
||||
private BrowserResultToFileObjectDtoMapper browserResultToFileObjectDtoMapper;
|
||||
|
||||
|
||||
@Before
|
||||
public void prepareEnvironment() throws Exception {
|
||||
public void prepareEnvironment() {
|
||||
browserResultToFileObjectDtoMapper = Mappers.getMapper(BrowserResultToFileObjectDtoMapper.class);
|
||||
browserResultToFileObjectDtoMapper.setChildrenMapper(fileObjectToFileObjectDtoMapper);
|
||||
browserResultToFileObjectDtoMapper.setResourceLinks(resourceLinks);
|
||||
when(serviceFactory.create(new NamespaceAndName("space", "repo"))).thenReturn(service);
|
||||
when(service.getBrowseCommand()).thenReturn(browseCommandBuilder);
|
||||
@@ -127,7 +122,7 @@ public class SourceRootResourceTest extends RepositoryTestBase {
|
||||
fileObject1.setDescription("File object 1");
|
||||
fileObject1.setPath("/foo/bar/fo1");
|
||||
fileObject1.setLength(1024L);
|
||||
fileObject1.setLastModified(0L);
|
||||
fileObject1.setCommitDate(0L);
|
||||
parent.addChild(fileObject1);
|
||||
|
||||
FileObject fileObject2 = new FileObject();
|
||||
@@ -136,7 +131,7 @@ public class SourceRootResourceTest extends RepositoryTestBase {
|
||||
fileObject2.setDescription("File object 2");
|
||||
fileObject2.setPath("/foo/bar/fo2");
|
||||
fileObject2.setLength(4096L);
|
||||
fileObject2.setLastModified(1234L);
|
||||
fileObject2.setCommitDate(1234L);
|
||||
parent.addChild(fileObject2);
|
||||
|
||||
return parent;
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
package sonia.scm.repository;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
import sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
import static java.lang.Integer.MAX_VALUE;
|
||||
import static java.time.Instant.MAX;
|
||||
import static java.time.temporal.ChronoUnit.MILLIS;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType.ASYNCHRONOUS;
|
||||
import static sonia.scm.repository.spi.SyncAsyncExecutor.ExecutionType.SYNCHRONOUS;
|
||||
|
||||
class DefaultSyncAsyncExecutorTest {
|
||||
|
||||
ExecutionType calledWithType = null;
|
||||
boolean aborted = false;
|
||||
|
||||
@Test
|
||||
void shouldExecuteSynchronouslyBeforeTimeout() {
|
||||
DefaultSyncAsyncExecutor executor = new DefaultSyncAsyncExecutor(Runnable::run, MAX, MAX_VALUE);
|
||||
|
||||
ExecutionType result = executor.execute(type -> calledWithType = type, () -> aborted = true);
|
||||
|
||||
assertThat(result).isEqualTo(SYNCHRONOUS);
|
||||
assertThat(calledWithType).isEqualTo(SYNCHRONOUS);
|
||||
assertThat(executor.hasExecutedAllSynchronously()).isTrue();
|
||||
assertThat(aborted).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldExecuteAsynchronouslyAfterTimeout() {
|
||||
DefaultSyncAsyncExecutor executor = new DefaultSyncAsyncExecutor(Runnable::run, Instant.now().minus(1, MILLIS), MAX_VALUE);
|
||||
|
||||
ExecutionType result = executor.execute(type -> calledWithType = type, () -> aborted = true);
|
||||
|
||||
assertThat(result).isEqualTo(ASYNCHRONOUS);
|
||||
assertThat(calledWithType).isEqualTo(ASYNCHRONOUS);
|
||||
assertThat(executor.hasExecutedAllSynchronously()).isFalse();
|
||||
assertThat(aborted).isFalse();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldCallFallbackAfterAbortion() {
|
||||
DefaultSyncAsyncExecutor executor = new DefaultSyncAsyncExecutor(Runnable::run, Instant.now().minus(1, MILLIS), 0);
|
||||
|
||||
ExecutionType result = executor.execute(type -> calledWithType = type, () -> aborted = true);
|
||||
|
||||
assertThat(result).isEqualTo(ASYNCHRONOUS);
|
||||
assertThat(calledWithType).isNull();
|
||||
assertThat(aborted).isTrue();
|
||||
}
|
||||
}
|
||||
414
yarn.lock
414
yarn.lock
@@ -840,7 +840,7 @@
|
||||
"@babel/helper-create-regexp-features-plugin" "^7.7.4"
|
||||
"@babel/helper-plugin-utils" "^7.0.0"
|
||||
|
||||
"@babel/polyfill@^7.0.0", "@babel/polyfill@^7.6.0":
|
||||
"@babel/polyfill@^7.6.0":
|
||||
version "7.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/polyfill/-/polyfill-7.6.0.tgz#6d89203f8b6cd323e8d946e47774ea35dc0619cc"
|
||||
integrity sha512-q5BZJI0n/B10VaQQvln1IlDK3BTBJFbADx7tv+oXDPIDZuTo37H5Adb9jhlXm/fEN4Y7/64qD9mnrJJG7rmaTw==
|
||||
@@ -2238,7 +2238,7 @@
|
||||
once "^1.4.0"
|
||||
universal-user-agent "^4.0.0"
|
||||
|
||||
"@octokit/rest@^16.28.4", "@octokit/rest@^16.33.1":
|
||||
"@octokit/rest@^16.28.4":
|
||||
version "16.34.0"
|
||||
resolved "https://registry.yarnpkg.com/@octokit/rest/-/rest-16.34.0.tgz#8703e46d7e9f6aec24a7e591b073f325ca13f6e2"
|
||||
integrity sha512-EBe5qMQQOZRuezahWCXCnSe0J6tAqrW2hrEH9U8esXzKor1+HUDf8jgImaZf5lkTyWCQA296x9kAH5c0pxEgVQ==
|
||||
@@ -2274,11 +2274,6 @@
|
||||
react-lifecycles-compat "^3.0.4"
|
||||
warning "^3.0.0"
|
||||
|
||||
"@sindresorhus/is@^0.7.0":
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/@sindresorhus/is/-/is-0.7.0.tgz#9a06f4f137ee84d7df0460c1fdb1135ffa6c50fd"
|
||||
integrity sha512-ONhaKPIufzzrlNbqtWFFd+jlnemX6lJAgq9ZeiZtS7I1PIf/la7CW4m83rTXRnVnsMbW2k56pGYu7AUFJD9Pow==
|
||||
|
||||
"@storybook/addon-actions@^5.2.3":
|
||||
version "5.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@storybook/addon-actions/-/addon-actions-5.2.8.tgz#f63c6e1afb59e94ca1ebc776b7cad9b815e7419e"
|
||||
@@ -3177,46 +3172,48 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@^2.4.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.6.0.tgz#e82ed43fc4527b21bfe35c20a2d6e4ed49fc7957"
|
||||
integrity sha512-iCcXREU4RciLmLniwKLRPCOFVXrkF7z27XuHq5DrykpREv/mz6ztKAyLg2fdkM0hQC7659p5ZF5uStH7uzAJ/w==
|
||||
"@typescript-eslint/eslint-plugin@^2.12.0":
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-2.12.0.tgz#0da7cbca7b24f4c6919e9eb31c704bfb126f90ad"
|
||||
integrity sha512-1t4r9rpLuEwl3hgt90jY18wJHSyb0E3orVL3DaqwmpiSDHmHiSspVsvsFF78BJ/3NNG3qmeso836jpuBWYziAA==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "2.6.0"
|
||||
eslint-utils "^1.4.2"
|
||||
"@typescript-eslint/experimental-utils" "2.12.0"
|
||||
eslint-utils "^1.4.3"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
regexpp "^2.0.1"
|
||||
regexpp "^3.0.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@typescript-eslint/experimental-utils@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.6.0.tgz#ed70bef72822bff54031ff0615fc888b9e2b6e8a"
|
||||
integrity sha512-34BAFpNOwHXeqT+AvdalLxOvcPYnCxA5JGmBAFL64RGMdP0u65rXjii7l/nwpgk5aLEE1LaqF+SsCU0/Cb64xA==
|
||||
"@typescript-eslint/experimental-utils@2.12.0":
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-2.12.0.tgz#e0a76ffb6293e058748408a191921e453c31d40d"
|
||||
integrity sha512-jv4gYpw5N5BrWF3ntROvCuLe1IjRenLy5+U57J24NbPGwZFAjhnM45qpq0nDH1y/AZMb3Br25YiNVwyPbz6RkA==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.3"
|
||||
"@typescript-eslint/typescript-estree" "2.6.0"
|
||||
"@typescript-eslint/typescript-estree" "2.12.0"
|
||||
eslint-scope "^5.0.0"
|
||||
|
||||
"@typescript-eslint/parser@^2.4.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.6.0.tgz#5106295c6a7056287b4719e24aae8d6293d5af49"
|
||||
integrity sha512-AvLejMmkcjRTJ2KD72v565W4slSrrzUIzkReu1JN34b8JnsEsxx7S9Xx/qXEuMQas0mkdUfETr0j3zOhq2DIqQ==
|
||||
"@typescript-eslint/parser@^2.12.0":
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-2.12.0.tgz#393f1604943a4ca570bb1a45bc8834e9b9158884"
|
||||
integrity sha512-lPdkwpdzxEfjI8TyTzZqPatkrswLSVu4bqUgnB03fHSOwpC7KSerPgJRgIAf11UGNf7HKjJV6oaPZI4AghLU6g==
|
||||
dependencies:
|
||||
"@types/eslint-visitor-keys" "^1.0.0"
|
||||
"@typescript-eslint/experimental-utils" "2.6.0"
|
||||
"@typescript-eslint/typescript-estree" "2.6.0"
|
||||
"@typescript-eslint/experimental-utils" "2.12.0"
|
||||
"@typescript-eslint/typescript-estree" "2.12.0"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@2.6.0":
|
||||
version "2.6.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.6.0.tgz#d3e9d8e001492e2b9124c4d4bd4e7f03c0fd7254"
|
||||
integrity sha512-A3lSBVIdj2Gp0lFEL6in2eSPqJ33uAc3Ko+Y4brhjkxzjbzLnwBH22CwsW2sCo+iwogfIyvb56/AJri15H0u5Q==
|
||||
"@typescript-eslint/typescript-estree@2.12.0":
|
||||
version "2.12.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-2.12.0.tgz#bd9e547ccffd17dfab0c3ab0947c80c8e2eb914c"
|
||||
integrity sha512-rGehVfjHEn8Frh9UW02ZZIfJs6SIIxIu/K1bbci8rFfDE/1lQ8krIJy5OXOV3DVnNdDPtoiPOdEANkLMrwXbiQ==
|
||||
dependencies:
|
||||
debug "^4.1.1"
|
||||
glob "^7.1.4"
|
||||
eslint-visitor-keys "^1.1.0"
|
||||
glob "^7.1.6"
|
||||
is-glob "^4.0.1"
|
||||
lodash.unescape "4.0.1"
|
||||
semver "^6.3.0"
|
||||
tsutils "^3.17.1"
|
||||
|
||||
"@webassemblyjs/ast@1.8.5":
|
||||
version "1.8.5"
|
||||
@@ -4380,11 +4377,6 @@ before-after-hook@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.1.0.tgz#b6c03487f44e24200dd30ca5e6a1979c5d2fb635"
|
||||
integrity sha512-IWIbu7pMqyw3EAJHzzHbWa85b6oud/yfKYg5rqB5hNE8CeMi3nX+2C2sj0HswfblST86hpVEOAb9x34NZd6P7A==
|
||||
|
||||
big-integer@^1.6.17:
|
||||
version "1.6.47"
|
||||
resolved "https://registry.yarnpkg.com/big-integer/-/big-integer-1.6.47.tgz#e1e9320e26c4cc81f64fbf4b3bb20e025bf18e2d"
|
||||
integrity sha512-9t9f7X3as2XGX8b52GqG6ox0GvIdM86LyIXASJnDCFhYNgt+A+MByQZ3W2PyMRZjEvG5f8TEbSPfEotVuMJnQg==
|
||||
|
||||
big.js@^5.2.2:
|
||||
version "5.2.2"
|
||||
resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328"
|
||||
@@ -4395,14 +4387,6 @@ binary-extensions@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.13.1.tgz#598afe54755b2868a5330d2aff9d4ebb53209b65"
|
||||
integrity sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==
|
||||
|
||||
binary@~0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/binary/-/binary-0.3.0.tgz#9f60553bc5ce8c3386f3b553cff47462adecaa79"
|
||||
integrity sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=
|
||||
dependencies:
|
||||
buffers "~0.1.1"
|
||||
chainsaw "~0.1.0"
|
||||
|
||||
block-stream@*:
|
||||
version "0.0.9"
|
||||
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
|
||||
@@ -4420,11 +4404,6 @@ bluebird@^3.5.1, bluebird@^3.5.3:
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.1.tgz#df70e302b471d7473489acf26a93d63b53f874de"
|
||||
integrity sha512-DdmyoGCleJnkbp3nkbxTLJ18rjDsE4yCggEwKNXkeV123sPNfOCYeDoeuOY+F2FrSjO1YXcTU+dsy96KMy+gcg==
|
||||
|
||||
bluebird@~3.4.1:
|
||||
version "3.4.7"
|
||||
resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.4.7.tgz#f72d760be09b7f76d08ed8fae98b289a8d05fab3"
|
||||
integrity sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=
|
||||
|
||||
bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.1.1, bn.js@^4.4.0:
|
||||
version "4.11.8"
|
||||
resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.11.8.tgz#2cde09eb5ee341f484746bb0309b3253b1b1442f"
|
||||
@@ -4621,11 +4600,6 @@ buffer-from@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/buffer-from/-/buffer-from-1.1.1.tgz#32713bc028f75c02fdb710d7c7bcec1f2c6070ef"
|
||||
integrity sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==
|
||||
|
||||
buffer-indexof-polyfill@~1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.1.tgz#a9fb806ce8145d5428510ce72f278bb363a638bf"
|
||||
integrity sha1-qfuAbOgUXVQoUQznLyeLs2OmOL8=
|
||||
|
||||
buffer-indexof@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c"
|
||||
@@ -4650,11 +4624,6 @@ buffer@^4.3.0:
|
||||
ieee754 "^1.1.4"
|
||||
isarray "^1.0.0"
|
||||
|
||||
buffers@~0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/buffers/-/buffers-0.1.1.tgz#b24579c3bed4d6d396aeee6d9a8ae7f5482ab7bb"
|
||||
integrity sha1-skV5w77U1tOWru5tmorn9Ugqt7s=
|
||||
|
||||
builtin-status-codes@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz#85982878e21b98e1c66425e03d0174788f569ee8"
|
||||
@@ -4748,19 +4717,6 @@ cache-loader@^4.1.0:
|
||||
neo-async "^2.6.1"
|
||||
schema-utils "^2.0.0"
|
||||
|
||||
cacheable-request@^2.1.1:
|
||||
version "2.1.4"
|
||||
resolved "https://registry.yarnpkg.com/cacheable-request/-/cacheable-request-2.1.4.tgz#0d808801b6342ad33c91df9d0b44dc09b91e5c3d"
|
||||
integrity sha1-DYCIAbY0KtM8kd+dC0TcCbkeXD0=
|
||||
dependencies:
|
||||
clone-response "1.0.2"
|
||||
get-stream "3.0.0"
|
||||
http-cache-semantics "3.8.1"
|
||||
keyv "3.0.0"
|
||||
lowercase-keys "1.0.0"
|
||||
normalize-url "2.0.1"
|
||||
responselike "1.0.2"
|
||||
|
||||
call-me-maybe@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/call-me-maybe/-/call-me-maybe-1.0.1.tgz#26d208ea89e37b5cbde60250a15f031c16a4d66b"
|
||||
@@ -4882,13 +4838,6 @@ caseless@~0.12.0:
|
||||
resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc"
|
||||
integrity sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=
|
||||
|
||||
chainsaw@~0.1.0:
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/chainsaw/-/chainsaw-0.1.0.tgz#5eab50b28afe58074d0d58291388828b5e5fbc98"
|
||||
integrity sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=
|
||||
dependencies:
|
||||
traverse ">=0.3.0 <0.4"
|
||||
|
||||
chalk@2.4.2, chalk@^2.0.0, chalk@^2.0.1, chalk@^2.1.0, chalk@^2.3.1, chalk@^2.4.1, chalk@^2.4.2:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
@@ -4929,11 +4878,6 @@ chardet@^0.7.0:
|
||||
resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.7.0.tgz#90094849f0937f2eedc2425d0d28a9e5f0cbad9e"
|
||||
integrity sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==
|
||||
|
||||
charenc@~0.0.1:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/charenc/-/charenc-0.0.2.tgz#c0a1d2f3a7092e03774bfa83f14c0fc5790a8667"
|
||||
integrity sha1-wKHS86cJLgN3S/qD8UwPxXkKhmc=
|
||||
|
||||
cheerio@^1.0.0-rc.2:
|
||||
version "1.0.0-rc.3"
|
||||
resolved "https://registry.yarnpkg.com/cheerio/-/cheerio-1.0.0-rc.3.tgz#094636d425b2e9c0f4eb91a46c05630c9a1a8bf6"
|
||||
@@ -5102,13 +5046,6 @@ clone-deep@^4.0.1:
|
||||
kind-of "^6.0.2"
|
||||
shallow-clone "^3.0.0"
|
||||
|
||||
clone-response@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/clone-response/-/clone-response-1.0.2.tgz#d1dc973920314df67fbeb94223b4ee350239e96b"
|
||||
integrity sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
clone@^1.0.2:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e"
|
||||
@@ -5179,7 +5116,7 @@ color@^3.0.0:
|
||||
color-convert "^1.9.1"
|
||||
color-string "^1.5.2"
|
||||
|
||||
colors@^1.1.2, colors@^1.3.2:
|
||||
colors@^1.1.2:
|
||||
version "1.4.0"
|
||||
resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78"
|
||||
integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==
|
||||
@@ -5623,11 +5560,6 @@ cross-spawn@^5.0.1:
|
||||
shebang-command "^1.2.0"
|
||||
which "^1.2.9"
|
||||
|
||||
crypt@~0.0.1:
|
||||
version "0.0.2"
|
||||
resolved "https://registry.yarnpkg.com/crypt/-/crypt-0.0.2.tgz#88d7ff7ec0dfb86f713dc87bbb42d044d3e6c41b"
|
||||
integrity sha1-iNf/fsDfuG9xPch7u0LQRNPmxBs=
|
||||
|
||||
crypto-browserify@^3.11.0:
|
||||
version "3.12.0"
|
||||
resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.12.0.tgz#396cf9f3137f03e4b8e532c58f698254e00f80ec"
|
||||
@@ -5959,13 +5891,6 @@ decode-uri-component@^0.2.0:
|
||||
resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545"
|
||||
integrity sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=
|
||||
|
||||
decompress-response@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/decompress-response/-/decompress-response-3.3.0.tgz#80a4dd323748384bfa248083622aedec982adff3"
|
||||
integrity sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=
|
||||
dependencies:
|
||||
mimic-response "^1.0.0"
|
||||
|
||||
dedent@^0.7.0:
|
||||
version "0.7.0"
|
||||
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
|
||||
@@ -6387,18 +6312,6 @@ dotenv@^8.0.0:
|
||||
resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a"
|
||||
integrity sha512-8sJ78ElpbDJBHNeBzUbUVLsqKdccaa/BXF1uPTw3GrvQTBgrQrtObr2mUrE38vzYd8cEv+m/JBfDLioYcfXoaw==
|
||||
|
||||
duplexer2@~0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/duplexer2/-/duplexer2-0.1.4.tgz#8b12dab878c0d69e3e7891051662a32fc6bddcc1"
|
||||
integrity sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=
|
||||
dependencies:
|
||||
readable-stream "^2.0.2"
|
||||
|
||||
duplexer3@^0.1.4:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/duplexer3/-/duplexer3-0.1.4.tgz#ee01dd1cac0ed3cbc7fdbea37dc0a8f1ce002ce2"
|
||||
integrity sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=
|
||||
|
||||
duplexer@^0.1.1:
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/duplexer/-/duplexer-0.1.1.tgz#ace6ff808c1ce66b57d1ebf97977acb02334cfc1"
|
||||
@@ -6844,7 +6757,7 @@ eslint-scope@^5.0.0:
|
||||
esrecurse "^4.1.0"
|
||||
estraverse "^4.1.1"
|
||||
|
||||
eslint-utils@^1.4.2, eslint-utils@^1.4.3:
|
||||
eslint-utils@^1.4.3:
|
||||
version "1.4.3"
|
||||
resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.3.tgz#74fec7c54d0776b6f67e0251040b5806564e981f"
|
||||
integrity sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==
|
||||
@@ -7380,49 +7293,6 @@ flatted@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08"
|
||||
integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==
|
||||
|
||||
flow-bin@^0.109.0:
|
||||
version "0.109.0"
|
||||
resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.109.0.tgz#dcdcb7402dd85b58200392d8716ccf14e5a8c24c"
|
||||
integrity sha512-tpcMTpAGIRivYhFV3KJq+zHI2HzcXo8MoGe9pXS4G/UZuey2Faq/e8/gdph2WF0erRlML5hmwfwiq7v9c25c7w==
|
||||
|
||||
flow-typed@^2.5.1, flow-typed@^2.6.1:
|
||||
version "2.6.2"
|
||||
resolved "https://registry.yarnpkg.com/flow-typed/-/flow-typed-2.6.2.tgz#6d324a96c4df300e0f823c13ca879c824bef40ce"
|
||||
integrity sha512-brTh8SukLidVpR1u8hSR3OcZSvLtptpwLEGgEhK/qBhWCB7zxPZmnmChYi40JQH6vB448ck380+qbkDo3fJ6qA==
|
||||
dependencies:
|
||||
"@babel/polyfill" "^7.0.0"
|
||||
"@octokit/rest" "^16.33.1"
|
||||
colors "^1.3.2"
|
||||
flowgen "^1.9.0"
|
||||
fs-extra "^7.0.0"
|
||||
glob "^7.1.3"
|
||||
got "^8.3.2"
|
||||
md5 "^2.2.1"
|
||||
mkdirp "^0.5.1"
|
||||
prettier "^1.18.2"
|
||||
rimraf "^2.6.2"
|
||||
semver "^5.5.1"
|
||||
table "^5.0.2"
|
||||
through "^2.3.8"
|
||||
unzipper "^0.9.3"
|
||||
which "^1.3.1"
|
||||
yargs "^12.0.2"
|
||||
|
||||
flowgen@^1.9.0:
|
||||
version "1.10.0"
|
||||
resolved "https://registry.yarnpkg.com/flowgen/-/flowgen-1.10.0.tgz#a041ae31d543d22166e7ba7c296b8445deb3c2e4"
|
||||
integrity sha512-3lsoaa1vxGXhnkHuoE4mLPJi/klvpR3ID8R9CFJ/GBNi+cxJXecWQaUPrWMdNI5tGs8Y+7wrIZaCVFKFLQiGOg==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.0.0"
|
||||
"@babel/highlight" "^7.0.0"
|
||||
commander "^2.11.0"
|
||||
lodash "^4.17.4"
|
||||
paralleljs "^0.2.1"
|
||||
prettier "^1.16.4"
|
||||
shelljs "^0.8.3"
|
||||
typescript "^3.4"
|
||||
typescript-compiler "^1.4.1-2"
|
||||
|
||||
flush-write-stream@^1.0.0:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8"
|
||||
@@ -7510,7 +7380,7 @@ fresh@0.5.2:
|
||||
resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7"
|
||||
integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=
|
||||
|
||||
from2@^2.1.0, from2@^2.1.1:
|
||||
from2@^2.1.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/from2/-/from2-2.3.0.tgz#8bfb5502bde4a4d36cfdeea007fcca21d7e382af"
|
||||
integrity sha1-i/tVAr3kpNNs/e6gB/zKIdfjgq8=
|
||||
@@ -7529,15 +7399,6 @@ fs-extra@^0.30.0:
|
||||
path-is-absolute "^1.0.0"
|
||||
rimraf "^2.2.8"
|
||||
|
||||
fs-extra@^7.0.0:
|
||||
version "7.0.1"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-7.0.1.tgz#4f189c44aa123b895f722804f55ea23eadc348e9"
|
||||
integrity sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==
|
||||
dependencies:
|
||||
graceful-fs "^4.1.2"
|
||||
jsonfile "^4.0.0"
|
||||
universalify "^0.1.0"
|
||||
|
||||
fs-extra@^8.0.1, fs-extra@^8.1.0:
|
||||
version "8.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-8.1.0.tgz#49d43c45a88cd9677668cb7be1b46efdb8d2e1c0"
|
||||
@@ -7679,7 +7540,7 @@ get-stdin@^6.0.0:
|
||||
resolved "https://registry.yarnpkg.com/get-stdin/-/get-stdin-6.0.0.tgz#9e09bf712b360ab9225e812048f71fde9c89657b"
|
||||
integrity sha512-jp4tHawyV7+fkkSKyvjuLZswblUtz+SQKzSWnBbii16BuZksJlU1wuBYXY75r+duh/llF1ur6oNwi+2ZzjKZ7g==
|
||||
|
||||
get-stream@3.0.0, get-stream@^3.0.0:
|
||||
get-stream@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/get-stream/-/get-stream-3.0.0.tgz#8e943d1358dc37555054ecbe2edb05aa174ede14"
|
||||
integrity sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=
|
||||
@@ -7781,7 +7642,7 @@ glob-to-regexp@^0.4.0:
|
||||
resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz#c75297087c851b9a578bd217dd59a92f59fe546e"
|
||||
integrity sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==
|
||||
|
||||
glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4:
|
||||
glob@^7.0.0, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6:
|
||||
version "7.1.6"
|
||||
resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6"
|
||||
integrity sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==
|
||||
@@ -7917,29 +7778,6 @@ good-listener@^1.2.2:
|
||||
dependencies:
|
||||
delegate "^3.1.2"
|
||||
|
||||
got@^8.3.2:
|
||||
version "8.3.2"
|
||||
resolved "https://registry.yarnpkg.com/got/-/got-8.3.2.tgz#1d23f64390e97f776cac52e5b936e5f514d2e937"
|
||||
integrity sha512-qjUJ5U/hawxosMryILofZCkm3C84PLJS/0grRIpjAwu+Lkxxj5cxeCU25BG0/3mDSpXKTyZr8oh8wIgLaH0QCw==
|
||||
dependencies:
|
||||
"@sindresorhus/is" "^0.7.0"
|
||||
cacheable-request "^2.1.1"
|
||||
decompress-response "^3.3.0"
|
||||
duplexer3 "^0.1.4"
|
||||
get-stream "^3.0.0"
|
||||
into-stream "^3.1.0"
|
||||
is-retry-allowed "^1.1.0"
|
||||
isurl "^1.0.0-alpha5"
|
||||
lowercase-keys "^1.0.0"
|
||||
mimic-response "^1.0.0"
|
||||
p-cancelable "^0.4.0"
|
||||
p-timeout "^2.0.1"
|
||||
pify "^3.0.0"
|
||||
safe-buffer "^5.1.1"
|
||||
timed-out "^4.0.1"
|
||||
url-parse-lax "^3.0.0"
|
||||
url-to-options "^1.0.1"
|
||||
|
||||
graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0:
|
||||
version "4.2.3"
|
||||
resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
|
||||
@@ -8004,23 +7842,11 @@ has-flag@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
has-symbol-support-x@^1.4.1:
|
||||
version "1.4.2"
|
||||
resolved "https://registry.yarnpkg.com/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz#1409f98bc00247da45da67cee0a36f282ff26455"
|
||||
integrity sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==
|
||||
|
||||
has-symbols@^1.0.0, has-symbols@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
|
||||
integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
|
||||
|
||||
has-to-string-tag-x@^1.2.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz#a045ab383d7b4b2012a00148ab0aa5f290044d4d"
|
||||
integrity sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==
|
||||
dependencies:
|
||||
has-symbol-support-x "^1.4.1"
|
||||
|
||||
has-unicode@^2.0.0, has-unicode@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9"
|
||||
@@ -8270,7 +8096,7 @@ htmlparser2@^4.0:
|
||||
domutils "^2.0.0"
|
||||
entities "^2.0.0"
|
||||
|
||||
http-cache-semantics@3.8.1, http-cache-semantics@^3.8.1:
|
||||
http-cache-semantics@^3.8.1:
|
||||
version "3.8.1"
|
||||
resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-3.8.1.tgz#39b0e16add9b605bf0a9ef3d9daaf4843b4cacd2"
|
||||
integrity sha512-5ai2iksyV8ZXmnZhHH4rWPoxxistEexSi5936zIQ1bnNTW5VnA85B6P/VpXiRM017IgRvb2kKo1a//y+0wSp3w==
|
||||
@@ -8635,14 +8461,6 @@ interpret@1.2.0, interpret@^1.0.0, interpret@^1.2.0:
|
||||
resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.2.0.tgz#d5061a6224be58e8083985f5014d844359576296"
|
||||
integrity sha512-mT34yGKMNceBQUoVn7iCDKDntA7SC6gycMAWzGx1z/CMCTV7b2AAtXlo3nRyHZ1FelRkQbQjprHSYGwzLtkVbw==
|
||||
|
||||
into-stream@^3.1.0:
|
||||
version "3.1.0"
|
||||
resolved "https://registry.yarnpkg.com/into-stream/-/into-stream-3.1.0.tgz#96fb0a936c12babd6ff1752a17d05616abd094c6"
|
||||
integrity sha1-lvsKk2wSur1v8XUqF9BWFqvQlMY=
|
||||
dependencies:
|
||||
from2 "^2.1.1"
|
||||
p-is-promise "^1.1.0"
|
||||
|
||||
invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4:
|
||||
version "2.2.4"
|
||||
resolved "https://registry.yarnpkg.com/invariant/-/invariant-2.2.4.tgz#610f3c92c9359ce1db616e538008d23ff35158e6"
|
||||
@@ -8744,7 +8562,7 @@ is-boolean-object@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-boolean-object/-/is-boolean-object-1.0.0.tgz#98f8b28030684219a95f375cfbd88ce3405dff93"
|
||||
integrity sha1-mPiygDBoQhmpXzdc+9iM40Bd/5M=
|
||||
|
||||
is-buffer@^1.0.2, is-buffer@^1.1.4, is-buffer@^1.1.5, is-buffer@~1.1.1:
|
||||
is-buffer@^1.0.2, is-buffer@^1.1.4, is-buffer@^1.1.5:
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
|
||||
integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
|
||||
@@ -8975,11 +8793,6 @@ is-resolvable@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88"
|
||||
integrity sha512-qgDYXFSR5WvEfuS5dMj6oTMEbrrSaM0CrFk2Yiq/gXnBvD9pMa2jGXxyhGLfvhZpuMZe18CJpFxAt3CRs42NMg==
|
||||
|
||||
is-retry-allowed@^1.1.0:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz#d778488bd0a4666a3be8a1482b9f2baafedea8b4"
|
||||
integrity sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==
|
||||
|
||||
is-root@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/is-root/-/is-root-2.1.0.tgz#809e18129cf1129644302a4f8544035d51984a9c"
|
||||
@@ -9158,14 +8971,6 @@ istanbul-reports@^2.2.6:
|
||||
dependencies:
|
||||
handlebars "^4.1.2"
|
||||
|
||||
isurl@^1.0.0-alpha5:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/isurl/-/isurl-1.0.0.tgz#b27f4f49f3cdaa3ea44a0a5b7f3462e6edc39d67"
|
||||
integrity sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==
|
||||
dependencies:
|
||||
has-to-string-tag-x "^1.2.0"
|
||||
is-object "^1.0.1"
|
||||
|
||||
jest-changed-files@^24.9.0:
|
||||
version "24.9.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-24.9.0.tgz#08d8c15eb79a7fa3fc98269bc14b451ee82f8039"
|
||||
@@ -9612,11 +9417,6 @@ jsesc@~0.5.0:
|
||||
resolved "https://registry.yarnpkg.com/jsesc/-/jsesc-0.5.0.tgz#e7dee66e35d6fc16f710fe91d5cf69f70f08911d"
|
||||
integrity sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=
|
||||
|
||||
json-buffer@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.0.tgz#5b1f397afc75d677bde8bcfc0e47e1f9a3d9a898"
|
||||
integrity sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=
|
||||
|
||||
json-parse-better-errors@^1.0.0, json-parse-better-errors@^1.0.1, json-parse-better-errors@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz#bb867cfb3450e69107c131d1c514bab3dc8bcaa9"
|
||||
@@ -9698,13 +9498,6 @@ jsx-ast-utils@^2.2.1:
|
||||
array-includes "^3.0.3"
|
||||
object.assign "^4.1.0"
|
||||
|
||||
keyv@3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/keyv/-/keyv-3.0.0.tgz#44923ba39e68b12a7cec7df6c3268c031f2ef373"
|
||||
integrity sha512-eguHnq22OE3uVoSYG0LVWNP+4ppamWr9+zWBe1bsNcovIMy6huUJFPgy4mGwCd/rnl3vOLGW1MTlu4c57CT1xA==
|
||||
dependencies:
|
||||
json-buffer "3.0.0"
|
||||
|
||||
killable@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/killable/-/killable-1.0.1.tgz#4c8ce441187a061c7474fb87ca08e2a638194892"
|
||||
@@ -9847,11 +9640,6 @@ lines-and-columns@^1.1.6:
|
||||
resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
|
||||
integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
|
||||
|
||||
listenercount@~1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/listenercount/-/listenercount-1.0.1.tgz#84c8a72ab59c4725321480c975e6508342e70937"
|
||||
integrity sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=
|
||||
|
||||
load-json-file@^1.0.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/load-json-file/-/load-json-file-1.1.0.tgz#956905708d58b4bab4c2261b04f59f31c99374c0"
|
||||
@@ -10046,7 +9834,7 @@ lodash.uniq@^4.5.0:
|
||||
resolved "https://registry.yarnpkg.com/lodash.uniq/-/lodash.uniq-4.5.0.tgz#d0225373aeb652adc1bc82e4945339a842754773"
|
||||
integrity sha1-0CJTc662Uq3BvILklFM5qEJ1R3M=
|
||||
|
||||
lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.4, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.10:
|
||||
lodash@^4.0.0, lodash@^4.15.0, lodash@^4.17.11, lodash@^4.17.12, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.17.5, lodash@^4.2.1, lodash@~4.17.10:
|
||||
version "4.17.15"
|
||||
resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
|
||||
integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
|
||||
@@ -10076,16 +9864,6 @@ lower-case@^1.1.1:
|
||||
resolved "https://registry.yarnpkg.com/lower-case/-/lower-case-1.1.4.tgz#9a2cabd1b9e8e0ae993a4bf7d5875c39c42e8eac"
|
||||
integrity sha1-miyr0bno4K6ZOkv31YdcOcQujqw=
|
||||
|
||||
lowercase-keys@1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306"
|
||||
integrity sha1-TjNms55/VFfjXxMkvfb4jQv8cwY=
|
||||
|
||||
lowercase-keys@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.1.tgz#6f9e30b47084d971a7c820ff15a6c5167b74c26f"
|
||||
integrity sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==
|
||||
|
||||
lowlight@~1.11.0:
|
||||
version "1.11.0"
|
||||
resolved "https://registry.yarnpkg.com/lowlight/-/lowlight-1.11.0.tgz#1304d83005126d4e8b1dc0f07981e9b689ec2efc"
|
||||
@@ -10229,15 +10007,6 @@ md5.js@^1.3.4:
|
||||
inherits "^2.0.1"
|
||||
safe-buffer "^5.1.2"
|
||||
|
||||
md5@^2.2.1:
|
||||
version "2.2.1"
|
||||
resolved "https://registry.yarnpkg.com/md5/-/md5-2.2.1.tgz#53ab38d5fe3c8891ba465329ea23fac0540126f9"
|
||||
integrity sha1-U6s41f48iJG6RlMp6iP6wFQBJvk=
|
||||
dependencies:
|
||||
charenc "~0.0.1"
|
||||
crypt "~0.0.1"
|
||||
is-buffer "~1.1.1"
|
||||
|
||||
mdast-add-list-metadata@1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mdast-add-list-metadata/-/mdast-add-list-metadata-1.0.1.tgz#95e73640ce2fc1fa2dcb7ec443d09e2bfe7db4cf"
|
||||
@@ -10435,11 +10204,6 @@ mimic-fn@^2.0.0, mimic-fn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
mimic-response@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mimic-response/-/mimic-response-1.0.1.tgz#4923538878eef42063cb8a3e3b0798781487ab1b"
|
||||
integrity sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==
|
||||
|
||||
min-document@^2.19.0:
|
||||
version "2.19.0"
|
||||
resolved "https://registry.yarnpkg.com/min-document/-/min-document-2.19.0.tgz#7bd282e3f5842ed295bb748cdd9f1ffa2c824685"
|
||||
@@ -10954,15 +10718,6 @@ normalize-url@1.9.1:
|
||||
query-string "^4.1.0"
|
||||
sort-keys "^1.0.0"
|
||||
|
||||
normalize-url@2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-2.0.1.tgz#835a9da1551fa26f70e92329069a23aa6574d7e6"
|
||||
integrity sha512-D6MUW4K/VzoJ4rJ01JFKxDrtY1v9wrgzCX5f2qj/lzH1m/lW6MhUZFKerVsnyjOhOsYzI9Kqqak+10l4LvLpMw==
|
||||
dependencies:
|
||||
prepend-http "^2.0.0"
|
||||
query-string "^5.0.1"
|
||||
sort-keys "^2.0.0"
|
||||
|
||||
normalize-url@^3.0.0, normalize-url@^3.3.0:
|
||||
version "3.3.0"
|
||||
resolved "https://registry.yarnpkg.com/normalize-url/-/normalize-url-3.3.0.tgz#b2e1c4dc4f7c6d57743df733a4f5978d18650559"
|
||||
@@ -11303,11 +11058,6 @@ osenv@0, osenv@^0.1.4, osenv@^0.1.5:
|
||||
os-homedir "^1.0.0"
|
||||
os-tmpdir "^1.0.0"
|
||||
|
||||
p-cancelable@^0.4.0:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/p-cancelable/-/p-cancelable-0.4.1.tgz#35f363d67d52081c8d9585e37bcceb7e0bbcb2a0"
|
||||
integrity sha512-HNa1A8LvB1kie7cERyy21VNeHb2CWJJYqyyC2o3klWFfMGlFmWv2Z7sFgZH8ZiaYL95ydToKTFVXgMV/Os0bBQ==
|
||||
|
||||
p-defer@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-defer/-/p-defer-1.0.0.tgz#9f6eb182f6c9aa8cd743004a7d4f96b196b0fb0c"
|
||||
@@ -11325,11 +11075,6 @@ p-finally@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae"
|
||||
integrity sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=
|
||||
|
||||
p-is-promise@^1.1.0:
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-1.1.0.tgz#9c9456989e9f6588017b0434d56097675c3da05e"
|
||||
integrity sha1-nJRWmJ6fZYgBewQ01WCXZ1w9oF4=
|
||||
|
||||
p-is-promise@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/p-is-promise/-/p-is-promise-2.1.0.tgz#918cebaea248a62cf7ffab8e3bca8c5f882fc42e"
|
||||
@@ -11406,13 +11151,6 @@ p-retry@^3.0.1:
|
||||
dependencies:
|
||||
retry "^0.12.0"
|
||||
|
||||
p-timeout@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-2.0.1.tgz#d8dd1979595d2dc0139e1fe46b8b646cb3cdf038"
|
||||
integrity sha512-88em58dDVB/KzPEx1X0N3LwFfYZPyDc4B6eF38M1rk9VTZMbxXXgjugz8mmwpS9Ox4BDZ+t6t3QP5+/gazweIA==
|
||||
dependencies:
|
||||
p-finally "^1.0.0"
|
||||
|
||||
p-try@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/p-try/-/p-try-1.0.0.tgz#cbc79cdbaf8fd4228e13f621f2b1a237c1b207b3"
|
||||
@@ -11444,11 +11182,6 @@ parallel-transform@^1.1.0:
|
||||
inherits "^2.0.3"
|
||||
readable-stream "^2.1.5"
|
||||
|
||||
paralleljs@^0.2.1:
|
||||
version "0.2.1"
|
||||
resolved "https://registry.yarnpkg.com/paralleljs/-/paralleljs-0.2.1.tgz#ebca745d3e09c01e2bebcc14858891ff4510e926"
|
||||
integrity sha1-68p0XT4JwB4r68wUhYiR/0UQ6SY=
|
||||
|
||||
param-case@^2.1.1:
|
||||
version "2.1.1"
|
||||
resolved "https://registry.yarnpkg.com/param-case/-/param-case-2.1.1.tgz#df94fd8cf6531ecf75e6bef9a0858fbc72be2247"
|
||||
@@ -12141,11 +11874,6 @@ prepend-http@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-1.0.4.tgz#d4f4562b0ce3696e41ac52d0e002e57a635dc6dc"
|
||||
integrity sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=
|
||||
|
||||
prepend-http@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prepend-http/-/prepend-http-2.0.0.tgz#e92434bfa5ea8c19f41cdfd401d741a3c819d897"
|
||||
integrity sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=
|
||||
|
||||
prettier-linter-helpers@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b"
|
||||
@@ -12158,11 +11886,6 @@ prettier-reflow@^1.18.2-1:
|
||||
resolved "https://registry.yarnpkg.com/prettier-reflow/-/prettier-reflow-1.18.2-2.tgz#8959aabf7b23138fa85b54a26f65afd15a52cfde"
|
||||
integrity sha512-Pd/rr0Si0f5qPQM+OheBsGV8w/u1mjpfXZPyVxLieG3aOLIOhbwzeCkoCPIlQ3VefWwKDq6gijoPlsa/fHQlFw==
|
||||
|
||||
prettier@^1.16.4, prettier@^1.18.2:
|
||||
version "1.18.2"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea"
|
||||
integrity sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw==
|
||||
|
||||
prettier@^1.19.1:
|
||||
version "1.19.1"
|
||||
resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.19.1.tgz#f7d7f5ff8a9cd872a7be4ca142095956a60797cb"
|
||||
@@ -13191,6 +12914,11 @@ regexpp@^2.0.1:
|
||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-2.0.1.tgz#8d19d31cf632482b589049f8281f93dbcba4d07f"
|
||||
integrity sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==
|
||||
|
||||
regexpp@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-3.0.0.tgz#dd63982ee3300e67b41c1956f850aa680d9d330e"
|
||||
integrity sha512-Z+hNr7RAVWxznLPuA7DIh8UNX1j9CDrUQxskw9IrBE1Dxue2lyXT+shqEIeLUjrokxIP8CMy1WkjgG3rTsd5/g==
|
||||
|
||||
regexpu-core@^4.6.0:
|
||||
version "4.6.0"
|
||||
resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-4.6.0.tgz#2037c18b327cfce8a6fea2a4ec441f2432afb8b6"
|
||||
@@ -13405,13 +13133,6 @@ resolve@^1.12.0, resolve@^1.5.0:
|
||||
dependencies:
|
||||
path-parse "^1.0.6"
|
||||
|
||||
responselike@1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/responselike/-/responselike-1.0.2.tgz#918720ef3b631c5642be068f15ade5a46f4ba1e7"
|
||||
integrity sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=
|
||||
dependencies:
|
||||
lowercase-keys "^1.0.0"
|
||||
|
||||
restore-cursor@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
|
||||
@@ -13738,7 +13459,7 @@ set-value@^2.0.0, set-value@^2.0.1:
|
||||
is-plain-object "^2.0.3"
|
||||
split-string "^3.0.1"
|
||||
|
||||
setimmediate@^1.0.4, setimmediate@^1.0.5, setimmediate@~1.0.4:
|
||||
setimmediate@^1.0.4, setimmediate@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/setimmediate/-/setimmediate-1.0.5.tgz#290cbb232e306942d7d7ea9b83732ab7856f8285"
|
||||
integrity sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=
|
||||
@@ -14521,7 +14242,7 @@ systemjs@0.21.6:
|
||||
resolved "https://registry.yarnpkg.com/systemjs/-/systemjs-0.21.6.tgz#9d15e79d9f60abbac23f0d179f887ec01f260a1b"
|
||||
integrity sha512-R+5S9eV9vcQgWOoS4D87joZ4xkFJHb19ZsyKY07D1+VBDE9bwYcU+KXE0r5XlDA8mFoJGyuWDbfrNoh90JsA8g==
|
||||
|
||||
table@^5.0.2, table@^5.2.3:
|
||||
table@^5.2.3:
|
||||
version "5.4.6"
|
||||
resolved "https://registry.yarnpkg.com/table/-/table-5.4.6.tgz#1292d19500ce3f86053b05f0e8e7e4a3bb21079e"
|
||||
integrity sha512-wmEc8m4fjnob4gt5riFRtTu/6+4rSe12TpAELNSqHMfF3IqnA+CH37USM6/YR3qRZv7e56kAEAtd6nKZaxe0Ug==
|
||||
@@ -14688,7 +14409,7 @@ through2@^3.0.0:
|
||||
dependencies:
|
||||
readable-stream "2 || 3"
|
||||
|
||||
through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6, through@^2.3.8:
|
||||
through@2, "through@>=2.2.7 <3", through@^2.3.4, through@^2.3.6:
|
||||
version "2.3.8"
|
||||
resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
|
||||
integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
|
||||
@@ -14698,11 +14419,6 @@ thunky@^1.0.2:
|
||||
resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d"
|
||||
integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA==
|
||||
|
||||
timed-out@^4.0.1:
|
||||
version "4.0.1"
|
||||
resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f"
|
||||
integrity sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=
|
||||
|
||||
timers-browserify@^2.0.4:
|
||||
version "2.0.11"
|
||||
resolved "https://registry.yarnpkg.com/timers-browserify/-/timers-browserify-2.0.11.tgz#800b1f3eee272e5bc53ee465a04d0e804c31211f"
|
||||
@@ -14817,11 +14533,6 @@ tr46@^1.0.1:
|
||||
dependencies:
|
||||
punycode "^2.1.0"
|
||||
|
||||
"traverse@>=0.3.0 <0.4":
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/traverse/-/traverse-0.3.9.tgz#717b8f220cc0bb7b44e40514c22b2e8bbc70d8b9"
|
||||
integrity sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=
|
||||
|
||||
trim-newlines@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/trim-newlines/-/trim-newlines-1.0.0.tgz#5887966bb582a4503a41eb524f7d35011815a613"
|
||||
@@ -14938,16 +14649,6 @@ typedarray@^0.0.6:
|
||||
resolved "https://registry.yarnpkg.com/typedarray/-/typedarray-0.0.6.tgz#867ac74e3864187b1d3d47d996a78ec5c8830777"
|
||||
integrity sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=
|
||||
|
||||
typescript-compiler@^1.4.1-2:
|
||||
version "1.4.1-2"
|
||||
resolved "https://registry.yarnpkg.com/typescript-compiler/-/typescript-compiler-1.4.1-2.tgz#ba4f7db22d91534a1929d90009dce161eb72fd3f"
|
||||
integrity sha1-uk99si2RU0oZKdkACdzhYety/T8=
|
||||
|
||||
typescript@^3.4:
|
||||
version "3.6.4"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.6.4.tgz#b18752bb3792bc1a0281335f7f6ebf1bbfc5b91d"
|
||||
integrity sha512-unoCll1+l+YK4i4F8f22TaNVPRHcD9PA3yCuZ8g5e0qGqlVlJ/8FSateOLLSagn+Yg5+ZwuPkL8LFUc0Jcvksg==
|
||||
|
||||
typescript@^3.7.2:
|
||||
version "3.7.2"
|
||||
resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.7.2.tgz#27e489b95fa5909445e9fef5ee48d81697ad18fb"
|
||||
@@ -15124,21 +14825,6 @@ unset-value@^1.0.0:
|
||||
has-value "^0.3.1"
|
||||
isobject "^3.0.0"
|
||||
|
||||
unzipper@^0.9.3:
|
||||
version "0.9.15"
|
||||
resolved "https://registry.yarnpkg.com/unzipper/-/unzipper-0.9.15.tgz#97d99203dad17698ee39882483c14e4845c7549c"
|
||||
integrity sha512-2aaUvO4RAeHDvOCuEtth7jrHFaCKTSXPqUkXwADaLBzGbgZGzUDccoEdJ5lW+3RmfpOZYNx0Rw6F6PUzM6caIA==
|
||||
dependencies:
|
||||
big-integer "^1.6.17"
|
||||
binary "~0.3.0"
|
||||
bluebird "~3.4.1"
|
||||
buffer-indexof-polyfill "~1.0.0"
|
||||
duplexer2 "~0.1.4"
|
||||
fstream "^1.0.12"
|
||||
listenercount "~1.0.1"
|
||||
readable-stream "~2.3.6"
|
||||
setimmediate "~1.0.4"
|
||||
|
||||
upath@^1.1.1:
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/upath/-/upath-1.2.0.tgz#8f66dbcd55a883acdae4408af8b035a5044c1894"
|
||||
@@ -15170,13 +14856,6 @@ url-loader@^2.0.1:
|
||||
mime "^2.4.4"
|
||||
schema-utils "^2.5.0"
|
||||
|
||||
url-parse-lax@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/url-parse-lax/-/url-parse-lax-3.0.0.tgz#16b5cafc07dbe3676c1b1999177823d6503acb0c"
|
||||
integrity sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=
|
||||
dependencies:
|
||||
prepend-http "^2.0.0"
|
||||
|
||||
url-parse@^1.4.3:
|
||||
version "1.4.7"
|
||||
resolved "https://registry.yarnpkg.com/url-parse/-/url-parse-1.4.7.tgz#a8a83535e8c00a316e403a5db4ac1b9b853ae278"
|
||||
@@ -15185,11 +14864,6 @@ url-parse@^1.4.3:
|
||||
querystringify "^2.1.1"
|
||||
requires-port "^1.0.0"
|
||||
|
||||
url-to-options@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/url-to-options/-/url-to-options-1.0.1.tgz#1505a03a289a48cbd7a434efbaeec5055f5633a9"
|
||||
integrity sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=
|
||||
|
||||
url@^0.11.0:
|
||||
version "0.11.0"
|
||||
resolved "https://registry.yarnpkg.com/url/-/url-0.11.0.tgz#3838e97cfc60521eb73c525a8e55bfdd9e2e28f1"
|
||||
@@ -15793,7 +15467,7 @@ yargs-parser@^5.0.0:
|
||||
dependencies:
|
||||
camelcase "^3.0.0"
|
||||
|
||||
yargs@12.0.5, yargs@^12.0.2:
|
||||
yargs@12.0.5:
|
||||
version "12.0.5"
|
||||
resolved "https://registry.yarnpkg.com/yargs/-/yargs-12.0.5.tgz#05f5997b609647b64f66b81e3b4b10a368e7ad13"
|
||||
integrity sha512-Lhz8TLaYnxq/2ObqHDql8dX8CJi97oHxrjUcYtzKbbykPtVW9WB+poxI+NM2UIzsMgNCZTIf0AQwsjK5yMAqZw==
|
||||
|
||||
Reference in New Issue
Block a user